aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor')
-rw-r--r--sources/shiboken6/ApiExtractor/AUTHORS8
-rw-r--r--sources/shiboken6/ApiExtractor/CMakeLists.txt138
-rw-r--r--sources/shiboken6/ApiExtractor/COPYING342
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetaargument.cpp201
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetaargument.h66
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp3749
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.h152
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder_helpers.cpp202
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h254
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetaenum.cpp423
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetaenum.h125
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafield.cpp254
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafield.h88
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.cpp1707
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.h487
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetalang.cpp1983
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetalang.h393
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetalang_enums.h50
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetalang_helpers.h35
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h36
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.cpp1120
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.h276
-rw-r--r--sources/shiboken6/ApiExtractor/addedfunction.cpp216
-rw-r--r--sources/shiboken6/ApiExtractor/addedfunction.h113
-rw-r--r--sources/shiboken6/ApiExtractor/addedfunction_p.h45
-rw-r--r--sources/shiboken6/ApiExtractor/anystringview_helpers.cpp56
-rw-r--r--sources/shiboken6/ApiExtractor/anystringview_helpers.h18
-rw-r--r--sources/shiboken6/ApiExtractor/apiextractor.cpp849
-rw-r--r--sources/shiboken6/ApiExtractor/apiextractor.h87
-rw-r--r--sources/shiboken6/ApiExtractor/apiextractorflags.h18
-rw-r--r--sources/shiboken6/ApiExtractor/apiextractorresult.cpp103
-rw-r--r--sources/shiboken6/ApiExtractor/apiextractorresult.h81
-rw-r--r--sources/shiboken6/ApiExtractor/arraytypeentry.h28
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp1296
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangbuilder.h37
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.cpp175
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.h26
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp323
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.h88
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangutils.cpp320
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangutils.h108
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp456
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.h56
-rw-r--r--sources/shiboken6/ApiExtractor/classdocumentation.cpp381
-rw-r--r--sources/shiboken6/ApiExtractor/classdocumentation.h82
-rw-r--r--sources/shiboken6/ApiExtractor/cmake_uninstall.cmake24
-rw-r--r--sources/shiboken6/ApiExtractor/codesnip.cpp78
-rw-r--r--sources/shiboken6/ApiExtractor/codesnip.h107
-rw-r--r--sources/shiboken6/ApiExtractor/codesniphelpers.cpp77
-rw-r--r--sources/shiboken6/ApiExtractor/codesniphelpers.h17
-rw-r--r--sources/shiboken6/ApiExtractor/complextypeentry.h179
-rw-r--r--sources/shiboken6/ApiExtractor/conditionalstreamreader.cpp211
-rw-r--r--sources/shiboken6/ApiExtractor/conditionalstreamreader.h90
-rw-r--r--sources/shiboken6/ApiExtractor/configurabletypeentry.h28
-rw-r--r--sources/shiboken6/ApiExtractor/constantvaluetypeentry.h23
-rw-r--r--sources/shiboken6/ApiExtractor/containertypeentry.h63
-rw-r--r--sources/shiboken6/ApiExtractor/customconversion.cpp197
-rw-r--r--sources/shiboken6/ApiExtractor/customconversion.h81
-rw-r--r--sources/shiboken6/ApiExtractor/customconversion_typedefs.h14
-rw-r--r--sources/shiboken6/ApiExtractor/customtypenentry.h30
-rw-r--r--sources/shiboken6/ApiExtractor/debughelpers_p.h56
-rw-r--r--sources/shiboken6/ApiExtractor/dependency.h22
-rw-r--r--sources/shiboken6/ApiExtractor/docparser.cpp232
-rw-r--r--sources/shiboken6/ApiExtractor/docparser.h125
-rw-r--r--sources/shiboken6/ApiExtractor/documentation.cpp65
-rw-r--r--sources/shiboken6/ApiExtractor/documentation.h65
-rw-r--r--sources/shiboken6/ApiExtractor/dotview.cpp58
-rw-r--r--sources/shiboken6/ApiExtractor/dotview.h14
-rw-r--r--sources/shiboken6/ApiExtractor/doxygenparser.cpp224
-rw-r--r--sources/shiboken6/ApiExtractor/doxygenparser.h18
-rw-r--r--sources/shiboken6/ApiExtractor/enclosingclassmixin.cpp14
-rw-r--r--sources/shiboken6/ApiExtractor/enclosingclassmixin.h24
-rw-r--r--sources/shiboken6/ApiExtractor/enumtypeentry.h51
-rw-r--r--sources/shiboken6/ApiExtractor/enumvaluetypeentry.h31
-rw-r--r--sources/shiboken6/ApiExtractor/exception.h27
-rw-r--r--sources/shiboken6/ApiExtractor/fileout.cpp198
-rw-r--r--sources/shiboken6/ApiExtractor/fileout.h43
-rw-r--r--sources/shiboken6/ApiExtractor/flagstypeentry.h36
-rw-r--r--sources/shiboken6/ApiExtractor/functiontypeentry.h35
-rw-r--r--sources/shiboken6/ApiExtractor/graph.h321
-rw-r--r--sources/shiboken6/ApiExtractor/header_paths.h46
-rw-r--r--sources/shiboken6/ApiExtractor/icecc.cmake14
-rw-r--r--sources/shiboken6/ApiExtractor/include.cpp79
-rw-r--r--sources/shiboken6/ApiExtractor/include.h95
-rw-r--r--sources/shiboken6/ApiExtractor/merge.xsl82
-rw-r--r--sources/shiboken6/ApiExtractor/messages.cpp1004
-rw-r--r--sources/shiboken6/ApiExtractor/messages.h267
-rw-r--r--sources/shiboken6/ApiExtractor/modifications.cpp672
-rw-r--r--sources/shiboken6/ApiExtractor/modifications.h342
-rw-r--r--sources/shiboken6/ApiExtractor/modifications_typedefs.h25
-rw-r--r--sources/shiboken6/ApiExtractor/namespacetypeentry.h51
-rw-r--r--sources/shiboken6/ApiExtractor/objecttypeentry.h21
-rw-r--r--sources/shiboken6/ApiExtractor/optionsparser.cpp232
-rw-r--r--sources/shiboken6/ApiExtractor/optionsparser.h98
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel.cpp1562
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel.h700
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel_enums.h61
-rw-r--r--sources/shiboken6/ApiExtractor/parser/codemodel_fwd.h61
-rw-r--r--sources/shiboken6/ApiExtractor/parser/enumvalue.cpp103
-rw-r--r--sources/shiboken6/ApiExtractor/parser/enumvalue.h61
-rw-r--r--sources/shiboken6/ApiExtractor/parser/typeinfo.cpp624
-rw-r--r--sources/shiboken6/ApiExtractor/parser/typeinfo.h128
-rw-r--r--sources/shiboken6/ApiExtractor/predefined_templates.cpp276
-rw-r--r--sources/shiboken6/ApiExtractor/predefined_templates.h27
-rw-r--r--sources/shiboken6/ApiExtractor/primitivetypeentry.h72
-rw-r--r--sources/shiboken6/ApiExtractor/propertyspec.cpp347
-rw-r--r--sources/shiboken6/ApiExtractor/propertyspec.h104
-rw-r--r--sources/shiboken6/ApiExtractor/pymethoddefentry.cpp53
-rw-r--r--sources/shiboken6/ApiExtractor/pymethoddefentry.h38
-rw-r--r--sources/shiboken6/ApiExtractor/pythontypeentry.h29
-rw-r--r--sources/shiboken6/ApiExtractor/qtcompat.h37
-rw-r--r--sources/shiboken6/ApiExtractor/qtdocparser.cpp445
-rw-r--r--sources/shiboken6/ApiExtractor/qtdocparser.h40
-rw-r--r--sources/shiboken6/ApiExtractor/reporthandler.cpp194
-rw-r--r--sources/shiboken6/ApiExtractor/reporthandler.h46
-rw-r--r--sources/shiboken6/ApiExtractor/smartpointertypeentry.h57
-rw-r--r--sources/shiboken6/ApiExtractor/sourcelocation.cpp75
-rw-r--r--sources/shiboken6/ApiExtractor/sourcelocation.h42
-rw-r--r--sources/shiboken6/ApiExtractor/symbols.filter7
-rw-r--r--sources/shiboken6/ApiExtractor/templateargumententry.h26
-rw-r--r--sources/shiboken6/ApiExtractor/tests/CMakeLists.txt66
-rw-r--r--sources/shiboken6/ApiExtractor/tests/a.xml14
-rw-r--r--sources/shiboken6/ApiExtractor/tests/injectedcode.txt5
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp778
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h38
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp229
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h24
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp522
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testaddfunction.h31
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp155
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testarrayargument.h18
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp164
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testcodeinjection.h23
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc6
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testcontainer.cpp84
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testcontainer.h16
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp178
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testconversionoperator.h19
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp240
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testconversionruletag.h19
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp57
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testctorinformation.h19
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp228
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h22
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp158
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testdtorinformation.h22
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testenum.cpp577
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testenum.h25
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp62
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testextrainclude.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp79
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testfunctiontag.h18
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp142
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h21
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp63
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testinserttemplate.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp117
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc5
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp480
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h26
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp50
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h18
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testnamespace.cpp77
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testnamespace.h19
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp115
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testnestedtypes.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp90
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp40
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h16
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp84
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testrefcounttag.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp37
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h16
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testremovefield.cpp76
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testremovefield.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp48
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h16
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp98
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h16
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp281
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testresolvetype.h21
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp129
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testreverseoperators.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testtemplates.cpp628
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testtemplates.h30
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testtoposort.cpp61
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testtoposort.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp92
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testtyperevision.h19
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testutil.h65
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp39
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h16
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp67
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testvoidarg.h17
-rw-r--r--sources/shiboken6/ApiExtractor/tests/utf8code.txt1
-rw-r--r--sources/shiboken6/ApiExtractor/textstream.cpp263
-rw-r--r--sources/shiboken6/ApiExtractor/textstream.h227
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase.cpp1661
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase.h208
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase_p.h25
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase_typedefs.h33
-rw-r--r--sources/shiboken6/ApiExtractor/typedefentry.h37
-rw-r--r--sources/shiboken6/ApiExtractor/typeparser.cpp292
-rw-r--r--sources/shiboken6/ApiExtractor/typeparser.h17
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem.cpp2649
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem.h215
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem_enums.h113
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem_typedefs.h79
-rw-r--r--sources/shiboken6/ApiExtractor/typesystemparser.cpp3648
-rw-r--r--sources/shiboken6/ApiExtractor/typesystemparser_p.h297
-rw-r--r--sources/shiboken6/ApiExtractor/typesystemtypeentry.h40
-rw-r--r--sources/shiboken6/ApiExtractor/usingmember.h21
-rw-r--r--sources/shiboken6/ApiExtractor/valuetypeentry.h40
-rw-r--r--sources/shiboken6/ApiExtractor/varargstypeentry.h20
-rw-r--r--sources/shiboken6/ApiExtractor/voidtypeentry.h20
-rw-r--r--sources/shiboken6/ApiExtractor/xmlutils.cpp42
-rw-r--r--sources/shiboken6/ApiExtractor/xmlutils.h29
-rw-r--r--sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp208
-rw-r--r--sources/shiboken6/ApiExtractor/xmlutils_libxslt.h16
-rw-r--r--sources/shiboken6/ApiExtractor/xmlutils_qt.h16
222 files changed, 45573 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/AUTHORS b/sources/shiboken6/ApiExtractor/AUTHORS
new file mode 100644
index 000000000..6e802fb53
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/AUTHORS
@@ -0,0 +1,8 @@
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruno Araujo <bruno.araujo@openbossa.org>
+Hugo Parente Lima <hugo.lima@openbossa.org>
+Lauro Moura <lauro.neto@openbossa.org>
+Luciano Wolf <luciano.wolf@openbossa.org>
+Marcelo Lira <marcelo.lira@openbossa.org>
+Renato Araujo Oliveira Filho <renato.filho@openbossa.org>
+
diff --git a/sources/shiboken6/ApiExtractor/CMakeLists.txt b/sources/shiboken6/ApiExtractor/CMakeLists.txt
new file mode 100644
index 000000000..7aa2fbd11
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/CMakeLists.txt
@@ -0,0 +1,138 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(apiextractor)
+
+cmake_minimum_required(VERSION 3.18)
+cmake_policy(VERSION 3.18)
+
+set(CMAKE_AUTOMOC ON)
+
+set(apiextractor_SRC
+abstractmetaargument.cpp abstractmetaargument.h
+abstractmetabuilder.cpp abstractmetabuilder.h abstractmetabuilder_p.h
+abstractmetabuilder_helpers.cpp
+abstractmetaenum.cpp abstractmetaenum.h
+abstractmetafield.cpp abstractmetafield.h
+abstractmetafunction.cpp abstractmetafunction.h
+abstractmetalang.cpp abstractmetalang.h abstractmetalang_helpers.h abstractmetalang_typedefs.h
+abstractmetatype.cpp abstractmetatype.h
+addedfunction.cpp addedfunction.h addedfunction_p.h
+anystringview_helpers.cpp anystringview_helpers.h
+apiextractor.cpp apiextractor.h apiextractorflags.h
+apiextractorresult.cpp apiextractorresult.h
+arraytypeentry.h
+classdocumentation.cpp classdocumentation.h
+codesnip.cpp codesnip.h
+codesniphelpers.cpp codesniphelpers.h
+complextypeentry.h
+conditionalstreamreader.cpp conditionalstreamreader.h
+configurabletypeentry.h
+constantvaluetypeentry.h
+containertypeentry.h
+customconversion.cpp customconversion.h customconversion_typedefs.h
+customtypenentry.h
+debughelpers_p.h
+dependency.h
+documentation.cpp documentation.h
+dotview.cpp dotview.h
+enclosingclassmixin.cpp enclosingclassmixin.h
+enumtypeentry.h
+enumvaluetypeentry.h
+exception.h
+fileout.cpp fileout.h
+flagstypeentry.h
+functiontypeentry.h
+graph.h
+header_paths.h
+include.cpp include.h
+messages.cpp messages.h
+modifications.cpp modifications.h modifications_typedefs.h
+namespacetypeentry.h
+objecttypeentry.h
+optionsparser.cpp optionsparser.h
+predefined_templates.cpp predefined_templates.h
+primitivetypeentry.h
+propertyspec.cpp propertyspec.h
+pymethoddefentry.cpp pymethoddefentry.h
+pythontypeentry.h
+reporthandler.cpp reporthandler.h
+smartpointertypeentry.h
+sourcelocation.cpp sourcelocation.h
+templateargumententry.h
+textstream.cpp textstream.h
+typedatabase.cpp typedatabase.h typedatabase_p.h typedatabase_typedefs.h
+typedefentry.h
+typeparser.cpp typeparser.h
+typesystem.cpp typesystem.h typesystem_enums.h typesystem_typedefs.h
+typesystemparser.cpp typesystemparser_p.h
+usingmember.h
+valuetypeentry.h
+varargstypeentry.h
+voidtypeentry.h
+xmlutils.cpp xmlutils.h xmlutils_libxslt.h xmlutils_qt.h
+# Clang
+clangparser/clangbuilder.cpp clangparser/clangbuilder.h
+clangparser/clangdebugutils.cpp clangparser/clangdebugutils.h
+clangparser/clangparser.cpp clangparser/clangparser.h
+clangparser/clangutils.cpp clangparser/clangutils.h
+clangparser/compilersupport.cpp clangparser/compilersupport.h
+# Old parser
+parser/codemodel.cpp parser/codemodel.h parser/codemodel_fwd.h parser/codemodel_enums.h
+parser/enumvalue.cpp parser/enumvalue.h
+parser/typeinfo.cpp parser/typeinfo.h
+)
+
+find_package(LibXml2 2.6.32)
+find_package(LibXslt 1.1.19)
+
+set(HAS_LIBXSLT 0)
+if (LIBXSLT_FOUND AND LIBXML2_FOUND)
+ set(HAS_LIBXSLT 1)
+endif()
+
+if(NOT HAS_LIBXSLT)
+ set(DISABLE_DOCSTRINGS TRUE)
+ message(WARNING
+ "Documentation will not be built due to missing dependency (libxslt not found).")
+endif()
+
+# Export to parent scope so that generator/CMakeLists.txt gets it
+set(DISABLE_DOCSTRINGS ${DISABLE_DOCSTRINGS} PARENT_SCOPE)
+
+add_library(apiextractor STATIC ${apiextractor_SRC})
+target_include_directories(apiextractor PRIVATE ${CLANG_INCLUDE_DIRS}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/parser)
+target_link_libraries(apiextractor PUBLIC Qt::Core)
+target_link_libraries(apiextractor PRIVATE libclang)
+
+if (HAS_LIBXSLT)
+ target_compile_definitions(apiextractor PUBLIC HAVE_LIBXSLT)
+ target_sources(apiextractor PRIVATE xmlutils_libxslt.cpp)
+ target_include_directories(apiextractor
+ PRIVATE ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR})
+ target_link_libraries(apiextractor
+ PRIVATE ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES})
+endif()
+
+if (NOT DISABLE_DOCSTRINGS)
+ target_sources(apiextractor PRIVATE
+ docparser.cpp docparser.h
+ doxygenparser.cpp doxygenparser.h
+ qtdocparser.cpp qtdocparser.h)
+endif()
+
+target_compile_definitions(apiextractor
+ PRIVATE CMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}"
+ PRIVATE QT_LEAN_HEADERS=1)
+
+set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE)
+
+if (BUILD_TESTS)
+ find_package(Qt6 REQUIRED COMPONENTS Test)
+ set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests)
+ enable_testing()
+ add_subdirectory(tests)
+endif()
diff --git a/sources/shiboken6/ApiExtractor/COPYING b/sources/shiboken6/ApiExtractor/COPYING
new file mode 100644
index 000000000..4ccd71466
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/COPYING
@@ -0,0 +1,342 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+-------------------------------------------------------------------------
diff --git a/sources/shiboken6/ApiExtractor/abstractmetaargument.cpp b/sources/shiboken6/ApiExtractor/abstractmetaargument.cpp
new file mode 100644
index 000000000..05cebe10a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetaargument.cpp
@@ -0,0 +1,201 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetaargument.h"
+#include "abstractmetatype.h"
+#include "documentation.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QSharedData>
+
+using namespace Qt::StringLiterals;
+
+class AbstractMetaArgumentData : public QSharedData
+{
+public:
+ QString toString() const;
+
+ QString m_name;
+ AbstractMetaType m_type;
+ AbstractMetaType m_modifiedType;
+ bool m_hasName = false;
+ Documentation m_doc;
+ QString m_expression;
+ QString m_originalExpression;
+ int m_argumentIndex = 0;
+ bool m_modifiedRemoved = false;
+};
+
+AbstractMetaArgument::AbstractMetaArgument() : d(new AbstractMetaArgumentData)
+{
+}
+
+AbstractMetaArgument::~AbstractMetaArgument() = default;
+
+AbstractMetaArgument::AbstractMetaArgument(const AbstractMetaArgument &) = default;
+
+AbstractMetaArgument &AbstractMetaArgument::operator=(const AbstractMetaArgument &) = default;
+
+AbstractMetaArgument::AbstractMetaArgument(AbstractMetaArgument &&) noexcept = default;
+
+AbstractMetaArgument &AbstractMetaArgument::operator=(AbstractMetaArgument &&) noexcept = default;
+
+const AbstractMetaType &AbstractMetaArgument::type() const
+{
+ return d->m_type;
+}
+
+void AbstractMetaArgument::setType(const AbstractMetaType &type)
+{
+ if (d->m_type != type)
+ d->m_type = d->m_modifiedType = type;
+}
+
+const AbstractMetaType &AbstractMetaArgument::modifiedType() const
+{
+ return d->m_modifiedType;
+}
+
+bool AbstractMetaArgument::isTypeModified() const
+{
+ return modifiedType() != type();
+}
+
+bool AbstractMetaArgument::isModifiedRemoved() const
+{
+ return d->m_modifiedRemoved;
+}
+
+void AbstractMetaArgument::setModifiedRemoved(bool v)
+{
+ if (d->m_modifiedRemoved != v)
+ d->m_modifiedRemoved = v;
+}
+
+void AbstractMetaArgument::setModifiedType(const AbstractMetaType &type)
+{
+ if (d->m_modifiedType != type)
+ d->m_modifiedType = type;
+}
+
+QString AbstractMetaArgument::name() const
+{
+ return d->m_name;
+}
+
+void AbstractMetaArgument::setName(const QString &name, bool realName)
+{
+ if (d->m_name != name || d->m_hasName != realName) {
+ d->m_name = name;
+ d->m_hasName = realName;
+ }
+}
+
+bool AbstractMetaArgument::hasName() const
+{
+ return d->m_hasName;
+}
+
+void AbstractMetaArgument::setDocumentation(const Documentation &doc)
+{
+ if (d->m_doc != doc)
+ d->m_doc = doc;
+}
+
+Documentation AbstractMetaArgument::documentation() const
+{
+ return d->m_doc;
+}
+
+QString AbstractMetaArgument::defaultValueExpression() const
+{
+ return d->m_expression;
+}
+
+void AbstractMetaArgument::setDefaultValueExpression(const QString &expr)
+{
+ if (d->m_expression != expr)
+ d->m_expression = expr;
+}
+
+QString AbstractMetaArgument::originalDefaultValueExpression() const
+{
+ return d->m_originalExpression;
+}
+
+void AbstractMetaArgument::setOriginalDefaultValueExpression(const QString &expr)
+{
+ if (d->m_originalExpression != expr)
+ d->m_originalExpression = expr;
+}
+
+bool AbstractMetaArgument::hasOriginalDefaultValueExpression() const
+{
+ return !d->m_originalExpression.isEmpty();
+}
+
+bool AbstractMetaArgument::hasDefaultValueExpression() const
+{
+ return !d->m_expression.isEmpty();
+}
+
+bool AbstractMetaArgument::hasUnmodifiedDefaultValueExpression() const
+{
+ return !d->m_originalExpression.isEmpty() && d->m_originalExpression == d->m_expression;
+}
+
+bool AbstractMetaArgument::hasModifiedDefaultValueExpression() const
+{
+ return !d->m_expression.isEmpty() && d->m_originalExpression != d->m_expression;
+}
+
+QString AbstractMetaArgumentData::toString() const
+{
+ QString result = m_type.name() + u' ' + m_name;
+ if (!m_expression.isEmpty())
+ result += u" = "_s + m_expression;
+ return result;
+}
+
+QString AbstractMetaArgument::toString() const
+{
+ return d->toString();
+}
+
+int AbstractMetaArgument::argumentIndex() const
+{
+ return d->m_argumentIndex;
+}
+
+void AbstractMetaArgument::setArgumentIndex(int argIndex)
+{
+ if (d->m_argumentIndex != argIndex)
+ d->m_argumentIndex = argIndex;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const AbstractMetaArgument &aa)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaArgument(" << aa.toString() << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaArgument *aa)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaArgument(";
+ if (aa != nullptr)
+ d << aa->toString();
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetaargument.h b/sources/shiboken6/ApiExtractor/abstractmetaargument.h
new file mode 100644
index 000000000..38402e369
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetaargument.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETAARGUMENT_H
+#define ABSTRACTMETAARGUMENT_H
+
+#include <QtCore/QSharedDataPointer>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class AbstractMetaType;
+class AbstractMetaArgumentData;
+class Documentation;
+
+class AbstractMetaArgument
+{
+public:
+ AbstractMetaArgument();
+ ~AbstractMetaArgument();
+ AbstractMetaArgument(const AbstractMetaArgument &);
+ AbstractMetaArgument &operator=(const AbstractMetaArgument &);
+ AbstractMetaArgument(AbstractMetaArgument &&) noexcept;
+ AbstractMetaArgument &operator=(AbstractMetaArgument &&) noexcept;
+
+ const AbstractMetaType &type() const;
+ void setType(const AbstractMetaType &type);
+ void setModifiedType(const AbstractMetaType &type);
+ const AbstractMetaType &modifiedType() const;
+ bool isTypeModified() const;
+
+ bool isModifiedRemoved() const;
+ void setModifiedRemoved(bool v);
+
+ QString name() const;
+ void setName(const QString &name, bool realName = true);
+ bool hasName() const;
+
+ void setDocumentation(const Documentation& doc);
+ Documentation documentation() const;
+
+ QString defaultValueExpression() const;
+ void setDefaultValueExpression(const QString &expr);
+
+ QString originalDefaultValueExpression() const;
+ void setOriginalDefaultValueExpression(const QString &expr);
+
+ bool hasDefaultValueExpression() const;
+ bool hasOriginalDefaultValueExpression() const;
+ bool hasUnmodifiedDefaultValueExpression() const;
+ bool hasModifiedDefaultValueExpression() const;
+
+ QString toString() const;
+
+ int argumentIndex() const;
+ void setArgumentIndex(int argIndex);
+
+private:
+ QSharedDataPointer<AbstractMetaArgumentData> d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const AbstractMetaArgument &aa);
+QDebug operator<<(QDebug d, const AbstractMetaArgument *aa);
+#endif
+
+#endif // ABSTRACTMETAARGUMENT_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
new file mode 100644
index 000000000..fa0767a62
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
@@ -0,0 +1,3749 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetabuilder_p.h"
+#include "abstractmetaargument.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafield.h"
+#include "abstractmetafunction.h"
+#include "abstractmetatype.h"
+#include "addedfunction.h"
+#include "graph.h"
+#include "debughelpers_p.h"
+#include "exception.h"
+#include "messages.h"
+#include "propertyspec.h"
+#include "reporthandler.h"
+#include "sourcelocation.h"
+#include "typedatabase.h"
+#include "enumtypeentry.h"
+#include "enumvaluetypeentry.h"
+#include "arraytypeentry.h"
+#include "constantvaluetypeentry.h"
+#include "containertypeentry.h"
+#include "flagstypeentry.h"
+#include "functiontypeentry.h"
+#include "namespacetypeentry.h"
+#include "primitivetypeentry.h"
+#include "smartpointertypeentry.h"
+#include "templateargumententry.h"
+#include "typedefentry.h"
+#include "typesystemtypeentry.h"
+#include "usingmember.h"
+
+#include "parser/codemodel.h"
+
+#include <clangparser/clangbuilder.h>
+#include <clangparser/clangutils.h>
+#include <clangparser/compilersupport.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMetaObject>
+#include <QtCore/QQueue>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QTextStream>
+
+#include <cstdio>
+#include <algorithm>
+#include <memory>
+
+using namespace Qt::StringLiterals;
+
+static QString stripTemplateArgs(const QString &name)
+{
+ const auto pos = name.indexOf(u'<');
+ return pos < 0 ? name : name.left(pos);
+}
+
+static void fixArgumentIndexes(AbstractMetaArgumentList *list)
+{
+ for (qsizetype i = 0, size = list->size(); i < size; ++i)
+ (*list)[i].setArgumentIndex(i);
+}
+
+bool operator<(const RejectEntry &re1, const RejectEntry &re2)
+{
+ return re1.reason != re2.reason
+ ? (re1.reason < re2.reason) : (re1.sortkey < re2.sortkey);
+}
+
+QTextStream &operator<<(QTextStream &str, const RejectEntry &re)
+{
+ str << re.signature;
+ if (!re.message.isEmpty())
+ str << ": " << re.message;
+ return str;
+}
+
+static void applyCachedFunctionModifications(AbstractMetaFunction *metaFunction,
+ const FunctionModificationList &functionMods)
+{
+ for (const FunctionModification &mod : functionMods) {
+ if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified)
+ metaFunction->setExceptionHandlingModification(mod.exceptionHandling());
+ if (mod.allowThread() != TypeSystem::AllowThread::Unspecified)
+ metaFunction->setAllowThreadModification(mod.allowThread());
+ }
+}
+
+bool AbstractMetaBuilderPrivate::m_useGlobalHeader = false;
+bool AbstractMetaBuilderPrivate::m_codeModelTestMode = false;
+
+AbstractMetaBuilderPrivate::AbstractMetaBuilderPrivate() :
+ m_logDirectory(u"."_s + QDir::separator())
+{
+}
+
+AbstractMetaBuilder::AbstractMetaBuilder() : d(new AbstractMetaBuilderPrivate)
+{
+ d->q = this;
+}
+
+AbstractMetaBuilder::~AbstractMetaBuilder()
+{
+ delete d;
+}
+
+const AbstractMetaClassList &AbstractMetaBuilder::classes() const
+{
+ return d->m_metaClasses;
+}
+
+AbstractMetaClassList AbstractMetaBuilder::takeClasses()
+{
+ AbstractMetaClassList result;
+ qSwap(result, d->m_metaClasses);
+ return result;
+}
+
+const AbstractMetaClassList &AbstractMetaBuilder::templates() const
+{
+ return d->m_templates;
+}
+
+AbstractMetaClassList AbstractMetaBuilder::takeTemplates()
+{
+ AbstractMetaClassList result;
+ qSwap(result, d->m_templates);
+ return result;
+}
+
+const AbstractMetaClassList &AbstractMetaBuilder::smartPointers() const
+{
+ return d->m_smartPointers;
+}
+
+AbstractMetaClassList AbstractMetaBuilder::takeSmartPointers()
+{
+ AbstractMetaClassList result;
+ qSwap(result, d->m_smartPointers);
+ return result;
+}
+
+const AbstractMetaFunctionCList &AbstractMetaBuilder::globalFunctions() const
+{
+ return d->m_globalFunctions;
+}
+
+const AbstractMetaEnumList &AbstractMetaBuilder::globalEnums() const
+{
+ return d->m_globalEnums;
+}
+
+const QHash<TypeEntryCPtr, AbstractMetaEnum> &AbstractMetaBuilder::typeEntryToEnumsHash() const
+{
+ return d->m_enums;
+}
+
+const QMultiHash<QString, QString> &AbstractMetaBuilder::typedefTargetToName() const
+{
+ return d->m_typedefTargetToName;
+}
+
+void AbstractMetaBuilderPrivate::checkFunctionModifications() const
+{
+ const auto &entries = TypeDatabase::instance()->entries();
+
+ for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) {
+ TypeEntryCPtr entry = it.value();
+ if (!entry)
+ continue;
+ if (!entry->isComplex() || !entry->generateCode())
+ continue;
+
+ auto centry = std::static_pointer_cast<const ComplexTypeEntry>(entry);
+
+ if (!centry->generateCode())
+ continue;
+
+ FunctionModificationList modifications = centry->functionModifications();
+
+ for (const FunctionModification &modification : std::as_const(modifications)) {
+ QString signature = modification.signature();
+
+ QString name = signature.trimmed();
+ name.truncate(name.indexOf(u'('));
+
+ const auto clazz = AbstractMetaClass::findClass(m_metaClasses, centry);
+ if (!clazz)
+ continue;
+
+ bool found = false;
+ QStringList possibleSignatures;
+ for (const auto &function : clazz->functions()) {
+ if (function->implementingClass() == clazz
+ && modification.matches(function->modificationSignatures())) {
+ found = true;
+ break;
+ }
+
+ if (function->originalName() == name) {
+ const QString signatures = function->modificationSignatures().join(u'/');
+ possibleSignatures.append(signatures + u" in "_s
+ + function->implementingClass()->name());
+ }
+ }
+
+ if (!found) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << msgNoFunctionForModification(clazz, signature,
+ modification.originalSignature(),
+ possibleSignatures, clazz->functions());
+ }
+ }
+ }
+}
+
+AbstractMetaClassPtr AbstractMetaBuilderPrivate::argumentToClass(const ArgumentModelItem &argument,
+ const AbstractMetaClassCPtr &currentClass)
+{
+ AbstractMetaClassPtr returned;
+ auto type = translateType(argument->type(), currentClass);
+ if (!type.has_value())
+ return returned;
+ TypeEntryCPtr entry = type->typeEntry();
+ if (entry && entry->isComplex())
+ returned = AbstractMetaClass::findClass(m_metaClasses, entry);
+ return returned;
+}
+
+/**
+ * Checks the argument of a hash function and flags the type if it is a complex type
+ */
+void AbstractMetaBuilderPrivate::registerHashFunction(const FunctionModelItem &function_item,
+ const AbstractMetaClassPtr &currentClass)
+{
+ if (function_item->isDeleted())
+ return;
+ ArgumentList arguments = function_item->arguments();
+ if (arguments.size() >= 1) { // (Class, Hash seed).
+ if (AbstractMetaClassPtr cls = argumentToClass(arguments.at(0), currentClass))
+ cls->setHashFunction(function_item->name());
+ }
+}
+
+void AbstractMetaBuilderPrivate::registerToStringCapabilityIn(const NamespaceModelItem &nsItem)
+{
+ const FunctionList &streamOps = nsItem->findFunctions("operator<<");
+ for (const FunctionModelItem &item : streamOps)
+ registerToStringCapability(item, nullptr);
+ for (const NamespaceModelItem &ni : nsItem->namespaces())
+ registerToStringCapabilityIn(ni);
+}
+
+/**
+ * Check if a class has a debug stream operator that can be used as toString
+ */
+
+void AbstractMetaBuilderPrivate::registerToStringCapability(const FunctionModelItem &function_item,
+ const AbstractMetaClassPtr &currentClass)
+{
+ ArgumentList arguments = function_item->arguments();
+ if (arguments.size() == 2) {
+ if (arguments.at(0)->type().toString() == u"QDebug") {
+ const ArgumentModelItem &arg = arguments.at(1);
+ if (AbstractMetaClassPtr cls = argumentToClass(arg, currentClass)) {
+ if (arg->type().indirections() < 2)
+ cls->setToStringCapability(true, int(arg->type().indirections()));
+ }
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelItem &item,
+ const AbstractMetaClassPtr &currentClass)
+{
+ if (item->accessPolicy() != Access::Public)
+ return;
+
+ const ArgumentList &itemArguments = item->arguments();
+ bool firstArgumentIsSelf = true;
+ bool unaryOperator = false;
+
+ auto baseoperandClass = argumentToClass(itemArguments.at(0), currentClass);
+
+ if (itemArguments.size() == 1) {
+ unaryOperator = true;
+ } else if (!baseoperandClass
+ || !baseoperandClass->typeEntry()->generateCode()) {
+ baseoperandClass = argumentToClass(itemArguments.at(1), currentClass);
+ firstArgumentIsSelf = false;
+ } else {
+ auto type = translateType(item->type(), currentClass);
+ const auto retType = type.has_value() ? type->typeEntry() : TypeEntryCPtr{};
+ const auto otherArgClass = argumentToClass(itemArguments.at(1), currentClass);
+ if (otherArgClass && retType
+ && (retType->isValue() || retType->isObject())
+ && retType != baseoperandClass->typeEntry()
+ && retType == otherArgClass->typeEntry()) {
+ baseoperandClass = AbstractMetaClass::findClass(m_metaClasses, retType);
+ firstArgumentIsSelf = false;
+ }
+ }
+ if (!baseoperandClass) {
+ rejectFunction(item, currentClass, AbstractMetaBuilder::UnmatchedOperator,
+ u"base operand class not found."_s);
+ return;
+ }
+
+ if (item->isSpaceshipOperator() && !item->isDeleted()) {
+ AbstractMetaClass::addSynthesizedComparisonOperators(baseoperandClass);
+ return;
+ }
+
+ AbstractMetaFunction *metaFunction = traverseFunction(item, baseoperandClass);
+ if (metaFunction == nullptr)
+ return;
+
+ auto flags = metaFunction->flags();
+ // Strip away first argument, since that is the containing object
+ AbstractMetaArgumentList arguments = metaFunction->arguments();
+ if (firstArgumentIsSelf || unaryOperator) {
+ AbstractMetaArgument first = arguments.takeFirst();
+ fixArgumentIndexes(&arguments);
+ if (!unaryOperator && first.type().indirections())
+ metaFunction->setPointerOperator(true);
+ metaFunction->setArguments(arguments);
+ flags.setFlag(AbstractMetaFunction::Flag::OperatorLeadingClassArgumentRemoved);
+ if (first.type().passByValue())
+ flags.setFlag(AbstractMetaFunction::Flag::OperatorClassArgumentByValue);
+ } else {
+ // If the operator method is not unary and the first operator is
+ // not of the same type of its owning class we suppose that it
+ // must be an reverse operator (e.g. CLASS::operator(TYPE, CLASS)).
+ // All operator overloads that operate over a class are already
+ // being added as member functions of that class by the API Extractor.
+ AbstractMetaArgument last = arguments.takeLast();
+ if (last.type().indirections())
+ metaFunction->setPointerOperator(true);
+ metaFunction->setArguments(arguments);
+ metaFunction->setReverseOperator(true);
+ flags.setFlag(AbstractMetaFunction::Flag::OperatorTrailingClassArgumentRemoved);
+ if (last.type().passByValue())
+ flags.setFlag(AbstractMetaFunction::Flag::OperatorClassArgumentByValue);
+ }
+ metaFunction->setFlags(flags);
+ metaFunction->setAccess(Access::Public);
+ AbstractMetaClass::addFunction(baseoperandClass, AbstractMetaFunctionCPtr(metaFunction));
+ if (!metaFunction->arguments().isEmpty()) {
+ const auto include = metaFunction->arguments().constFirst().type().typeEntry()->include();
+ baseoperandClass->typeEntry()->addArgumentInclude(include);
+ }
+ Q_ASSERT(!metaFunction->wasPrivate());
+}
+
+bool AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem &item,
+ const AbstractMetaClassPtr &currentClass)
+{
+ ArgumentList itemArguments = item->arguments();
+ if (itemArguments.size() != 2 || item->accessPolicy() != Access::Public)
+ return false;
+ auto streamClass = argumentToClass(itemArguments.at(0), currentClass);
+ if (streamClass == nullptr || !streamClass->isStream())
+ return false;
+ auto streamedClass = argumentToClass(itemArguments.at(1), currentClass);
+ if (streamedClass == nullptr)
+ return false;
+
+ AbstractMetaFunction *streamFunction = traverseFunction(item, streamedClass);
+ if (!streamFunction)
+ return false;
+
+ // Strip first argument, since that is the containing object
+ AbstractMetaArgumentList arguments = streamFunction->arguments();
+ if (!streamClass->typeEntry()->generateCode()) {
+ arguments.takeLast();
+ } else {
+ arguments.takeFirst();
+ fixArgumentIndexes(&arguments);
+ }
+
+ streamFunction->setArguments(arguments);
+
+ streamFunction->setAccess(Access::Public);
+
+ AbstractMetaClassPtr funcClass;
+
+ if (!streamClass->typeEntry()->generateCode()) {
+ AbstractMetaArgumentList reverseArgs = streamFunction->arguments();
+ std::reverse(reverseArgs.begin(), reverseArgs.end());
+ fixArgumentIndexes(&reverseArgs);
+ streamFunction->setArguments(reverseArgs);
+ streamFunction->setReverseOperator(true);
+ funcClass = streamedClass;
+ } else {
+ funcClass = streamClass;
+ }
+
+ AbstractMetaClass::addFunction(funcClass, AbstractMetaFunctionCPtr(streamFunction));
+ auto funcTe = funcClass->typeEntry();
+ if (funcClass == streamClass)
+ funcTe->addArgumentInclude(streamedClass->typeEntry()->include());
+ else
+ funcTe->addArgumentInclude(streamClass->typeEntry()->include());
+ return true;
+}
+
+static bool metaEnumLessThan(const AbstractMetaEnum &e1, const AbstractMetaEnum &e2)
+{ return e1.fullName() < e2.fullName(); }
+
+static bool metaClassLessThan(const AbstractMetaClassCPtr &c1, const AbstractMetaClassCPtr &c2)
+{ return c1->fullName() < c2->fullName(); }
+
+static bool metaFunctionLessThan(const AbstractMetaFunctionCPtr &f1, const AbstractMetaFunctionCPtr &f2)
+{ return f1->name() < f2->name(); }
+
+void AbstractMetaBuilderPrivate::sortLists()
+{
+ // Ensure indepedent classes are in alphabetical order,
+ std::sort(m_metaClasses.begin(), m_metaClasses.end(), metaClassLessThan);
+ // this is a temporary solution before new type revision implementation
+ // We need move QMetaObject register before QObject.
+ Dependencies additionalDependencies;
+ if (auto qObjectClass = AbstractMetaClass::findClass(m_metaClasses, "QObject")) {
+ if (auto qMetaObjectClass = AbstractMetaClass::findClass(m_metaClasses, "QMetaObject")) {
+ Dependency dependency;
+ dependency.parent = qMetaObjectClass;
+ dependency.child = qObjectClass;
+ additionalDependencies.append(dependency);
+ }
+ }
+ m_metaClasses = classesTopologicalSorted(m_metaClasses, additionalDependencies);
+
+ for (const auto &cls : std::as_const(m_metaClasses))
+ cls->sortFunctions();
+
+ // Ensure that indexes are in alphabetical order, roughly, except
+ // for classes, which are in topological order
+ std::sort(m_globalEnums.begin(), m_globalEnums.end(), metaEnumLessThan);
+ std::sort(m_templates.begin(), m_templates.end(), metaClassLessThan);
+ std::sort(m_smartPointers.begin(), m_smartPointers.end(), metaClassLessThan);
+ std::sort(m_globalFunctions.begin(), m_globalFunctions.end(), metaFunctionLessThan);
+}
+
+FileModelItem AbstractMetaBuilderPrivate::buildDom(QByteArrayList arguments,
+ bool addCompilerSupportArguments,
+ LanguageLevel level,
+ unsigned clangFlags)
+{
+ clang::Builder builder;
+ builder.setForceProcessSystemIncludes(TypeDatabase::instance()->forceProcessSystemIncludes());
+ if (addCompilerSupportArguments) {
+ if (level == LanguageLevel::Default)
+ level = clang::emulatedCompilerLanguageLevel();
+ arguments.prepend(QByteArrayLiteral("-std=")
+ + clang::languageLevelOption(level));
+ }
+ FileModelItem result = clang::parse(arguments, addCompilerSupportArguments,
+ level, clangFlags, builder)
+ ? builder.dom() : FileModelItem();
+ const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics();
+ if (const auto diagnosticsCount = diagnostics.size()) {
+ QDebug d = qWarning();
+ d.nospace();
+ d.noquote();
+ d << "Clang: " << diagnosticsCount << " diagnostic messages:\n";
+ for (qsizetype i = 0; i < diagnosticsCount; ++i)
+ d << " " << diagnostics.at(i) << '\n';
+ }
+ return result;
+}
+
+// List of candidates for a mismatched added global function.
+static QStringList functionCandidates(const AbstractMetaFunctionCList &list,
+ const QString &signature)
+{
+ QString name = signature;
+ const auto parenPos = name.indexOf(u'(');
+ if (parenPos > 0)
+ name.truncate(parenPos);
+ QStringList result;
+ for (const auto &func : list) {
+ if (name == func->name())
+ result += func->minimalSignature();
+ }
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom,
+ ApiExtractorFlags flags)
+{
+ const TypeDatabase *types = TypeDatabase::instance();
+
+ pushScope(dom);
+
+ // Start the generation...
+ const ClassList &typeValues = dom->classes();
+
+ ReportHandler::startProgress("Generated class model ("
+ + QByteArray::number(typeValues.size()) + ").");
+ for (const ClassModelItem &item : typeValues) {
+ if (const auto cls = traverseClass(dom, item, nullptr))
+ addAbstractMetaClass(cls, item.get());
+ }
+
+ // We need to know all global enums
+ const EnumList &enums = dom->enums();
+
+ ReportHandler::startProgress("Generated enum model ("
+ + QByteArray::number(enums.size()) + ").");
+ for (const EnumModelItem &item : enums) {
+ auto metaEnum = traverseEnum(item, nullptr, QSet<QString>());
+ if (metaEnum.has_value()) {
+ if (metaEnum->typeEntry()->generateCode())
+ m_globalEnums << metaEnum.value();
+ }
+ }
+
+ const auto &namespaceTypeValues = dom->namespaces();
+ ReportHandler::startProgress("Generated namespace model ("
+ + QByteArray::number(namespaceTypeValues.size()) + ").");
+ for (const NamespaceModelItem &item : namespaceTypeValues)
+ traverseNamespace(dom, item);
+
+ // Go through all typedefs to see if we have defined any
+ // specific typedefs to be used as classes.
+ const TypeDefList typeDefs = dom->typeDefs();
+ ReportHandler::startProgress("Resolved typedefs ("
+ + QByteArray::number(typeDefs.size()) + ").");
+ for (const TypeDefModelItem &typeDef : typeDefs) {
+ if (const auto cls = traverseTypeDef(dom, typeDef, nullptr))
+ addAbstractMetaClass(cls, typeDef.get());
+ }
+
+ traverseTypesystemTypedefs();
+
+ for (const ClassModelItem &item : typeValues)
+ traverseClassMembers(item);
+
+ for (const NamespaceModelItem &item : namespaceTypeValues)
+ traverseNamespaceMembers(item);
+
+ // Global functions
+ const FunctionList &functions = dom->functions();
+ for (const FunctionModelItem &func : functions) {
+ if (func->accessPolicy() != Access::Public || func->name().startsWith(u"operator"))
+ continue;
+
+ FunctionTypeEntryPtr funcEntry = types->findFunctionType(func->name());
+ if (!funcEntry || !funcEntry->generateCode())
+ continue;
+
+ AbstractMetaFunction *metaFunc = traverseFunction(func, nullptr);
+ if (!metaFunc)
+ continue;
+
+ AbstractMetaFunctionCPtr metaFuncPtr(metaFunc);
+ if (!funcEntry->hasSignature(metaFunc->minimalSignature()))
+ continue;
+
+ metaFunc->setTypeEntry(funcEntry);
+ applyFunctionModifications(metaFunc);
+ metaFunc->applyTypeModifications();
+
+ setInclude(funcEntry, func->fileName());
+
+ m_globalFunctions << metaFuncPtr;
+ }
+
+ ReportHandler::startProgress("Fixed class inheritance.");
+ for (const auto &cls : std::as_const(m_metaClasses)) {
+ if (cls->needsInheritanceSetup()) {
+ setupInheritance(cls);
+ traverseUsingMembers(cls);
+ if (cls->templateBaseClass())
+ inheritTemplateFunctions(cls);
+ if (!cls->hasVirtualDestructor() && cls->baseClass()
+ && cls->baseClass()->hasVirtualDestructor())
+ cls->setHasVirtualDestructor(true);
+ }
+ }
+
+ ReportHandler::startProgress("Checked for inconsistencies in class model.");
+ for (const auto &cls : std::as_const(m_metaClasses)) {
+ AbstractMetaClass::fixFunctions(cls);
+
+ if (cls->canAddDefaultConstructor())
+ AbstractMetaClass::addDefaultConstructor(cls);
+ if (cls->canAddDefaultCopyConstructor())
+ AbstractMetaClass::addDefaultCopyConstructor(cls);
+
+ const bool avoidProtectedHack = flags.testFlag(ApiExtractorFlag::AvoidProtectedHack);
+ const bool vco =
+ AbstractMetaClass::determineValueTypeWithCopyConstructorOnly(cls, avoidProtectedHack);
+ cls->setValueTypeWithCopyConstructorOnly(vco);
+ cls->typeEntry()->setValueTypeWithCopyConstructorOnly(vco);
+ }
+
+ const auto &allEntries = types->entries();
+
+ ReportHandler::startProgress("Checked for inconsistencies in typesystem ("
+ + QByteArray::number(allEntries.size()) + ").");
+ for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) {
+ const TypeEntryPtr &entry = it.value();
+ if (!entry->isPrimitive()) {
+ if ((entry->isValue() || entry->isObject())
+ && !types->shouldDropTypeEntry(entry->qualifiedCppName())
+ && !entry->isContainer()
+ && !entry->isCustom()
+ && entry->generateCode()
+ && !AbstractMetaClass::findClass(m_metaClasses, entry)) {
+ qCWarning(lcShiboken, "%s", qPrintable(msgTypeNotDefined(entry)));
+ } else if (entry->generateCode() && entry->type() == TypeEntry::FunctionType) {
+ auto fte = std::static_pointer_cast<const FunctionTypeEntry>(entry);
+ const QStringList &signatures = fte->signatures();
+ for (const QString &signature : signatures) {
+ bool ok = false;
+ for (const auto &func : std::as_const(m_globalFunctions)) {
+ if (signature == func->minimalSignature()) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
+ const QStringList candidates = functionCandidates(m_globalFunctions,
+ signatures.constFirst());
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgGlobalFunctionNotDefined(fte, signature, candidates)));
+ }
+ }
+ } else if (entry->isEnum() && entry->generateCode()) {
+ const auto enumEntry = std::static_pointer_cast<const EnumTypeEntry>(entry);
+ const auto cls = AbstractMetaClass::findClass(m_metaClasses,
+ enumEntry->parent());
+
+ const bool enumFound = cls
+ ? cls->findEnum(entry->targetLangEntryName()).has_value()
+ : m_enums.contains(entry);
+
+ if (!enumFound) {
+ entry->setCodeGeneration(TypeEntry::GenerateNothing);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgEnumNotDefined(enumEntry)));
+ }
+
+ }
+ }
+ }
+
+ {
+ const FunctionList &hashFunctions = dom->findFunctions("qHash");
+ for (const FunctionModelItem &item : hashFunctions)
+ registerHashFunction(item, nullptr);
+ }
+
+ registerToStringCapabilityIn(dom);
+
+ for (const auto &func : dom->functions()) {
+ switch (func->functionType()) {
+ case CodeModel::ComparisonOperator:
+ case CodeModel::ArithmeticOperator:
+ case CodeModel::BitwiseOperator:
+ case CodeModel::LogicalOperator:
+ traverseOperatorFunction(func, nullptr);
+ break;
+ case CodeModel::ShiftOperator:
+ if (!traverseStreamOperator(func, nullptr))
+ traverseOperatorFunction(func, nullptr);
+ default:
+ break;
+ }
+ }
+
+ ReportHandler::startProgress("Checked for inconsistencies in function modifications.");
+
+ checkFunctionModifications();
+
+ ReportHandler::startProgress("Wrote log files.");
+
+ for (const auto &cls : std::as_const(m_metaClasses)) {
+// setupEquals(cls);
+// setupComparable(cls);
+ setupExternalConversion(cls);
+
+ // sort all inner classes topologically
+ if (!cls->typeEntry()->codeGeneration() || cls->innerClasses().size() < 2)
+ continue;
+
+ cls->setInnerClasses(classesTopologicalSorted(cls->innerClasses()));
+ }
+
+ fixSmartPointers();
+
+ dumpLog();
+
+ sortLists();
+
+ // Functions added to the module on the type system.
+ QString errorMessage;
+ const AddedFunctionList &globalUserFunctions = types->globalUserFunctions();
+ for (const AddedFunctionPtr &addedFunc : globalUserFunctions) {
+ if (!traverseAddedGlobalFunction(addedFunc, &errorMessage))
+ throw Exception(errorMessage);
+ }
+
+ if (!m_codeModelTestMode) {
+ m_itemToClass.clear();
+ m_classToItem.clear();
+ m_typeSystemTypeDefs.clear();
+ m_scopes.clear();
+ }
+
+ ReportHandler::endProgress();
+}
+
+bool AbstractMetaBuilder::build(const QByteArrayList &arguments,
+ ApiExtractorFlags apiExtractorFlags,
+ bool addCompilerSupportArguments,
+ LanguageLevel level,
+ unsigned clangFlags)
+{
+ const FileModelItem dom = d->buildDom(arguments, addCompilerSupportArguments,
+ level, clangFlags);
+ if (!dom)
+ return false;
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug))
+ qCDebug(lcShiboken) << dom.get();
+ d->traverseDom(dom, apiExtractorFlags);
+
+ return true;
+}
+
+void AbstractMetaBuilder::setLogDirectory(const QString &logDir)
+{
+ d->m_logDirectory = logDir;
+ if (!d->m_logDirectory.endsWith(QDir::separator()))
+ d->m_logDirectory.append(QDir::separator());
+}
+
+void AbstractMetaBuilderPrivate::addAbstractMetaClass(const AbstractMetaClassPtr &cls,
+ const _CodeModelItem *item)
+{
+ m_itemToClass.insert(item, cls);
+ m_classToItem.insert(cls, item);
+ if (cls->typeEntry()->isContainer()) {
+ m_templates << cls;
+ } else if (cls->typeEntry()->isSmartPointer()) {
+ m_smartPointers << cls;
+ } else {
+ m_metaClasses << cls;
+ }
+}
+
+AbstractMetaClassPtr
+ AbstractMetaBuilderPrivate::traverseNamespace(const FileModelItem &dom,
+ const NamespaceModelItem &namespaceItem)
+{
+ QString namespaceName = currentScope()->qualifiedName().join(u"::"_s);
+ if (!namespaceName.isEmpty())
+ namespaceName.append(u"::"_s);
+ namespaceName.append(namespaceItem->name());
+
+ if (TypeDatabase::instance()->isClassRejected(namespaceName)) {
+ m_rejectedClasses.insert({AbstractMetaBuilder::GenerationDisabled,
+ namespaceName, namespaceName, QString{}});
+ return {};
+ }
+
+ auto type = TypeDatabase::instance()->findNamespaceType(namespaceName, namespaceItem->fileName());
+ if (!type) {
+ const QString rejectReason = msgNamespaceNoTypeEntry(namespaceItem, namespaceName);
+ qCWarning(lcShiboken, "%s", qPrintable(rejectReason));
+ m_rejectedClasses.insert({AbstractMetaBuilder::GenerationDisabled,
+ namespaceName, namespaceName, rejectReason});
+ return nullptr;
+ }
+
+ if (namespaceItem->type() == NamespaceType::Inline) {
+ type->setInlineNamespace(true);
+ TypeDatabase::instance()->addInlineNamespaceLookups(type);
+ }
+
+ // Continue populating namespace?
+ AbstractMetaClassPtr metaClass = AbstractMetaClass::findClass(m_metaClasses, type);
+ if (!metaClass) {
+ metaClass.reset(new AbstractMetaClass);
+ metaClass->setTypeEntry(type);
+ addAbstractMetaClass(metaClass, namespaceItem.get());
+ if (auto extendsType = type->extends()) {
+ const auto extended = AbstractMetaClass::findClass(m_metaClasses, extendsType);
+ if (!extended) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgNamespaceToBeExtendedNotFound(extendsType->name(), extendsType->targetLangPackage())));
+ return {};
+ }
+ metaClass->setExtendedNamespace(extended);
+ }
+ } else {
+ m_itemToClass.insert(namespaceItem.get(), metaClass);
+ }
+
+ traverseEnums(namespaceItem, metaClass, namespaceItem->enumsDeclarations());
+
+ pushScope(namespaceItem);
+
+ const ClassList &classes = namespaceItem->classes();
+ for (const ClassModelItem &cls : classes) {
+ const auto mjc = traverseClass(dom, cls, metaClass);
+ if (mjc) {
+ metaClass->addInnerClass(mjc);
+ mjc->setEnclosingClass(metaClass);
+ addAbstractMetaClass(mjc, cls.get());
+ }
+ }
+
+ // Go through all typedefs to see if we have defined any
+ // specific typedefs to be used as classes.
+ const TypeDefList typeDefs = namespaceItem->typeDefs();
+ for (const TypeDefModelItem &typeDef : typeDefs) {
+ const auto cls = traverseTypeDef(dom, typeDef, metaClass);
+ if (cls) {
+ metaClass->addInnerClass(cls);
+ cls->setEnclosingClass(metaClass);
+ addAbstractMetaClass(cls, typeDef.get());
+ }
+ }
+
+ // Traverse namespaces recursively
+ for (const NamespaceModelItem &ni : namespaceItem->namespaces()) {
+ const auto mjc = traverseNamespace(dom, ni);
+ if (mjc) {
+ metaClass->addInnerClass(mjc);
+ mjc->setEnclosingClass(metaClass);
+ m_classToItem.insert(mjc, ni.get()); // Add for enum lookup.
+ m_itemToClass.insert(ni.get(), mjc);
+ }
+ }
+
+ popScope();
+
+ if (!type->include().isValid())
+ setInclude(type, namespaceItem->fileName());
+
+ return metaClass;
+}
+
+std::optional<AbstractMetaEnum>
+ AbstractMetaBuilderPrivate::traverseEnum(const EnumModelItem &enumItem,
+ const AbstractMetaClassPtr &enclosing,
+ const QSet<QString> &enumsDeclarations)
+{
+ QString qualifiedName = enumItem->qualifiedName().join(u"::"_s);
+
+ TypeEntryPtr typeEntry;
+ const auto enclosingTypeEntry = enclosing ? enclosing->typeEntry() : TypeEntryCPtr{};
+ if (enumItem->accessPolicy() == Access::Private) {
+ typeEntry.reset(new EnumTypeEntry(enumItem->qualifiedName().constLast(),
+ QVersionNumber(0, 0), enclosingTypeEntry));
+ TypeDatabase::instance()->addType(typeEntry);
+ } else if (enumItem->enumKind() != AnonymousEnum) {
+ typeEntry = TypeDatabase::instance()->findType(qualifiedName);
+ } else {
+ QStringList tmpQualifiedName = enumItem->qualifiedName();
+ const EnumeratorList &enums = enumItem->enumerators();
+ for (const EnumeratorModelItem &enumValue : enums) {
+ tmpQualifiedName.removeLast();
+ tmpQualifiedName << enumValue->name();
+ qualifiedName = tmpQualifiedName.join(u"::"_s);
+ typeEntry = TypeDatabase::instance()->findType(qualifiedName);
+ if (typeEntry)
+ break;
+ }
+ }
+
+ QString enumName = enumItem->name();
+
+ QString className;
+ if (enclosingTypeEntry)
+ className = enclosingTypeEntry->qualifiedCppName();
+
+ QString rejectReason;
+ if (TypeDatabase::instance()->isEnumRejected(className, enumName, &rejectReason)) {
+ if (typeEntry)
+ typeEntry->setCodeGeneration(TypeEntry::GenerateNothing);
+ m_rejectedEnums.insert({AbstractMetaBuilder::GenerationDisabled, qualifiedName,
+ qualifiedName, rejectReason});
+ return {};
+ }
+
+ const bool rejectionWarning = !enclosing || enclosing->typeEntry()->generateCode();
+
+ if (!typeEntry) {
+ const QString rejectReason = msgNoEnumTypeEntry(enumItem, className);
+ if (rejectionWarning)
+ qCWarning(lcShiboken, "%s", qPrintable(rejectReason));
+ m_rejectedEnums.insert({AbstractMetaBuilder::NotInTypeSystem, qualifiedName,
+ qualifiedName, rejectReason});
+ return {};
+ }
+
+ if (!typeEntry->isEnum()) {
+ const QString rejectReason = msgNoEnumTypeConflict(enumItem, className, typeEntry);
+ if (rejectionWarning)
+ qCWarning(lcShiboken, "%s", qPrintable(rejectReason));
+ m_rejectedEnums.insert({AbstractMetaBuilder::NotInTypeSystem, qualifiedName,
+ qualifiedName, rejectReason});
+ return {};
+ }
+
+ AbstractMetaEnum metaEnum;
+ metaEnum.setEnumKind(enumItem->enumKind());
+ metaEnum.setDeprecated(enumItem->isDeprecated());
+ metaEnum.setUnderlyingType(enumItem->underlyingType());
+ metaEnum.setSigned(enumItem->isSigned());
+ if (enumsDeclarations.contains(qualifiedName)
+ || enumsDeclarations.contains(enumName)) {
+ metaEnum.setHasQEnumsDeclaration(true);
+ }
+
+ auto enumTypeEntry = std::static_pointer_cast<EnumTypeEntry>(typeEntry);
+ metaEnum.setTypeEntry(enumTypeEntry);
+ metaEnum.setAccess(enumItem->accessPolicy());
+ if (metaEnum.access() == Access::Private)
+ typeEntry->setCodeGeneration(TypeEntry::GenerateNothing);
+ // PYSIDE-2088, MSVC signedness issue in Qt
+ const bool castToUnsigned = enumItem->isSigned()
+ && enumTypeEntry->cppType().contains(u"unsigned"_s);
+ const EnumeratorList &enums = enumItem->enumerators();
+ for (const EnumeratorModelItem &valueItem : enums) {
+
+ AbstractMetaEnumValue metaEnumValue;
+ metaEnumValue.setName(valueItem->name());
+ // Deciding the enum value...
+
+ metaEnumValue.setStringValue(valueItem->stringValue());
+ const auto value = valueItem->value();
+ metaEnumValue.setValue(castToUnsigned ? value.toUnsigned() : value);
+ metaEnumValue.setDeprecated(valueItem->isDeprecated());
+ metaEnum.addEnumValue(metaEnumValue);
+ }
+
+ if (!metaEnum.typeEntry()->include().isValid()) {
+ auto te = std::const_pointer_cast<EnumTypeEntry>(metaEnum.typeEntry());
+ setInclude(te, enumItem->fileName());
+ }
+
+ // Register all enum values on Type database
+ const bool isScopedEnum = enumItem->enumKind() == EnumClass;
+ const EnumeratorList &enumerators = enumItem->enumerators();
+ for (const EnumeratorModelItem &e : enumerators) {
+ auto enumValue = std::make_shared<EnumValueTypeEntry>(e->name(), e->stringValue(),
+ enumTypeEntry, isScopedEnum,
+ enumTypeEntry->version());
+ TypeDatabase::instance()->addType(enumValue);
+ if (e->value().isNullValue())
+ enumTypeEntry->setNullValue(enumValue);
+ }
+
+ metaEnum.setEnclosingClass(enclosing);
+ m_enums.insert(typeEntry, metaEnum);
+
+ return metaEnum;
+}
+
+AbstractMetaClassPtr
+ AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelItem &dom,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr &currentClass)
+{
+ auto result = traverseTypeDefHelper(dom, typeDef, currentClass);
+ if (!result && typeDef->type().isPlain()) {
+ const auto &type = typeDef->type();
+ QString fullName;
+ if (currentClass)
+ fullName += currentClass->qualifiedCppName() + "::"_L1;
+ fullName += typeDef->name();
+ QString targetName = typeDef->type().toString();
+ m_typedefTargetToName.insert(targetName, fullName);
+ const QByteArray normalized = QMetaObject::normalizedType(targetName.toUtf8().constData());
+ if (targetName != QLatin1StringView(normalized))
+ m_typedefTargetToName.insert(QString::fromUtf8(normalized), fullName);
+ }
+ return result;
+}
+
+AbstractMetaClassPtr
+ AbstractMetaBuilderPrivate::traverseTypeDefHelper(const FileModelItem &,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr &currentClass)
+{
+ TypeDatabase *types = TypeDatabase::instance();
+ QString className = stripTemplateArgs(typeDef->name());
+
+ QString fullClassName = className;
+ // we have an inner class
+ if (currentClass) {
+ fullClassName = stripTemplateArgs(currentClass->typeEntry()->qualifiedCppName())
+ + u"::"_s + fullClassName;
+ }
+
+ // If this is the alias for a primitive type
+ // we store the aliased type on the alias
+ // TypeEntry
+ const auto ptype = types->findPrimitiveType(className);
+ const auto &targetNames = typeDef->type().qualifiedName();
+ const auto pTarget = targetNames.size() == 1
+ ? types->findPrimitiveType(targetNames.constFirst()) : PrimitiveTypeEntryPtr{};
+ if (ptype) {
+ ptype->setReferencedTypeEntry(pTarget);
+ return nullptr;
+ }
+
+ // It is a (nested?) global typedef to a primitive type
+ // (like size_t = unsigned)? Add it to the type DB.
+ if (pTarget && isCppPrimitive(basicReferencedNonBuiltinTypeEntry(pTarget))
+ && currentClass == nullptr) {
+ auto pte = std::make_shared<PrimitiveTypeEntry>(className, QVersionNumber{},
+ TypeEntryCPtr{});
+ pte->setReferencedTypeEntry(pTarget);
+ pte->setBuiltIn(true);
+ types->addType(pte);
+ return nullptr;
+ }
+
+ // If we haven't specified anything for the typedef, then we don't care
+ auto type = types->findComplexType(fullClassName);
+ if (!type)
+ return nullptr;
+
+ auto metaClass = std::make_shared<AbstractMetaClass>();
+ metaClass->setTypeDef(true);
+ metaClass->setTypeEntry(type);
+ metaClass->setBaseClassNames(QStringList(typeDef->type().toString()));
+
+ // Set the default include file name
+ if (!type->include().isValid())
+ setInclude(type, typeDef->fileName());
+
+ fillAddedFunctions(metaClass);
+
+ return metaClass;
+}
+
+// Add the typedef'ed classes
+void AbstractMetaBuilderPrivate::traverseTypesystemTypedefs()
+{
+ const auto &entries = TypeDatabase::instance()->typedefEntries();
+ for (auto it = entries.begin(), end = entries.end(); it != end; ++it) {
+ const TypedefEntryPtr &te = it.value();
+ auto metaClass = std::make_shared<AbstractMetaClass>();
+ metaClass->setTypeDef(true);
+ metaClass->setTypeEntry(te->target());
+ metaClass->setBaseClassNames(QStringList(te->sourceType()));
+ fillAddedFunctions(metaClass);
+ addAbstractMetaClass(metaClass, nullptr);
+ // Ensure base classes are set up when traversing functions for the
+ // type to be resolved.
+ if (setupInheritance(metaClass)) {
+ // Create an entry to look up up types obtained from parsing
+ // functions in reverse. As opposed to container specializations,
+ // which are generated into every instantiating module (indicated
+ // by ContainerTypeEntry::targetLangPackage() being empty), the
+ // correct index array of the module needs to be found by reverse
+ // mapping the instantiations to the typedef entry.
+ // Synthesize a AbstractMetaType which would be found by an
+ // instantiation.
+ AbstractMetaType sourceType;
+ sourceType.setTypeEntry(metaClass->templateBaseClass()->typeEntry());
+ sourceType.setInstantiations(metaClass->templateBaseClassInstantiations());
+ sourceType.decideUsagePattern();
+ m_typeSystemTypeDefs.append({sourceType, metaClass});
+ }
+ }
+}
+
+AbstractMetaClassPtr AbstractMetaBuilderPrivate::traverseClass(const FileModelItem &dom,
+ const ClassModelItem &classItem,
+ const AbstractMetaClassPtr &currentClass)
+{
+ QString className = stripTemplateArgs(classItem->name());
+ QString fullClassName = className;
+
+ // we have inner an class
+ if (currentClass) {
+ fullClassName = stripTemplateArgs(currentClass->typeEntry()->qualifiedCppName())
+ + u"::"_s + fullClassName;
+ }
+
+ const auto type = TypeDatabase::instance()->findComplexType(fullClassName);
+ AbstractMetaBuilder::RejectReason reason = AbstractMetaBuilder::NoReason;
+
+ if (TypeDatabase::instance()->isClassRejected(fullClassName)) {
+ reason = AbstractMetaBuilder::GenerationDisabled;
+ } else if (!type) {
+ TypeEntryPtr te = TypeDatabase::instance()->findType(fullClassName);
+ if (te && !te->isComplex()) {
+ reason = AbstractMetaBuilder::RedefinedToNotClass;
+ // Set the default include file name
+ if (!te->include().isValid())
+ setInclude(te, classItem->fileName());
+ } else {
+ reason = AbstractMetaBuilder::NotInTypeSystem;
+ }
+ } else if (type->codeGeneration() == TypeEntry::GenerateNothing) {
+ reason = AbstractMetaBuilder::GenerationDisabled;
+ }
+ if (reason != AbstractMetaBuilder::NoReason) {
+ if (fullClassName.isEmpty()) {
+ QTextStream(&fullClassName) << "anonymous struct at " << classItem->fileName()
+ << ':' << classItem->startLine();
+ }
+ m_rejectedClasses.insert({reason, fullClassName, fullClassName, QString{}});
+ return nullptr;
+ }
+
+ auto metaClass = std::make_shared<AbstractMetaClass>();
+ metaClass->setSourceLocation(classItem->sourceLocation());
+ metaClass->setTypeEntry(type);
+ if ((type->typeFlags() & ComplexTypeEntry::ForceAbstract) != 0)
+ *metaClass += AbstractMetaClass::Abstract;
+
+ if (classItem->isFinal())
+ *metaClass += AbstractMetaClass::FinalCppClass;
+
+ if (classItem->classType() == CodeModel::Struct)
+ *metaClass += AbstractMetaClass::Struct;
+
+ QStringList baseClassNames;
+ const QList<_ClassModelItem::BaseClass> &baseClasses = classItem->baseClasses();
+ for (const _ClassModelItem::BaseClass &baseClass : baseClasses) {
+ if (baseClass.accessPolicy == Access::Public)
+ baseClassNames.append(baseClass.name);
+ }
+
+ metaClass->setBaseClassNames(baseClassNames);
+ if (type->stream())
+ metaClass->setStream(true);
+
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug)) {
+ const QString message = type->isContainer()
+ ? u"container: '"_s + fullClassName + u'\''
+ : u"class: '"_s + metaClass->fullName() + u'\'';
+ qCInfo(lcShiboken, "%s", qPrintable(message));
+ }
+
+ TemplateParameterList template_parameters = classItem->templateParameters();
+ TypeEntryCList template_args;
+ template_args.clear();
+ auto argumentParent = typeSystemTypeEntry(metaClass->typeEntry());
+ for (qsizetype i = 0; i < template_parameters.size(); ++i) {
+ const TemplateParameterModelItem &param = template_parameters.at(i);
+ auto param_type =
+ std::make_shared<TemplateArgumentEntry>(param->name(), type->version(),
+ argumentParent);
+ param_type->setOrdinal(i);
+ template_args.append(TypeEntryCPtr(param_type));
+ }
+ metaClass->setTemplateArguments(template_args);
+
+ parseQ_Properties(metaClass, classItem->propertyDeclarations());
+
+ traverseEnums(classItem, metaClass, classItem->enumsDeclarations());
+
+ // Inner classes
+ {
+ const ClassList &innerClasses = classItem->classes();
+ for (const ClassModelItem &ci : innerClasses) {
+ const auto cl = traverseClass(dom, ci, metaClass);
+ if (cl) {
+ cl->setEnclosingClass(metaClass);
+ metaClass->addInnerClass(cl);
+ addAbstractMetaClass(cl, ci.get());
+ }
+ }
+
+ }
+
+ // Go through all typedefs to see if we have defined any
+ // specific typedefs to be used as classes.
+ const TypeDefList typeDefs = classItem->typeDefs();
+ for (const TypeDefModelItem &typeDef : typeDefs) {
+ const auto cls = traverseTypeDef(dom, typeDef, metaClass);
+ if (cls) {
+ cls->setEnclosingClass(metaClass);
+ addAbstractMetaClass(cls, typeDef.get());
+ }
+ }
+
+ // Set the default include file name
+ if (!type->include().isValid())
+ setInclude(type, classItem->fileName());
+
+ return metaClass;
+}
+
+void AbstractMetaBuilderPrivate::traverseScopeMembers(const ScopeModelItem &item,
+ const AbstractMetaClassPtr &metaClass)
+{
+ // Classes/Namespace members
+ traverseFields(item, metaClass);
+ traverseFunctions(item, metaClass);
+
+ // Inner classes
+ const ClassList &innerClasses = item->classes();
+ for (const ClassModelItem &ci : innerClasses)
+ traverseClassMembers(ci);
+}
+
+void AbstractMetaBuilderPrivate::traverseClassMembers(const ClassModelItem &item)
+{
+ const auto metaClass = m_itemToClass.value(item.get());
+ if (metaClass) // Class members
+ traverseScopeMembers(item, metaClass);
+}
+
+void AbstractMetaBuilderPrivate::traverseUsingMembers(const AbstractMetaClassPtr &metaClass) const
+{
+ const _CodeModelItem *item = m_classToItem.value(metaClass);
+ if (item == nullptr || item->kind() != _CodeModelItem::Kind_Class)
+ return;
+ const auto *classItem = static_cast<const _ClassModelItem *>(item);
+ for (const auto &um : classItem->usingMembers()) {
+ QString className = um.className;
+ auto pos = className.indexOf(u'<'); // strip "QList<value>"
+ if (pos != -1)
+ className.truncate(pos);
+ if (auto baseClass = findBaseClass(metaClass, className)) {
+ QString name = um.memberName;
+ const auto lastQualPos = name.lastIndexOf(u"::"_s);
+ if (lastQualPos != -1)
+ name.remove(0, lastQualPos + 2);
+ metaClass->addUsingMember({name, baseClass, um.access});
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUsingMemberClassNotFound(metaClass, um.className,
+ um.memberName)));
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::traverseNamespaceMembers(const NamespaceModelItem &item)
+{
+ const auto metaClass = m_itemToClass.value(item.get());
+ if (!metaClass)
+ return;
+
+ // Namespace members
+ traverseScopeMembers(item, metaClass);
+
+ // Inner namespaces
+ for (const NamespaceModelItem &ni : item->namespaces())
+ traverseNamespaceMembers(ni);
+
+}
+
+static inline QString fieldSignatureWithType(const VariableModelItem &field)
+{
+ return field->name() + " -> "_L1 + field->type().toString();
+}
+
+static inline QString qualifiedFieldSignatureWithType(const QString &className,
+ const VariableModelItem &field)
+{
+ return className + u"::"_s + fieldSignatureWithType(field);
+}
+
+std::optional<AbstractMetaField>
+ AbstractMetaBuilderPrivate::traverseField(const VariableModelItem &field,
+ const AbstractMetaClassCPtr &cls)
+{
+ QString fieldName = field->name();
+ QString className = cls->typeEntry()->qualifiedCppName();
+
+ // Ignore friend decl.
+ if (field->isFriend())
+ return {};
+
+ if (field->accessPolicy() == Access::Private)
+ return {};
+
+ QString rejectReason;
+ if (TypeDatabase::instance()->isFieldRejected(className, fieldName, &rejectReason)) {
+ const QString signature = qualifiedFieldSignatureWithType(className, field);
+ m_rejectedFields.insert({AbstractMetaBuilder::GenerationDisabled,
+ signature, signature, rejectReason});
+ return {};
+ }
+
+
+ AbstractMetaField metaField;
+ metaField.setName(fieldName);
+ metaField.setEnclosingClass(cls);
+
+ TypeInfo fieldType = field->type();
+ auto metaType = translateType(fieldType, cls);
+
+ if (!metaType.has_value()) {
+ const QString type = TypeInfo::resolveType(fieldType, currentScope()).qualifiedName().join(u"::"_s);
+ if (cls->typeEntry()->generateCode()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgSkippingField(field, cls->name(), type)));
+ }
+ return {};
+ }
+
+ metaField.setType(metaType.value());
+
+ metaField.setStatic(field->isStatic());
+ metaField.setAccess(field->accessPolicy());
+
+ return metaField;
+}
+
+static bool applyFieldModifications(AbstractMetaField *f)
+{
+ const auto &modifications = f->modifications();
+ for (const auto &mod : modifications) {
+ if (mod.isRemoved())
+ return false;
+ if (mod.isRenameModifier()) {
+ f->setOriginalName(f->name());
+ f->setName(mod.renamedToName());
+ } else if (!mod.isReadable()) {
+ f->setGetterEnabled(false);
+ } else if (!mod.isWritable()) {
+ f->setSetterEnabled(false);
+ }
+ }
+ return true;
+}
+
+void AbstractMetaBuilderPrivate::traverseFields(const ScopeModelItem &scope_item,
+ const AbstractMetaClassPtr &metaClass)
+{
+ const VariableList &variables = scope_item->variables();
+ for (const VariableModelItem &field : variables) {
+ auto metaFieldO = traverseField(field, metaClass);
+ if (metaFieldO.has_value()) {
+ AbstractMetaField metaField = metaFieldO.value();
+ if (applyFieldModifications(&metaField))
+ metaClass->addField(metaField);
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction)
+{
+ if (!metaFunction->isConversionOperator())
+ return;
+
+ TypeDatabase *types = TypeDatabase::instance();
+ static const QRegularExpression operatorRegExp("^operator "_L1);
+ Q_ASSERT(operatorRegExp.isValid());
+ QString castTo = metaFunction->name().remove(operatorRegExp).trimmed();
+
+ if (castTo.endsWith(u'&'))
+ castTo.chop(1);
+ if (castTo.startsWith(u"const "))
+ castTo.remove(0, 6);
+
+ TypeEntryPtr retType = types->findType(castTo);
+ if (!retType)
+ return;
+
+ AbstractMetaType metaType(retType);
+ metaType.decideUsagePattern();
+ metaFunction->setType(metaType);
+}
+
+AbstractMetaFunctionRawPtrList
+ AbstractMetaBuilderPrivate::classFunctionList(const ScopeModelItem &scopeItem,
+ AbstractMetaClass::Attributes *constructorAttributes,
+ const AbstractMetaClassPtr &currentClass)
+{
+ *constructorAttributes = {};
+ AbstractMetaFunctionRawPtrList result;
+ const FunctionList &scopeFunctionList = scopeItem->functions();
+ result.reserve(scopeFunctionList.size());
+ const bool isNamespace = currentClass->isNamespace();
+ for (const FunctionModelItem &function : scopeFunctionList) {
+ if (isNamespace && function->isOperator()) {
+ traverseOperatorFunction(function, currentClass);
+ } else if (function->isSpaceshipOperator() && !function->isDeleted()) {
+ if (currentClass)
+ AbstractMetaClass::addSynthesizedComparisonOperators(currentClass);
+ } else if (auto *metaFunction = traverseFunction(function, currentClass)) {
+ result.append(metaFunction);
+ } else if (!function->isDeleted() && function->functionType() == CodeModel::Constructor) {
+ auto arguments = function->arguments();
+ *constructorAttributes |= AbstractMetaClass::HasRejectedConstructor;
+ if (arguments.isEmpty() || arguments.constFirst()->defaultValue())
+ *constructorAttributes |= AbstractMetaClass::HasRejectedDefaultConstructor;
+ }
+ }
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::traverseFunctions(const ScopeModelItem& scopeItem,
+ const AbstractMetaClassPtr &metaClass)
+{
+ AbstractMetaClass::Attributes constructorAttributes;
+ const AbstractMetaFunctionRawPtrList functions =
+ classFunctionList(scopeItem, &constructorAttributes, metaClass);
+ metaClass->setAttributes(metaClass->attributes() | constructorAttributes);
+
+ for (AbstractMetaFunction *metaFunction : functions) {
+ if (metaClass->isNamespace())
+ metaFunction->setCppAttribute(FunctionAttribute::Static);
+
+ const auto propertyFunction = metaClass->searchPropertyFunction(metaFunction->name());
+ if (propertyFunction.index >= 0) {
+ QPropertySpec prop = metaClass->propertySpecs().at(propertyFunction.index);
+ switch (propertyFunction.function) {
+ case AbstractMetaClass::PropertyFunction::Read:
+ // Property reader must be in the form "<type> name()"
+ if (!metaFunction->isSignal()
+ && prop.typeEntry() == metaFunction->type().typeEntry()
+ && metaFunction->arguments().isEmpty()) {
+ *metaFunction += AbstractMetaFunction::PropertyReader;
+ metaFunction->setPropertySpecIndex(propertyFunction.index);
+ }
+ break;
+ case AbstractMetaClass::PropertyFunction::Write:
+ // Property setter must be in the form "void name(<type>)"
+ // Make sure the function was created with all arguments; some
+ // argument can be missing during the parsing because of errors
+ // in the typesystem.
+ if (metaFunction->isVoid() && metaFunction->arguments().size() == 1
+ && (prop.typeEntry() == metaFunction->arguments().at(0).type().typeEntry())) {
+ *metaFunction += AbstractMetaFunction::PropertyWriter;
+ metaFunction->setPropertySpecIndex(propertyFunction.index);
+ }
+ break;
+ case AbstractMetaClass::PropertyFunction::Reset:
+ // Property resetter must be in the form "void name()"
+ if (metaFunction->isVoid() && metaFunction->arguments().isEmpty()) {
+ *metaFunction += AbstractMetaFunction::PropertyResetter;
+ metaFunction->setPropertySpecIndex(propertyFunction.index);
+ }
+ break;
+ case AbstractMetaClass::PropertyFunction::Notify:
+ if (metaFunction->isSignal()) {
+ *metaFunction += AbstractMetaFunction::PropertyNotify;
+ metaFunction->setPropertySpecIndex(propertyFunction.index);
+ }
+ }
+ }
+
+ if (metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction
+ && metaFunction->isPrivate()) {
+ metaClass->setHasPrivateConstructor(true);
+ }
+ if (metaFunction->isConstructor() && !metaFunction->isPrivate()) // Including Copy CT
+ metaClass->setHasNonPrivateConstructor(true);
+
+ if (!metaFunction->isDestructor()
+ && !(metaFunction->isPrivate() && metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction)) {
+
+ if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction))
+ qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass, metaFunction)));
+
+ if (metaFunction->isConversionOperator())
+ fixReturnTypeOfConversionOperator(metaFunction);
+
+ AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction));
+ applyFunctionModifications(metaFunction);
+ } else if (metaFunction->isDestructor()) {
+ metaClass->setHasPrivateDestructor(metaFunction->isPrivate());
+ metaClass->setHasProtectedDestructor(metaFunction->isProtected());
+ metaClass->setHasVirtualDestructor(metaFunction->isVirtual());
+ }
+ if (!metaFunction->ownerClass()) {
+ delete metaFunction;
+ metaFunction = nullptr;
+ }
+ }
+
+ fillAddedFunctions(metaClass);
+}
+
+void AbstractMetaBuilderPrivate::fillAddedFunctions(const AbstractMetaClassPtr &metaClass)
+{
+ // Add the functions added by the typesystem
+ QString errorMessage;
+ const AddedFunctionList &addedFunctions = metaClass->typeEntry()->addedFunctions();
+ for (const AddedFunctionPtr &addedFunc : addedFunctions) {
+ if (!traverseAddedMemberFunction(addedFunc, metaClass, &errorMessage))
+ throw Exception(qPrintable(errorMessage));
+ }
+}
+
+QString AbstractMetaBuilder::getSnakeCaseName(const QString &name)
+{
+ const auto size = name.size();
+ if (size < 3)
+ return name;
+ QString result;
+ result.reserve(size + 4);
+ for (qsizetype i = 0; i < size; ++i) {
+ const QChar c = name.at(i);
+ if (c.isUpper()) {
+ if (i > 0) {
+ if (name.at(i - 1).isUpper())
+ return name; // Give up at consecutive upper chars
+ result.append(u'_');
+ }
+ result.append(c.toLower());
+ } else {
+ result.append(c);
+ }
+ }
+ return result;
+}
+
+// Names under which an item will be registered to Python depending on snakeCase
+QStringList AbstractMetaBuilder::definitionNames(const QString &name,
+ TypeSystem::SnakeCase snakeCase)
+{
+ QStringList result;
+ switch (snakeCase) {
+ case TypeSystem::SnakeCase::Unspecified:
+ case TypeSystem::SnakeCase::Disabled:
+ result.append(name);
+ break;
+ case TypeSystem::SnakeCase::Enabled:
+ result.append(AbstractMetaBuilder::getSnakeCaseName(name));
+ break;
+ case TypeSystem::SnakeCase::Both:
+ result.append(AbstractMetaBuilder::getSnakeCaseName(name));
+ if (name != result.constFirst())
+ result.append(name);
+ break;
+ }
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func)
+{
+ AbstractMetaFunction& funcRef = *func;
+ for (const FunctionModification &mod : func->modifications(func->implementingClass())) {
+ if (mod.isRenameModifier()) {
+ func->setOriginalName(func->name());
+ func->setName(mod.renamedToName());
+ } else if (mod.isAccessModifier()) {
+ if (mod.isPublic())
+ funcRef.modifyAccess(Access::Public);
+ else if (mod.isProtected())
+ funcRef.modifyAccess(Access::Protected);
+ else if (mod.isPrivate())
+ funcRef.modifyAccess(Access::Private);
+ }
+ }
+}
+
+bool AbstractMetaBuilderPrivate::setupInheritance(const AbstractMetaClassPtr &metaClass)
+{
+ if (metaClass->inheritanceDone())
+ return true;
+
+ metaClass->setInheritanceDone(true);
+
+ QStringList baseClasses = metaClass->baseClassNames();
+
+ // we only support our own containers and ONLY if there is only one baseclass
+ if (baseClasses.size() == 1 && baseClasses.constFirst().contains(u'<')) {
+ TypeInfo info;
+ ComplexTypeEntryPtr baseContainerType;
+ const auto templ = findTemplateClass(baseClasses.constFirst(), metaClass,
+ &info, &baseContainerType);
+ if (templ) {
+ setupInheritance(templ);
+ if (!inheritTemplate(metaClass, templ, info))
+ return false;
+ metaClass->typeEntry()->setBaseContainerType(templ->typeEntry());
+ return true;
+ }
+
+ if (baseContainerType) {
+ // Container types are not necessarily wrapped as 'real' classes,
+ // but there may still be classes derived from them. In such case,
+ // we still need to set the base container type in order to
+ // generate correct code for type conversion checking.
+ //
+ // Additionally, we consider this case as successfully setting up
+ // inheritance.
+ metaClass->typeEntry()->setBaseContainerType(baseContainerType);
+ return true;
+ }
+
+ qCWarning(lcShiboken, "template baseclass '%s' of '%s' is not known",
+ qPrintable(baseClasses.constFirst()),
+ qPrintable(metaClass->name()));
+ return false;
+ }
+
+ auto *types = TypeDatabase::instance();
+
+ for (const auto &baseClassName : baseClasses) {
+ if (!types->isClassRejected(baseClassName)) {
+ auto typeEntry = types->findType(baseClassName);
+ if (typeEntry == nullptr || !typeEntry->isComplex()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgBaseNotInTypeSystem(metaClass, baseClassName)));
+ return false;
+ }
+ auto baseClass = AbstractMetaClass::findClass(m_metaClasses, typeEntry);
+ if (!baseClass) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnknownBase(metaClass, baseClassName)));
+ return false;
+ }
+ metaClass->addBaseClass(baseClass);
+
+ setupInheritance(baseClass);
+ }
+ }
+
+ // Super class set by attribute "default-superclass".
+ const QString defaultSuperclassName = metaClass->typeEntry()->defaultSuperclass();
+ if (!defaultSuperclassName.isEmpty()) {
+ auto defaultSuper = AbstractMetaClass::findClass(m_metaClasses, defaultSuperclassName);
+ if (defaultSuper != nullptr) {
+ metaClass->setDefaultSuperclass(defaultSuper);
+ } else {
+ QString message;
+ QTextStream(&message) << "Class \"" << defaultSuperclassName
+ << R"(" specified as "default-superclass" of ")" << metaClass->name()
+ << "\" could not be found in the code model.";
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ }
+ }
+
+ return true;
+}
+
+void AbstractMetaBuilderPrivate::traverseEnums(const ScopeModelItem &scopeItem,
+ const AbstractMetaClassPtr &metaClass,
+ const QStringList &enumsDeclarations)
+{
+ const EnumList &enums = scopeItem->enums();
+ const QSet<QString> enumsDeclarationSet(enumsDeclarations.cbegin(), enumsDeclarations.cend());
+ for (const EnumModelItem &enumItem : enums) {
+ auto metaEnum = traverseEnum(enumItem, metaClass, enumsDeclarationSet);
+ if (metaEnum.has_value()) {
+ metaClass->addEnum(metaEnum.value());
+ }
+ }
+}
+
+static void applyDefaultExpressionModifications(const FunctionModificationList &functionMods,
+ int i, AbstractMetaArgument *metaArg)
+{
+ // use replace/remove-default-expression for set default value
+ for (const auto &modification : functionMods) {
+ for (const auto &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == i + 1) {
+ if (argumentModification.removedDefaultExpression()) {
+ metaArg->setDefaultValueExpression(QString());
+ break;
+ }
+ if (!argumentModification.replacedDefaultExpression().isEmpty()) {
+ metaArg->setDefaultValueExpression(argumentModification.replacedDefaultExpression());
+ break;
+ }
+ }
+ }
+ }
+}
+
+static AbstractMetaFunction::FunctionType functionTypeFromName(const QString &);
+
+bool AbstractMetaBuilderPrivate::traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc,
+ QString *errorMessage)
+{
+ AbstractMetaFunction *metaFunction =
+ traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage);
+ if (metaFunction == nullptr)
+ return false;
+ m_globalFunctions << AbstractMetaFunctionCPtr(metaFunction);
+ return true;
+}
+
+AbstractMetaFunction *
+ AbstractMetaBuilderPrivate::traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
+ const AbstractMetaClassPtr &metaClass /* = {} */,
+ QString *errorMessage)
+{
+ auto returnType = translateType(addedFunc->returnType(), metaClass, {}, errorMessage);
+ if (!returnType.has_value()) {
+ *errorMessage =
+ msgAddedFunctionInvalidReturnType(addedFunc->name(),
+ addedFunc->returnType().qualifiedName(),
+ *errorMessage, metaClass);
+ return nullptr;
+ }
+
+ auto *metaFunction = new AbstractMetaFunction(addedFunc);
+ metaFunction->setType(returnType.value());
+ metaFunction->setFunctionType(functionTypeFromName(addedFunc->name()));
+
+ const auto &args = addedFunc->arguments();
+
+ qsizetype argCount = args.size();
+ // Check "foo(void)"
+ if (argCount == 1 && args.constFirst().typeInfo.isVoid())
+ argCount = 0;
+ for (qsizetype i = 0; i < argCount; ++i) {
+ const AddedFunction::Argument &arg = args.at(i);
+ auto type = translateType(arg.typeInfo, metaClass, {}, errorMessage);
+ if (Q_UNLIKELY(!type.has_value())) {
+ *errorMessage =
+ msgAddedFunctionInvalidArgType(addedFunc->name(),
+ arg.typeInfo.qualifiedName(), i + 1,
+ *errorMessage, metaClass);
+ delete metaFunction;
+ return nullptr;
+ }
+ type->decideUsagePattern();
+
+ AbstractMetaArgument metaArg;
+ if (!args.at(i).name.isEmpty())
+ metaArg.setName(args.at(i).name);
+ metaArg.setType(type.value());
+ metaArg.setArgumentIndex(i);
+ metaArg.setDefaultValueExpression(arg.defaultValue);
+ metaArg.setOriginalDefaultValueExpression(arg.defaultValue);
+ metaFunction->addArgument(metaArg);
+ }
+
+ AbstractMetaArgumentList &metaArguments = metaFunction->arguments();
+
+ if (metaFunction->isOperatorOverload() && !metaFunction->isCallOperator()) {
+ if (metaArguments.size() > 2) {
+ qCWarning(lcShiboken) << "An operator overload need to have 0, 1 or 2 arguments if it's reverse.";
+ } else if (metaArguments.size() == 2) {
+ // Check if it's a reverse operator
+ if (metaArguments[1].type().typeEntry() == metaClass->typeEntry()) {
+ metaFunction->setReverseOperator(true);
+ // we need to call these two function to cache the old signature (with two args)
+ // we do this buggy behaviour to comply with the original apiextractor buggy behaviour.
+ metaFunction->signature();
+ metaFunction->minimalSignature();
+ metaArguments.removeLast();
+ metaFunction->setArguments(metaArguments);
+ } else {
+ qCWarning(lcShiboken) << "Operator overload can have two arguments only if it's a reverse operator!";
+ }
+ }
+ }
+
+
+ // Find the correct default values
+ const FunctionModificationList functionMods = metaFunction->modifications(metaClass);
+ applyCachedFunctionModifications(metaFunction, functionMods);
+ for (qsizetype i = 0; i < metaArguments.size(); ++i) {
+ AbstractMetaArgument &metaArg = metaArguments[i];
+
+ // use replace-default-expression for set default value
+ applyDefaultExpressionModifications(functionMods, i, &metaArg);
+ metaArg.setOriginalDefaultValueExpression(metaArg.defaultValueExpression()); // appear unmodified
+ }
+
+ if (!metaArguments.isEmpty())
+ fixArgumentNames(metaFunction, metaFunction->modifications(metaClass));
+
+ return metaFunction;
+}
+
+bool AbstractMetaBuilderPrivate::traverseAddedMemberFunction(const AddedFunctionPtr &addedFunc,
+ const AbstractMetaClassPtr &metaClass,
+ QString *errorMessage)
+{
+ AbstractMetaFunction *metaFunction =
+ traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage);
+ if (metaFunction == nullptr)
+ return false;
+
+ const AbstractMetaArgumentList fargs = metaFunction->arguments();
+ if (metaClass->isNamespace())
+ metaFunction->setCppAttribute(FunctionAttribute::Static);
+ if (metaFunction->name() == metaClass->name()) {
+ metaFunction->setFunctionType(AbstractMetaFunction::ConstructorFunction);
+ if (fargs.size() == 1) {
+ const auto te = fargs.constFirst().type().typeEntry();
+ if (te->isCustom())
+ metaFunction->setExplicit(true);
+ if (te->name() == metaFunction->name())
+ metaFunction->setFunctionType(AbstractMetaFunction::CopyConstructorFunction);
+ }
+ }
+
+ metaFunction->setDeclaringClass(metaClass);
+ metaFunction->setImplementingClass(metaClass);
+ AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction));
+ metaClass->setHasNonPrivateConstructor(true);
+ return true;
+}
+
+void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods)
+{
+ AbstractMetaArgumentList &arguments = func->arguments();
+
+ for (const FunctionModification &mod : mods) {
+ for (const ArgumentModification &argMod : mod.argument_mods()) {
+ if (!argMod.renamedToName().isEmpty())
+ arguments[argMod.index() - 1].setName(argMod.renamedToName(), false);
+ }
+ }
+
+ for (qsizetype i = 0, size = arguments.size(); i < size; ++i) {
+ if (arguments.at(i).name().isEmpty())
+ arguments[i].setName(u"arg__"_s + QString::number(i + 1), false);
+ }
+}
+
+static QString functionSignature(const FunctionModelItem &functionItem)
+{
+ QStringList args;
+ const ArgumentList &arguments = functionItem->arguments();
+ for (const ArgumentModelItem &arg : arguments)
+ args << arg->type().toString();
+ return functionItem->name() + u'(' + args.join(u',') + u')';
+}
+
+static inline QString qualifiedFunctionSignatureWithType(const FunctionModelItem &functionItem,
+ const QString &className = QString())
+{
+ QString result = functionItem->type().toString() + u' ';
+ if (!className.isEmpty())
+ result += className + u"::"_s;
+ result += functionSignature(functionItem);
+ return result;
+}
+static inline AbstractMetaFunction::FunctionType functionTypeFromCodeModel(CodeModel::FunctionType ft)
+{
+ AbstractMetaFunction::FunctionType result = AbstractMetaFunction::NormalFunction;
+ switch (ft) {
+ case CodeModel::Constructor:
+ result = AbstractMetaFunction::ConstructorFunction;
+ break;
+ case CodeModel::CopyConstructor:
+ result = AbstractMetaFunction::CopyConstructorFunction;
+ break;
+ case CodeModel::MoveConstructor:
+ result = AbstractMetaFunction::MoveConstructorFunction;
+ break;
+ case CodeModel::Destructor:
+ result = AbstractMetaFunction::DestructorFunction;
+ break;
+ case CodeModel::AssignmentOperator:
+ result = AbstractMetaFunction::AssignmentOperatorFunction;
+ break;
+ case CodeModel::CallOperator:
+ result = AbstractMetaFunction::CallOperator;
+ break;
+ case CodeModel::ConversionOperator:
+ result = AbstractMetaFunction::ConversionOperator;
+ break;
+ case CodeModel::DereferenceOperator:
+ result = AbstractMetaFunction::DereferenceOperator;
+ break;
+ case CodeModel::ReferenceOperator:
+ result = AbstractMetaFunction::ReferenceOperator;
+ break;
+ case CodeModel::ArrowOperator:
+ result = AbstractMetaFunction::ArrowOperator;
+ break;
+ case CodeModel::ArithmeticOperator:
+ result = AbstractMetaFunction::ArithmeticOperator;
+ break;
+ case CodeModel::IncrementOperator:
+ result = AbstractMetaFunction::IncrementOperator;
+ break;
+ case CodeModel::DecrementOperator:
+ result = AbstractMetaFunction::DecrementOperator;
+ break;
+ case CodeModel::BitwiseOperator:
+ result = AbstractMetaFunction::BitwiseOperator;
+ break;
+ case CodeModel::LogicalOperator:
+ result = AbstractMetaFunction::LogicalOperator;
+ break;
+ case CodeModel::ShiftOperator:
+ result = AbstractMetaFunction::ShiftOperator;
+ break;
+ case CodeModel::SubscriptOperator:
+ result = AbstractMetaFunction::SubscriptOperator;
+ break;
+ case CodeModel::ComparisonOperator:
+ result = AbstractMetaFunction::ComparisonOperator;
+ break;
+ case CodeModel::Normal:
+ break;
+ case CodeModel::Signal:
+ result = AbstractMetaFunction::SignalFunction;
+ break;
+ case CodeModel::Slot:
+ result = AbstractMetaFunction::SlotFunction;
+ break;
+ }
+ return result;
+}
+
+static AbstractMetaFunction::FunctionType functionTypeFromName(const QString &name)
+{
+ if (name == u"__getattro__")
+ return AbstractMetaFunction::GetAttroFunction;
+ if (name == u"__setattro__")
+ return AbstractMetaFunction::SetAttroFunction;
+ const auto typeOpt = _FunctionModelItem::functionTypeFromName(name);
+ if (typeOpt.has_value())
+ return functionTypeFromCodeModel(typeOpt.value());
+ return AbstractMetaFunction::NormalFunction;
+}
+
+// Apply the <array> modifications of the arguments
+static bool applyArrayArgumentModifications(const FunctionModificationList &functionMods,
+ AbstractMetaFunction *func,
+ QString *errorMessage)
+{
+ for (const FunctionModification &mod : functionMods) {
+ for (const ArgumentModification &argMod : mod.argument_mods()) {
+ if (argMod.isArray()) {
+ const int i = argMod.index() - 1;
+ if (i < 0 || i >= func->arguments().size()) {
+ *errorMessage = msgCannotSetArrayUsage(func->minimalSignature(), i,
+ u"Index out of range."_s);
+ return false;
+ }
+ auto t = func->arguments().at(i).type();
+ if (!t.applyArrayModification(errorMessage)) {
+ *errorMessage = msgCannotSetArrayUsage(func->minimalSignature(), i, *errorMessage);
+ return false;
+ }
+ func->arguments()[i].setType(t);
+ }
+ }
+ }
+ return true;
+}
+
+// Create the meta type for a view (std::string_view -> std::string)
+static AbstractMetaType createViewOnType(const AbstractMetaType &metaType,
+ const TypeEntryCPtr &viewOnTypeEntry)
+{
+ auto result = metaType;
+ result.setTypeEntry(viewOnTypeEntry);
+ if (!metaType.isContainer() || !viewOnTypeEntry->isContainer())
+ return result;
+ // For containers, when sth with several template parameters
+ // (std::span<T, int N>) is mapped onto a std::vector<T>,
+ // remove the superfluous template parameters and strip 'const'.
+ const auto vcte = std::static_pointer_cast<const ContainerTypeEntry>(viewOnTypeEntry);
+ const auto &instantiations = metaType.instantiations();
+ AbstractMetaTypeList viewInstantiations;
+ const auto size = std::min(vcte->templateParameterCount(), instantiations.size());
+ for (qsizetype i = 0; i < size; ++i) {
+ auto ins = instantiations.at(i);
+ ins.setConstant(false);
+ viewInstantiations.append(ins);
+ }
+ result.setInstantiations(viewInstantiations);
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::rejectFunction(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr &currentClass,
+ AbstractMetaBuilder::RejectReason reason,
+ const QString &rejectReason)
+{
+ QString sortKey;
+ if (currentClass)
+ sortKey += currentClass->typeEntry()->qualifiedCppName() + u"::"_s;
+ sortKey += functionSignature(functionItem); // Sort without return type
+ const QString signatureWithType = functionItem->type().toString() + u' ' + sortKey;
+ m_rejectedFunctions.insert({reason, signatureWithType, sortKey, rejectReason});
+}
+
+AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr &currentClass)
+{
+ const auto *tdb = TypeDatabase::instance();
+
+ if (!functionItem->templateParameters().isEmpty())
+ return nullptr;
+
+ if (functionItem->isDeleted()) {
+ switch (functionItem->functionType()) {
+ case CodeModel::Constructor:
+ if (functionItem->isDefaultConstructor())
+ currentClass->setHasDeletedDefaultConstructor(true);
+ break;
+ case CodeModel::CopyConstructor:
+ currentClass->setHasDeletedCopyConstructor(true);
+ break;
+ default:
+ break;
+ }
+ return nullptr;
+ }
+ const QString &functionName = functionItem->name();
+ const QString className = currentClass != nullptr ?
+ currentClass->typeEntry()->qualifiedCppName() : QString{};
+
+ if (m_apiExtractorFlags.testFlag(ApiExtractorFlag::UsePySideExtensions)) {
+ // Skip enum helpers generated by Q_ENUM
+ if ((currentClass == nullptr || currentClass->isNamespace())
+ && (functionName == u"qt_getEnumMetaObject" || functionName == u"qt_getEnumName")) {
+ return nullptr;
+ }
+
+ // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT
+ // and overridden metaObject(), QGADGET helpers
+ if (currentClass != nullptr) {
+ if (functionName == u"qt_check_for_QGADGET_macro"
+ || functionName.startsWith(u"qt_meta")) {
+ return nullptr;
+ }
+ if (functionName == u"metaObject" && className != u"QObject")
+ return nullptr;
+ }
+ } // PySide extensions
+
+ QString rejectReason;
+ if (tdb->isFunctionRejected(className, functionName, &rejectReason)) {
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::GenerationDisabled, rejectReason);
+ return nullptr;
+ }
+
+ const QString &signature = functionSignature(functionItem);
+ if (tdb->isFunctionRejected(className, signature, &rejectReason)) {
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::GenerationDisabled, rejectReason);
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug)) {
+ qCInfo(lcShiboken, "%s::%s was rejected by the type database (%s).",
+ qPrintable(className), qPrintable(signature), qPrintable(rejectReason));
+ }
+ return nullptr;
+ }
+
+ if (functionItem->isFriend())
+ return nullptr;
+
+ const auto cppAttributes = functionItem->attributes();
+ const bool deprecated = cppAttributes.testFlag(FunctionAttribute::Deprecated);
+ if (deprecated && m_skipDeprecated) {
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::GenerationDisabled, u" is deprecated."_s);
+ return nullptr;
+ }
+
+ AbstractMetaFunction::Flags flags;
+ auto *metaFunction = new AbstractMetaFunction(functionName);
+ metaFunction->setCppAttributes(cppAttributes);
+ const QByteArray cSignature = signature.toUtf8();
+ const QString unresolvedSignature =
+ QString::fromUtf8(QMetaObject::normalizedSignature(cSignature.constData()));
+ metaFunction->setUnresolvedSignature(unresolvedSignature);
+ if (functionItem->isHiddenFriend())
+ flags.setFlag(AbstractMetaFunction::Flag::HiddenFriend);
+ metaFunction->setSourceLocation(functionItem->sourceLocation());
+
+ // Additional check for assignment/move assignment down below
+ metaFunction->setFunctionType(functionTypeFromCodeModel(functionItem->functionType()));
+ metaFunction->setConstant(functionItem->isConstant());
+ metaFunction->setExceptionSpecification(functionItem->exceptionSpecification());
+
+ // Access rights
+ metaFunction->setAccess(functionItem->accessPolicy());
+
+ QString errorMessage;
+ switch (metaFunction->functionType()) {
+ case AbstractMetaFunction::DestructorFunction:
+ metaFunction->setType(AbstractMetaType::createVoid());
+ break;
+ case AbstractMetaFunction::ConstructorFunction:
+ metaFunction->setName(currentClass->name());
+ metaFunction->setType(AbstractMetaType::createVoid());
+ break;
+ default: {
+ TypeInfo returnType = functionItem->type();
+
+ if (tdb->isReturnTypeRejected(className, returnType.toString(), &rejectReason)) {
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::GenerationDisabled, rejectReason);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ TranslateTypeFlags flags;
+ if (functionItem->scopeResolution())
+ flags.setFlag(AbstractMetaBuilder::NoClassScopeLookup);
+ auto type = translateType(returnType, currentClass, flags, &errorMessage);
+ if (!type.has_value()) {
+ const QString reason = msgUnmatchedReturnType(functionItem, errorMessage);
+ const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgSkippingFunction(functionItem, signature, reason)));
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::UnmatchedReturnType, reason);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ metaFunction->setType(type.value());
+ }
+ break;
+ }
+
+ ArgumentList arguments = functionItem->arguments();
+ // Add private signals for documentation purposes
+ if (!arguments.isEmpty()
+ && m_apiExtractorFlags.testFlag(ApiExtractorFlag::UsePySideExtensions)
+ && functionItem->functionType() == CodeModel::Signal
+ && arguments.constLast()->type().qualifiedName().constLast() == u"QPrivateSignal") {
+ flags.setFlag(AbstractMetaFunction::Flag::PrivateSignal);
+ arguments.removeLast();
+ }
+
+ if (arguments.size() == 1) {
+ ArgumentModelItem arg = arguments.at(0);
+ TypeInfo type = arg->type();
+ if (type.qualifiedName().constFirst() == u"void" && type.indirections() == 0)
+ arguments.pop_front();
+ }
+
+ for (qsizetype i = 0; i < arguments.size(); ++i) {
+ const ArgumentModelItem &arg = arguments.at(i);
+
+ if (tdb->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) {
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::GenerationDisabled, rejectReason);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ TranslateTypeFlags flags;
+ if (arg->scopeResolution())
+ flags.setFlag(AbstractMetaBuilder::NoClassScopeLookup);
+ auto metaTypeO = translateType(arg->type(), currentClass, flags, &errorMessage);
+ if (!metaTypeO.has_value()) {
+ // If an invalid argument has a default value, simply remove it
+ // unless the function is virtual (since the override in the
+ // wrapper can then not correctly be generated).
+ if (arg->defaultValue()
+ && !functionItem->attributes().testFlag(FunctionAttribute::Virtual)) {
+ if (!currentClass || currentClass->typeEntry()->generateCode()) {
+ const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgStrippingArgument(functionItem, i, signature,
+ arg, errorMessage)));
+ }
+ break;
+ }
+ const QString reason = msgUnmatchedParameterType(arg, i, errorMessage);
+ const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgSkippingFunction(functionItem, signature, reason)));
+ rejectFunction(functionItem, currentClass,
+ AbstractMetaBuilder::UnmatchedArgumentType, reason);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ auto metaType = metaTypeO.value();
+ // Add view substitution for simple view types of function arguments
+ // std::string_view -> std::string for foo(std::string_view)
+ auto viewOnTypeEntry = metaType.typeEntry()->viewOn();
+ if (viewOnTypeEntry != nullptr && metaType.indirections() == 0
+ && metaType.arrayElementType() == nullptr
+ && (!metaType.hasInstantiations() || metaType.isContainer())) {
+ metaType.setViewOn(createViewOnType(metaType, viewOnTypeEntry));
+ }
+
+ AbstractMetaArgument metaArgument;
+ metaArgument.setType(metaType);
+ metaArgument.setName(arg->name());
+ metaArgument.setArgumentIndex(i);
+ metaFunction->addArgument(metaArgument);
+ }
+
+ AbstractMetaArgumentList &metaArguments = metaFunction->arguments();
+
+ const FunctionModificationList functionMods = currentClass
+ ? AbstractMetaFunction::findClassModifications(metaFunction, currentClass)
+ : AbstractMetaFunction::findGlobalModifications(metaFunction);
+
+ applyCachedFunctionModifications(metaFunction, functionMods);
+
+ // Find the correct default values
+ for (qsizetype i = 0, size = metaArguments.size(); i < size; ++i) {
+ const ArgumentModelItem &arg = arguments.at(i);
+ AbstractMetaArgument &metaArg = metaArguments[i];
+
+ const QString originalDefaultExpression =
+ fixDefaultValue(arg->defaultValueExpression(), metaArg.type(), currentClass);
+
+ metaArg.setOriginalDefaultValueExpression(originalDefaultExpression);
+ metaArg.setDefaultValueExpression(originalDefaultExpression);
+
+ applyDefaultExpressionModifications(functionMods, i, &metaArg);
+
+ //Check for missing argument name
+ if (!metaArg.defaultValueExpression().isEmpty()
+ && !metaArg.hasName()
+ && !metaFunction->isOperatorOverload()
+ && !metaFunction->isSignal()
+ && metaFunction->argumentName(i + 1, false, currentClass).isEmpty()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnnamedArgumentDefaultExpression(currentClass, i + 1,
+ className, metaFunction)));
+ }
+
+ }
+
+ if (!metaArguments.isEmpty()) {
+ fixArgumentNames(metaFunction, functionMods);
+ QString errorMessage;
+ if (!applyArrayArgumentModifications(functionMods, metaFunction, &errorMessage)) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgArrayModificationFailed(functionItem, className, errorMessage)));
+ }
+ }
+
+ // Determine class special functions
+ if (currentClass && metaFunction->arguments().size() == 1) {
+ const AbstractMetaType &argType = metaFunction->arguments().constFirst().type();
+ if (argType.typeEntry() == currentClass->typeEntry() && argType.indirections() == 0) {
+ if (metaFunction->name() == u"operator=") {
+ switch (argType.referenceType()) {
+ case NoReference:
+ metaFunction->setFunctionType(AbstractMetaFunction::AssignmentOperatorFunction);
+ break;
+ case LValueReference:
+ if (argType.isConstant())
+ metaFunction->setFunctionType(AbstractMetaFunction::AssignmentOperatorFunction);
+ break;
+ case RValueReference:
+ metaFunction->setFunctionType(AbstractMetaFunction::MoveAssignmentOperatorFunction);
+ break;
+ }
+ }
+ }
+ }
+ metaFunction->setFlags(flags);
+ return metaFunction;
+}
+
+static TypeEntryCPtr findTypeEntryUsingContext(const AbstractMetaClassCPtr &metaClass,
+ const QString& qualifiedName)
+{
+ TypeEntryCPtr type;
+ QStringList context = metaClass->qualifiedCppName().split(u"::"_s);
+ while (!type && !context.isEmpty()) {
+ type = TypeDatabase::instance()->findType(context.join(u"::"_s) + u"::"_s + qualifiedName);
+ context.removeLast();
+ }
+ return type;
+}
+
+// Helper for findTypeEntries/translateTypeStatic()
+TypeEntryCList AbstractMetaBuilderPrivate::findTypeEntriesHelper(const QString &qualifiedName,
+ const QString &name,
+ TranslateTypeFlags flags,
+ const AbstractMetaClassCPtr &currentClass,
+ AbstractMetaBuilderPrivate *d)
+{
+ // 5.1 - Try first using the current scope
+ if (currentClass != nullptr
+ && !flags.testFlag(AbstractMetaBuilder::NoClassScopeLookup)) {
+ if (auto type = findTypeEntryUsingContext(currentClass, qualifiedName))
+ return {type};
+
+ // 5.1.1 - Try using the class parents' scopes
+ if (d && !currentClass->baseClassNames().isEmpty()) {
+ const auto &baseClasses = d->getBaseClasses(currentClass);
+ for (const auto &cls : baseClasses) {
+ if (auto type = findTypeEntryUsingContext(cls, qualifiedName))
+ return {type};
+ }
+ }
+ }
+
+ // 5.2 - Try without scope
+ auto types = TypeDatabase::instance()->findCppTypes(qualifiedName);
+ if (!types.isEmpty())
+ return types;
+
+ // 6. No? Try looking it up as a flags type
+ if (auto type = TypeDatabase::instance()->findFlagsType(qualifiedName))
+ return {type};
+
+ // 7. No? Try looking it up as a container type
+ if (auto type = TypeDatabase::instance()->findContainerType(name))
+ return {type};
+
+ // 8. No? Check if the current class is a template and this type is one
+ // of the parameters.
+ if (currentClass) {
+ const auto &template_args = currentClass->templateArguments();
+ for (const auto &te : template_args) {
+ if (te->name() == qualifiedName)
+ return {te};
+ }
+ }
+ return {};
+}
+
+// Helper for translateTypeStatic() that calls findTypeEntriesHelper()
+// and does some error checking.
+TypeEntryCList AbstractMetaBuilderPrivate::findTypeEntries(const QString &qualifiedName,
+ const QString &name,
+ TranslateTypeFlags flags,
+ const AbstractMetaClassCPtr &currentClass,
+ AbstractMetaBuilderPrivate *d,
+ QString *errorMessage)
+{
+ TypeEntryCList types = findTypeEntriesHelper(qualifiedName, name, flags,
+ currentClass, d);
+ if (types.isEmpty()) {
+ if (errorMessage != nullptr)
+ *errorMessage = msgCannotFindTypeEntry(qualifiedName);
+ return {};
+ }
+
+ // Resolve entries added by metabuilder (for example, "GLenum") to match
+ // the signatures for modifications.
+ for (qsizetype i = 0, size = types.size(); i < size; ++i) {
+ const auto &e = types.at(i);
+ if (e->isPrimitive()) {
+ const auto pte = std::static_pointer_cast<const PrimitiveTypeEntry>(e);
+ types[i] = basicReferencedNonBuiltinTypeEntry(pte);
+ }
+ }
+
+ if (types.size() == 1)
+ return types;
+
+ const auto typeEntryType = types.constFirst()->type();
+ const bool sameType = std::all_of(types.cbegin() + 1, types.cend(),
+ [typeEntryType](const TypeEntryCPtr &e) {
+ return e->type() == typeEntryType;
+ });
+
+ if (!sameType) {
+ if (errorMessage != nullptr)
+ *errorMessage = msgAmbiguousVaryingTypesFound(qualifiedName, types);
+ return {};
+ }
+ // Ambiguous primitive/smart pointer types are possible (when
+ // including type systems).
+ if (typeEntryType != TypeEntry::PrimitiveType
+ && typeEntryType != TypeEntry::SmartPointerType) {
+ if (errorMessage != nullptr)
+ *errorMessage = msgAmbiguousTypesFound(qualifiedName, types);
+ return {};
+ }
+ return types;
+}
+
+// Reverse lookup of AbstractMetaType representing a template specialization
+// found during traversing function arguments to its type system typedef'ed
+// class.
+AbstractMetaClassCPtr AbstractMetaBuilderPrivate::resolveTypeSystemTypeDef(const AbstractMetaType &t) const
+{
+ if (t.hasInstantiations()) {
+ auto pred = [t](const TypeClassEntry &e) { return e.type == t; };
+ auto it = std::find_if(m_typeSystemTypeDefs.cbegin(), m_typeSystemTypeDefs.cend(), pred);
+ if (it != m_typeSystemTypeDefs.cend())
+ return it->klass;
+ }
+ return nullptr;
+}
+
+// The below helpers and AbstractMetaBuilderPrivate::fixSmartPointers()
+// synthesize missing smart pointer functions and classes. For example for
+// std::shared_ptr, the full class declaration or base classes from
+// internal, compiler-dependent STL implementation headers might not be exposed
+// to the parser unless those headers are specified as <system-include>.
+
+static void synthesizeWarning(const AbstractMetaFunctionCPtr &f)
+{
+ qCWarning(lcShiboken, "Synthesizing \"%s\"...",
+ qPrintable(f->classQualifiedSignature()));
+}
+
+static AbstractMetaFunctionPtr
+ addMethod(const AbstractMetaClassPtr &s, const AbstractMetaType &returnType,
+ const QString &name, bool isConst = true)
+{
+ auto function = std::make_shared<AbstractMetaFunction>(name);
+ function->setType(returnType);
+ AbstractMetaClass::addFunction(s, function);
+ function->setConstant(isConst);
+ synthesizeWarning(function);
+ return function;
+}
+
+static AbstractMetaFunctionPtr
+ addMethod(const AbstractMetaClassPtr &s, const QString &returnTypeName,
+ const QString &name, bool isConst = true)
+{
+ auto typeEntry = TypeDatabase::instance()->findPrimitiveType(returnTypeName);
+ Q_ASSERT(typeEntry);
+ AbstractMetaType returnType(typeEntry);
+ returnType.decideUsagePattern();
+ return addMethod(s, returnType, name, isConst);
+}
+
+// Create the instantiation type of a smart pointer
+static AbstractMetaType instantiationType(const AbstractMetaClassCPtr &s,
+ const SmartPointerTypeEntryCPtr &ste)
+{
+ AbstractMetaType type(s->templateArguments().constFirst());
+ if (ste->smartPointerType() != TypeSystem::SmartPointerType::ValueHandle)
+ type.addIndirection();
+ type.decideUsagePattern();
+ return type;
+}
+
+// Create the pointee argument of a smart pointer constructor or reset()
+static AbstractMetaArgument pointeeArgument(const AbstractMetaClassCPtr &s,
+ const SmartPointerTypeEntryCPtr &ste)
+{
+ AbstractMetaArgument pointee;
+ pointee.setType(instantiationType(s, ste));
+ pointee.setName(u"pointee"_s);
+ return pointee;
+}
+
+// Add the smart pointer constructors. For MSVC, (when not specifying
+// <system-header>), clang only sees the default constructor.
+static void fixSmartPointerConstructors(const AbstractMetaClassPtr &s,
+ const SmartPointerTypeEntryCPtr &ste)
+{
+ const auto ctors = s->queryFunctions(FunctionQueryOption::Constructors);
+ bool seenDefaultConstructor = false;
+ bool seenParameter = false;
+ for (const auto &ctor : ctors) {
+ if (ctor->arguments().isEmpty())
+ seenDefaultConstructor = true;
+ else
+ seenParameter = true;
+ }
+
+ if (!seenParameter) {
+ auto constructor = std::make_shared<AbstractMetaFunction>(s->name());
+ constructor->setFunctionType(AbstractMetaFunction::ConstructorFunction);
+ constructor->addArgument(pointeeArgument(s, ste));
+ AbstractMetaClass::addFunction(s, constructor);
+ synthesizeWarning(constructor);
+ }
+
+ if (!seenDefaultConstructor) {
+ auto constructor = std::make_shared<AbstractMetaFunction>(s->name());
+ constructor->setFunctionType(AbstractMetaFunction::ConstructorFunction);
+ AbstractMetaClass::addFunction(s, constructor);
+ synthesizeWarning(constructor);
+ }
+}
+
+// Similarly, add the smart pointer reset() functions
+static void fixSmartPointerReset(const AbstractMetaClassPtr &s,
+ const SmartPointerTypeEntryCPtr &ste)
+{
+ const QString resetMethodName = ste->resetMethod();
+ const auto functions = s->findFunctions(resetMethodName);
+ bool seenParameterLess = false;
+ bool seenParameter = false;
+ for (const auto &function : functions) {
+ if (function->arguments().isEmpty())
+ seenParameterLess = true;
+ else
+ seenParameter = true;
+ }
+
+ if (!seenParameter) {
+ auto f = std::make_shared<AbstractMetaFunction>(resetMethodName);
+ f->addArgument(pointeeArgument(s, ste));
+ AbstractMetaClass::addFunction(s, f);
+ synthesizeWarning(f);
+ }
+
+ if (!seenParameterLess) {
+ auto f = std::make_shared<AbstractMetaFunction>(resetMethodName);
+ AbstractMetaClass::addFunction(s, f);
+ synthesizeWarning(f);
+ }
+}
+
+// Add the relevant missing smart pointer functions.
+static void fixSmartPointerClass(const AbstractMetaClassPtr &s,
+ const SmartPointerTypeEntryCPtr &ste)
+{
+ fixSmartPointerConstructors(s, ste);
+
+ if (!ste->resetMethod().isEmpty())
+ fixSmartPointerReset(s, ste);
+
+ const QString getterName = ste->getter();
+ if (!s->findFunction(getterName))
+ addMethod(s, instantiationType(s, ste), getterName);
+
+ const QString refCountName = ste->refCountMethodName();
+ if (!refCountName.isEmpty() && !s->findFunction(refCountName))
+ addMethod(s, u"int"_s, refCountName);
+
+ const QString valueCheckMethod = ste->valueCheckMethod();
+ if (!valueCheckMethod.isEmpty() && !s->findFunction(valueCheckMethod)) {
+ auto f = addMethod(s, u"bool"_s, valueCheckMethod);
+ if (valueCheckMethod == u"operator bool")
+ f->setFunctionType(AbstractMetaFunction::ConversionOperator);
+ }
+
+ const QString nullCheckMethod = ste->nullCheckMethod();
+ if (!nullCheckMethod.isEmpty() && !s->findFunction(nullCheckMethod))
+ addMethod(s, u"bool"_s, nullCheckMethod);
+}
+
+// Create a missing smart pointer class
+static AbstractMetaClassPtr createSmartPointerClass(const SmartPointerTypeEntryCPtr &ste,
+ const AbstractMetaClassList &allClasses)
+{
+ auto result = std::make_shared<AbstractMetaClass>();
+ result->setTypeEntry(std::const_pointer_cast<SmartPointerTypeEntry>(ste));
+ auto templateArg = std::make_shared<TemplateArgumentEntry>(u"T"_s, ste->version(),
+ typeSystemTypeEntry(ste));
+ result->setTemplateArguments({templateArg});
+ fixSmartPointerClass(result, ste);
+ auto enclosingTe = ste->parent();
+ if (!enclosingTe->isTypeSystem()) {
+ const auto enclosing = AbstractMetaClass::findClass(allClasses, enclosingTe);
+ if (!enclosing)
+ throw Exception(msgEnclosingClassNotFound(ste));
+ result->setEnclosingClass(enclosing);
+ auto inner = enclosing->innerClasses();
+ inner.append(std::const_pointer_cast<const AbstractMetaClass>(result));
+ enclosing->setInnerClasses(inner);
+ }
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::fixSmartPointers()
+{
+ const auto smartPointerTypes = TypeDatabase::instance()->smartPointerTypes();
+ for (const auto &ste : smartPointerTypes) {
+ const auto smartPointerClass =
+ AbstractMetaClass::findClass(m_smartPointers, ste);
+ if (smartPointerClass) {
+ fixSmartPointerClass(std::const_pointer_cast<AbstractMetaClass>(smartPointerClass),
+ ste);
+ } else {
+ qCWarning(lcShiboken, "Synthesizing smart pointer \"%s\"...",
+ qPrintable(ste->qualifiedCppName()));
+ m_smartPointers.append(createSmartPointerClass(ste, m_metaClasses));
+ }
+ }
+}
+
+std::optional<AbstractMetaType>
+ AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typei,
+ const AbstractMetaClassCPtr &currentClass,
+ TranslateTypeFlags flags,
+ QString *errorMessage)
+{
+ return translateTypeStatic(_typei, currentClass, this, flags, errorMessage);
+}
+
+static bool isNumber(const QString &s)
+{
+ return std::all_of(s.cbegin(), s.cend(),
+ [](QChar c) { return c.isDigit(); });
+}
+
+// A type entry relevant only for non type template "X<5>"
+static bool isNonTypeTemplateArgument(const TypeEntryCPtr &te)
+{
+ const auto type = te->type();
+ return type == TypeEntry::EnumValue || type == TypeEntry::ConstantValueType;
+}
+
+std::optional<AbstractMetaType>
+ AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo &_typei,
+ const AbstractMetaClassCPtr &currentClass,
+ AbstractMetaBuilderPrivate *d,
+ TranslateTypeFlags flags,
+ QString *errorMessageIn)
+{
+ if (_typei.isVoid())
+ return AbstractMetaType::createVoid();
+
+ // 1. Test the type info without resolving typedefs in case this is present in the
+ // type system
+ const bool resolveType = !flags.testFlag(AbstractMetaBuilder::DontResolveType);
+ if (resolveType) {
+ auto resolved =
+ translateTypeStatic(_typei, currentClass, d,
+ flags | AbstractMetaBuilder::DontResolveType,
+ errorMessageIn);
+ if (resolved.has_value())
+ return resolved;
+ }
+
+ TypeInfo typeInfo = _typei;
+ if (resolveType) {
+ // Go through all parts of the current scope (including global namespace)
+ // to resolve typedefs. The parser does not properly resolve typedefs in
+ // the global scope when they are referenced from inside a namespace.
+ // This is a work around to fix this bug since fixing it in resolveType
+ // seemed non-trivial
+ qsizetype i = d ? d->m_scopes.size() - 1 : -1;
+ while (i >= 0) {
+ typeInfo = TypeInfo::resolveType(_typei, d->m_scopes.at(i--));
+ if (typeInfo.qualifiedName().join(u"::"_s) != _typei.qualifiedName().join(u"::"_s))
+ break;
+ }
+
+ }
+
+ if (typeInfo.isFunctionPointer()) {
+ if (errorMessageIn)
+ *errorMessageIn = msgUnableToTranslateType(_typei, u"Unsupported function pointer."_s);
+ return {};
+ }
+
+ QString errorMessage;
+
+ // 2. Handle arrays.
+ // 2.1 Handle char arrays with unspecified size (aka "const char[]") as "const char*" with
+ // NativePointerPattern usage.
+ bool oneDimensionalArrayOfUnspecifiedSize =
+ typeInfo.arrayElements().size() == 1
+ && typeInfo.arrayElements().at(0).isEmpty();
+
+ bool isConstCharStarCase =
+ oneDimensionalArrayOfUnspecifiedSize
+ && typeInfo.qualifiedName().size() == 1
+ && typeInfo.qualifiedName().at(0) == "char"_L1
+ && typeInfo.indirections() == 0
+ && typeInfo.isConstant()
+ && typeInfo.referenceType() == NoReference
+ && typeInfo.arguments().isEmpty();
+
+ if (isConstCharStarCase)
+ typeInfo.setIndirections(typeInfo.indirections() + typeInfo.arrayElements().size());
+
+ // 2.2 Handle regular arrays.
+ if (!typeInfo.arrayElements().isEmpty() && !isConstCharStarCase) {
+ TypeInfo newInfo;
+ //newInfo.setArguments(typeInfo.arguments());
+ newInfo.setIndirectionsV(typeInfo.indirectionsV());
+ newInfo.setConstant(typeInfo.isConstant());
+ newInfo.setVolatile(typeInfo.isVolatile());
+ newInfo.setFunctionPointer(typeInfo.isFunctionPointer());
+ newInfo.setQualifiedName(typeInfo.qualifiedName());
+ newInfo.setReferenceType(typeInfo.referenceType());
+ newInfo.setVolatile(typeInfo.isVolatile());
+
+ auto elementType = translateTypeStatic(newInfo, currentClass, d, flags, &errorMessage);
+ if (!elementType.has_value()) {
+ if (errorMessageIn) {
+ errorMessage.prepend(u"Unable to translate array element: "_s);
+ *errorMessageIn = msgUnableToTranslateType(_typei, errorMessage);
+ }
+ return {};
+ }
+
+ for (auto i = typeInfo.arrayElements().size() - 1; i >= 0; --i) {
+ AbstractMetaType arrayType;
+ arrayType.setArrayElementType(elementType.value());
+ const QString &arrayElement = typeInfo.arrayElements().at(i);
+ if (!arrayElement.isEmpty()) {
+ bool _ok;
+ const qint64 elems = d
+ ? d->findOutValueFromString(arrayElement, _ok)
+ : arrayElement.toLongLong(&_ok, 0);
+ if (_ok)
+ arrayType.setArrayElementCount(int(elems));
+ }
+ auto elementTypeEntry = elementType->typeEntry();
+ auto at = std::make_shared<ArrayTypeEntry>(elementTypeEntry, elementTypeEntry->version(),
+ elementTypeEntry->parent());
+ arrayType.setTypeEntry(at);
+ arrayType.decideUsagePattern();
+
+ elementType = arrayType;
+ }
+
+ return elementType;
+ }
+
+ QStringList qualifierList = typeInfo.qualifiedName();
+ if (qualifierList.isEmpty()) {
+ errorMessage = msgUnableToTranslateType(_typei, u"horribly broken type"_s);
+ if (errorMessageIn)
+ *errorMessageIn = errorMessage;
+ else
+ qCWarning(lcShiboken,"%s", qPrintable(errorMessage));
+ return {};
+ }
+
+ QString qualifiedName = qualifierList.join(u"::"_s);
+ QString name = qualifierList.takeLast();
+
+ // 4. Special case QFlags (include instantiation in name)
+ if (qualifiedName == u"QFlags") {
+ qualifiedName = typeInfo.toString();
+ typeInfo.clearInstantiations();
+ }
+
+ TypeEntryCList types = findTypeEntries(qualifiedName, name, flags,
+ currentClass, d, errorMessageIn);
+ if (!flags.testFlag(AbstractMetaBuilder::TemplateArgument)) {
+ // Avoid clashes between QByteArray and enum value QMetaType::QByteArray
+ // unless we are looking for template arguments.
+ auto end = std::remove_if(types.begin(), types.end(),
+ isNonTypeTemplateArgument);
+ types.erase(end, types.end());
+ }
+
+ if (types.isEmpty()) {
+ if (errorMessageIn != nullptr)
+ *errorMessageIn = msgUnableToTranslateType(_typei, *errorMessageIn);
+ return {};
+ }
+
+ TypeEntryCPtr type = types.constFirst();
+ const TypeEntry::Type typeEntryType = type->type();
+
+ AbstractMetaType metaType;
+ metaType.setIndirectionsV(typeInfo.indirectionsV());
+ metaType.setReferenceType(typeInfo.referenceType());
+ metaType.setConstant(typeInfo.isConstant());
+ metaType.setVolatile(typeInfo.isVolatile());
+ metaType.setOriginalTypeDescription(_typei.toString());
+
+ const auto &templateArguments = typeInfo.instantiations();
+ for (qsizetype t = 0, size = templateArguments.size(); t < size; ++t) {
+ const TypeInfo &ti = templateArguments.at(t);
+ auto targType = translateTypeStatic(ti, currentClass, d,
+ flags | AbstractMetaBuilder::TemplateArgument,
+ &errorMessage);
+ // For non-type template parameters, create a dummy type entry on the fly
+ // as is done for classes.
+ if (!targType.has_value()) {
+ const QString value = ti.qualifiedName().join(u"::"_s);
+ if (isNumber(value)) {
+ auto module = typeSystemTypeEntry(type);
+ TypeDatabase::instance()->addConstantValueTypeEntry(value, module);
+ targType = translateTypeStatic(ti, currentClass, d, flags, &errorMessage);
+ }
+ }
+ if (!targType) {
+ if (errorMessageIn)
+ *errorMessageIn = msgCannotTranslateTemplateArgument(t, ti, errorMessage);
+ return {};
+ }
+
+ metaType.addInstantiation(targType.value());
+ }
+
+ if (typeEntryType == TypeEntry::SmartPointerType) {
+ // Find a matching instantiation
+ if (metaType.instantiations().size() != 1) {
+ if (errorMessageIn)
+ *errorMessageIn = msgInvalidSmartPointerType(_typei);
+ return {};
+ }
+ auto instantiationType = metaType.instantiations().constFirst().typeEntry();
+ if (instantiationType->type() == TypeEntry::TemplateArgumentType) {
+ // Member functions of the template itself, SharedPtr(const SharedPtr &)
+ type = instantiationType;
+ } else {
+ auto it = std::find_if(types.cbegin(), types.cend(),
+ [instantiationType](const TypeEntryCPtr &e) {
+ auto smartPtr = std::static_pointer_cast<const SmartPointerTypeEntry>(e);
+ return smartPtr->matchesInstantiation(instantiationType);
+ });
+ if (it == types.cend()) {
+ if (errorMessageIn)
+ *errorMessageIn = msgCannotFindSmartPointerInstantion(_typei);
+ return {};
+ }
+ type =*it;
+ }
+ }
+
+ metaType.setTypeEntry(type);
+
+ // The usage pattern *must* be decided *after* the possible template
+ // instantiations have been determined, or else the absence of
+ // such instantiations will break the caching scheme of
+ // AbstractMetaType::cppSignature().
+ metaType.decideUsagePattern();
+
+ if (d) {
+ // Reverse lookup of type system typedefs. Replace by class.
+ if (auto klass = d->resolveTypeSystemTypeDef(metaType)) {
+ metaType = AbstractMetaType{};
+ metaType.setTypeEntry(klass->typeEntry());
+ metaType.decideUsagePattern();
+ }
+ }
+
+ return metaType;
+}
+
+std::optional<AbstractMetaType>
+ AbstractMetaBuilder::translateType(const TypeInfo &_typei,
+ const AbstractMetaClassPtr &currentClass,
+ TranslateTypeFlags flags,
+ QString *errorMessage)
+{
+ return AbstractMetaBuilderPrivate::translateTypeStatic(_typei, currentClass,
+ nullptr, flags,
+ errorMessage);
+}
+
+std::optional<AbstractMetaType>
+ AbstractMetaBuilder::translateType(const QString &t,
+ const AbstractMetaClassPtr &currentClass,
+ TranslateTypeFlags flags,
+ QString *errorMessageIn)
+{
+ QString errorMessage;
+ TypeInfo typeInfo = TypeParser::parse(t, &errorMessage);
+ if (typeInfo.qualifiedName().isEmpty()) {
+ errorMessage = msgUnableToTranslateType(t, errorMessage);
+ if (errorMessageIn)
+ *errorMessageIn = errorMessage;
+ else
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ return {};
+ }
+ return translateType(typeInfo, currentClass, flags, errorMessageIn);
+}
+
+qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringValue, bool &ok)
+{
+ qint64 value = stringValue.toLongLong(&ok);
+ if (ok)
+ return value;
+
+ if (stringValue == u"true" || stringValue == u"false") {
+ ok = true;
+ return (stringValue == u"true");
+ }
+
+ // This is a very lame way to handle expression evaluation,
+ // but it is not critical and will do for the time being.
+ static const QRegularExpression variableNameRegExp("^[a-zA-Z_][a-zA-Z0-9_]*$"_L1);
+ Q_ASSERT(variableNameRegExp.isValid());
+ if (!variableNameRegExp.match(stringValue).hasMatch()) {
+ ok = true;
+ return 0;
+ }
+
+ auto enumValue = AbstractMetaClass::findEnumValue(m_metaClasses, stringValue);
+ if (enumValue.has_value()) {
+ ok = true;
+ return enumValue->value().value();
+ }
+
+ for (const AbstractMetaEnum &metaEnum : std::as_const(m_globalEnums)) {
+ auto ev = metaEnum.findEnumValue(stringValue);
+ if (ev.has_value()) {
+ ok = true;
+ return ev->value().value();
+ }
+ }
+
+ ok = false;
+ return 0;
+}
+
+// Return whether candidate is some underqualified specification of qualifiedType
+// ("B::C" should be qualified to "A::B::C")
+static bool isUnderQualifiedSpec(QStringView qualifiedType, QStringView candidate)
+{
+ const auto candidateSize = candidate.size();
+ const auto qualifiedTypeSize = qualifiedType.size();
+ return candidateSize < qualifiedTypeSize
+ && qualifiedType.endsWith(candidate)
+ && qualifiedType.at(qualifiedTypeSize - candidateSize - 1) == u':';
+}
+
+QString AbstractMetaBuilder::fixEnumDefault(const AbstractMetaType &type,
+ const QString &expr,
+ const AbstractMetaClassCPtr &klass) const
+{
+ return d->fixEnumDefault(type, expr, klass);
+}
+
+void AbstractMetaBuilder::setCodeModelTestMode(bool b)
+{
+ AbstractMetaBuilderPrivate::m_codeModelTestMode = b;
+}
+
+// Helper to fix a simple default value (field or enum reference) in a
+// class context.
+QString AbstractMetaBuilderPrivate::fixSimpleDefaultValue(QStringView expr,
+ const AbstractMetaClassCPtr &klass) const
+{
+ const QString field = qualifyStaticField(klass, expr);
+
+ if (!field.isEmpty())
+ return field;
+ const auto cit = m_classToItem.constFind(klass);
+ if (cit == m_classToItem.cend())
+ return {};
+ auto *scope = dynamic_cast<const _ScopeModelItem *>(cit.value());
+ if (!scope)
+ return {};
+ if (auto enumValue = scope->findEnumByValue(expr))
+ return enumValue.qualifiedName;
+ return {};
+}
+
+// see TestResolveType::testFixDefaultArguments()
+QString AbstractMetaBuilderPrivate::fixDefaultValue(QString expr, const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &implementingClass) const
+{
+ expr.replace(u'\n', u' '); // breaks signature parser
+
+ if (AbstractMetaBuilder::dontFixDefaultValue(expr))
+ return expr;
+
+ if (type.isFlags() || type.isEnum()) {
+ expr = fixEnumDefault(type, expr, implementingClass);
+ } else if (type.isContainer() && expr.contains(u'<')) {
+ // Expand a container of a nested class, fex
+ // "QList<FormatRange>()" -> "QList<QTextLayout::FormatRange>()"
+ if (type.instantiations().size() != 1)
+ return expr; // Only simple types are handled, not QMap<int, int>.
+ auto innerTypeEntry = type.instantiations().constFirst().typeEntry();
+ if (!innerTypeEntry->isComplex())
+ return expr;
+ const QString &qualifiedInnerTypeName = innerTypeEntry->qualifiedCppName();
+ if (!qualifiedInnerTypeName.contains(u"::")) // Nothing to qualify here
+ return expr;
+ const auto openPos = expr.indexOf(u'<');
+ const auto closingPos = expr.lastIndexOf(u'>');
+ if (openPos == -1 || closingPos == -1)
+ return expr;
+ const auto innerPos = openPos + 1;
+ const auto innerLen = closingPos - innerPos;
+ const auto innerType = QStringView{expr}.mid(innerPos, innerLen).trimmed();
+ if (isUnderQualifiedSpec(qualifiedInnerTypeName, innerType))
+ expr.replace(innerPos, innerLen, qualifiedInnerTypeName);
+ } else {
+ // Here the default value is supposed to be a constructor, a class field,
+ // a constructor receiving a static class field or an enum. Consider
+ // class QSqlDatabase { ...
+ // static const char *defaultConnection;
+ // QSqlDatabase(const QString &connection = QLatin1String(defaultConnection))
+ // -> = QLatin1String(QSqlDatabase::defaultConnection)
+ // static void foo(QSqlDatabase db = QSqlDatabase(defaultConnection));
+ // -> = QSqlDatabase(QSqlDatabase::defaultConnection)
+ //
+ // Enum values from the class as defaults of int and others types (via
+ // implicit conversion) are handled here as well:
+ // class QStyleOption { ...
+ // enum StyleOptionType { Type = SO_Default };
+ // QStyleOption(..., int type = SO_Default);
+ // -> = QStyleOption::StyleOptionType::SO_Default
+
+ // Is this a single field or an enum?
+ if (isQualifiedCppIdentifier(expr)) {
+ const QString fixed = fixSimpleDefaultValue(expr, implementingClass);
+ return fixed.isEmpty() ? expr : fixed;
+ }
+
+ // Is this sth like "QLatin1String(field)", "Class(Field)", "Class()"?
+ const auto parenPos = expr.indexOf(u'(');
+ if (parenPos == -1 || !expr.endsWith(u')'))
+ return expr;
+ // Is the term within parentheses a class field or enum?
+ const auto innerLength = expr.size() - parenPos - 2;
+ if (innerLength > 0) { // Not some function call "defaultFunc()"
+ const auto inner = QStringView{expr}.mid(parenPos + 1, innerLength);
+ if (isQualifiedCppIdentifier(inner)
+ && !AbstractMetaBuilder::dontFixDefaultValue(inner)) {
+ const QString replacement = fixSimpleDefaultValue(inner, implementingClass);
+ if (!replacement.isEmpty() && replacement != inner)
+ expr.replace(parenPos + 1, innerLength, replacement);
+ }
+ }
+ // Is this a class constructor "Class(Field)"? Expand it.
+ const auto te = type.typeEntry();
+ if (!te->isComplex())
+ return expr;
+ const QString &qualifiedTypeName = te->qualifiedCppName();
+ if (!qualifiedTypeName.contains(u"::")) // Nothing to qualify here
+ return expr;
+ const auto className = QStringView{expr}.left(parenPos);
+ if (isUnderQualifiedSpec(qualifiedTypeName, className))
+ expr.replace(0, className.size(), qualifiedTypeName);
+ }
+
+ return expr;
+}
+
+QString AbstractMetaBuilder::fixDefaultValue(const QString &expr, const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &c) const
+{
+ return d->fixDefaultValue(expr, type, c);
+}
+
+bool AbstractMetaBuilderPrivate::isEnum(const FileModelItem &dom, const QStringList& qualified_name)
+{
+ CodeModelItem item = dom->model()->findItem(qualified_name, dom);
+ return item && item->kind() == _EnumModelItem::__node_kind;
+}
+
+AbstractMetaClassPtr
+ AbstractMetaBuilderPrivate::findTemplateClass(const QString &name,
+ const AbstractMetaClassCPtr &context,
+ TypeInfo *info,
+ ComplexTypeEntryPtr *baseContainerType) const
+{
+ if (baseContainerType)
+ baseContainerType->reset();
+ auto *types = TypeDatabase::instance();
+
+ QStringList scope = context->typeEntry()->qualifiedCppName().split(u"::"_s);
+ QString errorMessage;
+ scope.removeLast();
+ for (auto i = scope.size(); i >= 0; --i) {
+ QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join(u"::"_s) + u"::"_s : QString();
+ QString completeName = prefix + name;
+ const TypeInfo parsed = TypeParser::parse(completeName, &errorMessage);
+ QString qualifiedName = parsed.qualifiedName().join(u"::"_s);
+ if (qualifiedName.isEmpty()) {
+ qWarning().noquote().nospace() << "Unable to parse type \"" << completeName
+ << "\" while looking for template \"" << name << "\": " << errorMessage;
+ continue;
+ }
+ if (info)
+ *info = parsed;
+
+ AbstractMetaClassPtr templ;
+ for (const auto &c : std::as_const(m_templates)) {
+ if (c->typeEntry()->name() == qualifiedName) {
+ templ = c;
+ break;
+ }
+ }
+
+ if (!templ)
+ templ = AbstractMetaClass::findClass(m_metaClasses, qualifiedName);
+
+ if (templ)
+ return templ;
+
+ if (baseContainerType)
+ *baseContainerType = types->findContainerType(qualifiedName);
+ }
+
+ return nullptr;
+}
+
+AbstractMetaClassCList
+ AbstractMetaBuilderPrivate::getBaseClasses(const AbstractMetaClassCPtr &metaClass) const
+{
+ // Shortcut if inheritance has already been set up
+ if (metaClass->inheritanceDone() || !metaClass->needsInheritanceSetup())
+ return metaClass->baseClasses();
+ AbstractMetaClassCList baseClasses;
+ const QStringList &baseClassNames = metaClass->baseClassNames();
+ for (const QString& parent : baseClassNames) {
+ const auto cls = parent.contains(u'<')
+ ? findTemplateClass(parent, metaClass)
+ : AbstractMetaClass::findClass(m_metaClasses, parent);
+
+ if (cls)
+ baseClasses << cls;
+ }
+ return baseClasses;
+}
+
+std::optional<AbstractMetaType>
+ AbstractMetaBuilderPrivate::inheritTemplateType(const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaType &metaType)
+{
+ auto returned = metaType;
+
+ if (!metaType.typeEntry()->isTemplateArgument() && !metaType.hasInstantiations())
+ return returned;
+
+ returned.setOriginalTemplateType(metaType);
+
+ if (returned.typeEntry()->isTemplateArgument()) {
+ const auto tae = std::static_pointer_cast<const TemplateArgumentEntry>(returned.typeEntry());
+
+ // If the template is intantiated with void we special case this as rejecting the functions that use this
+ // parameter from the instantiation.
+ const AbstractMetaType &templateType = templateTypes.value(tae->ordinal());
+ if (templateType.typeEntry()->isVoid())
+ return {};
+
+ AbstractMetaType t = returned;
+ t.setTypeEntry(templateType.typeEntry());
+ t.setIndirections(templateType.indirections() + t.indirections() ? 1 : 0);
+ t.decideUsagePattern();
+
+ return inheritTemplateType(templateTypes, t);
+ }
+
+ if (returned.hasInstantiations()) {
+ AbstractMetaTypeList instantiations = returned.instantiations();
+ for (qsizetype i = 0; i < instantiations.size(); ++i) {
+ auto ins = inheritTemplateType(templateTypes, instantiations.at(i));
+ if (!ins.has_value())
+ return {};
+ instantiations[i] = ins.value();
+ }
+ returned.setInstantiations(instantiations);
+ }
+
+ return returned;
+}
+
+AbstractMetaClassPtr
+ AbstractMetaBuilder::inheritTemplateClass(const ComplexTypeEntryPtr &te,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes)
+{
+ auto result = std::make_shared<AbstractMetaClass>();
+ result->setTypeDef(true);
+
+ result->setTypeEntry(te);
+ if (!AbstractMetaBuilderPrivate::inheritTemplate(result, templateClass,
+ templateTypes)) {
+ return {};
+ }
+ AbstractMetaBuilderPrivate::inheritTemplateFunctions(result);
+ return result;
+}
+
+
+static std::optional<AbstractMetaType>
+ inheritTemplateParameter(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const TypeInfo &info, QString *errorMessage)
+{
+ QString typeName = info.qualifiedName().join("::"_L1);
+ TypeDatabase *typeDb = TypeDatabase::instance();
+ TypeEntryPtr t;
+ // Check for a non-type template integer parameter, that is, for a base
+ // "template <int R, int C> Matrix<R, C>" and subclass
+ // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of
+ // EnumValueTypeEntry for the integer values encountered on the fly.
+ if (isNumber(typeName)) {
+ t = typeDb->findType(typeName);
+ if (!t) {
+ auto parent = typeSystemTypeEntry(subclass->typeEntry());
+ t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent);
+ }
+ } else {
+ QStringList possibleNames;
+ possibleNames << subclass->qualifiedCppName() + "::"_L1 + typeName;
+ possibleNames << templateClass->qualifiedCppName() + "::"_L1 + typeName;
+ if (subclass->enclosingClass())
+ possibleNames << subclass->enclosingClass()->qualifiedCppName() + "::"_L1 + typeName;
+ possibleNames << typeName;
+
+ for (const QString &possibleName : std::as_const(possibleNames)) {
+ t = typeDb->findType(possibleName);
+ if (t)
+ break;
+ }
+ }
+
+ if (!t) {
+ *errorMessage = msgIgnoringTemplateParameter(typeName,
+ "The corresponding type was not found in the typesystem.");
+ return std::nullopt;
+ }
+
+ if (t->isContainer()) {
+ *errorMessage = msgIgnoringTemplateParameter(typeName,
+ "Template inheritance from nested containers is not supported");
+ return std::nullopt;
+ }
+ AbstractMetaType result(t);
+ result.setConstant(info.isConstant());
+ result.setReferenceType(info.referenceType());
+ result.setIndirectionsV(info.indirectionsV());
+ result.decideUsagePattern();
+ return result;
+}
+
+bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const TypeInfo &info)
+{
+ AbstractMetaTypeList templateTypes;
+
+ QString errorMessage;
+ for (const TypeInfo &i : info.instantiations()) {
+ const auto typeO = inheritTemplateParameter(subclass, templateClass, i, &errorMessage);
+ if (typeO.has_value()) {
+ templateTypes.append(typeO.value());
+ } else {
+ errorMessage = msgInheritTemplateIssue(subclass, info, errorMessage);
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ }
+ }
+ if (templateTypes.isEmpty()) {
+ errorMessage = msgInheritTemplateIssue(subclass, info,
+ "No template parameters could be inherited"_L1);
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ return false;
+ }
+ return inheritTemplate(subclass, templateClass, templateTypes);
+}
+
+bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes)
+{
+ subclass->setTemplateBaseClass(templateClass);
+ subclass->setTemplateBaseClassInstantiations(templateTypes);
+ subclass->setBaseClass(templateClass->baseClass());
+ return true;
+}
+
+AbstractMetaFunctionPtr
+ AbstractMetaBuilderPrivate::inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes)
+{
+ AbstractMetaFunctionPtr f(function->copy());
+ f->setArguments(AbstractMetaArgumentList());
+ f->setFlags(f->flags() | AbstractMetaFunction::Flag::InheritedFromTemplate);
+
+ if (!function->isVoid()) {
+ auto returnType = inheritTemplateType(templateTypes, function->type());
+ if (!returnType.has_value())
+ return {};
+ f->setType(returnType.value());
+ }
+
+ const AbstractMetaArgumentList &arguments = function->arguments();
+ for (const AbstractMetaArgument &argument : arguments) {
+ auto argType = inheritTemplateType(templateTypes, argument.type());
+ if (!argType.has_value())
+ return {};
+ AbstractMetaArgument arg = argument;
+ arg.setType(argType.value());
+ f->addArgument(arg);
+ }
+
+ return f;
+}
+
+AbstractMetaFunctionPtr
+ AbstractMetaBuilder::inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes)
+{
+ return AbstractMetaBuilderPrivate::inheritTemplateFunction(function, templateTypes);
+}
+
+AbstractMetaFunctionPtr
+ AbstractMetaBuilderPrivate::inheritTemplateMember(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaClassPtr &subclass)
+{
+ AbstractMetaFunctionPtr f = inheritTemplateFunction(function, templateTypes);
+ if (!f)
+ return {};
+
+ // There is no base class in the target language to inherit from here, so
+ // the template instantiation is the class that implements the function.
+ f->setImplementingClass(subclass);
+
+ // We also set it as the declaring class, since the superclass is
+ // supposed to disappear. This allows us to make certain function modifications
+ // on the inherited functions.
+ f->setDeclaringClass(subclass);
+
+ if (f->isConstructor()) {
+ f->setName(subclass->name());
+ f->setOriginalName(subclass->name());
+ }
+
+ ComplexTypeEntryPtr te = subclass->typeEntry();
+ const FunctionModificationList mods = function->modifications(templateClass);
+
+ for (auto mod : mods) {
+ mod.setSignature(f->minimalSignature());
+
+// If we ever need it... Below is the code to do
+// substitution of the template instantation type inside
+// injected code..
+#if 0
+ if (mod.modifiers & Modification::CodeInjection) {
+ for (int j = 0; j < template_types.size(); ++j) {
+ CodeSnip &snip = mod.snips.last();
+ QString code = snip.code();
+ code.replace(QString::fromLatin1("$$QT_TEMPLATE_%1$$").arg(j),
+ template_types.at(j)->typeEntry()->qualifiedCppName());
+ snip.codeList.clear();
+ snip.addCode(code);
+ }
+ }
+#endif
+ te->addFunctionModification(mod);
+ }
+
+ QString errorMessage;
+ if (!applyArrayArgumentModifications(f->modifications(subclass), f.get(),
+ &errorMessage)) {
+ qCWarning(lcShiboken, "While specializing %s (%s): %s",
+ qPrintable(subclass->name()), qPrintable(templateClass->name()),
+ qPrintable(errorMessage));
+ }
+ return f;
+}
+
+AbstractMetaFunctionPtr
+ AbstractMetaBuilder::inheritTemplateMember(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaClassPtr &subclass)
+{
+ return AbstractMetaBuilderPrivate::inheritTemplateMember(function, templateTypes,
+ templateClass, subclass);
+}
+
+static bool doInheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaFunctionCList &existingSubclassFuncs,
+ const AbstractMetaClassCPtr &templateBaseClass,
+ const AbstractMetaClassCPtr &subclass)
+{
+ // If the function is modified or the instantiation has an equally named
+ // function we are shadowing, so we need to skip it (unless the subclass
+ // declares it via "using").
+ if (function->isModifiedRemoved())
+ return false;
+ if (function->isConstructor() && !subclass->isTypeDef())
+ return false;
+ return AbstractMetaFunction::find(existingSubclassFuncs, function->name()) == nullptr
+ || subclass->isUsingMember(templateBaseClass, function->name(), Access::Protected);
+}
+
+void AbstractMetaBuilderPrivate::inheritTemplateFunctions(const AbstractMetaClassPtr &subclass)
+{
+ auto templateClass = subclass->templateBaseClass();
+
+ if (subclass->isTypeDef()) {
+ subclass->setHashFunction(templateClass->hashFunction());
+ subclass->setHasNonPrivateConstructor(templateClass->hasNonPrivateConstructor());
+ subclass->setHasPrivateDestructor(templateClass->hasPrivateDestructor());
+ subclass->setHasProtectedDestructor(templateClass->hasProtectedDestructor());
+ subclass->setHasVirtualDestructor(templateClass->hasVirtualDestructor());
+ }
+
+ const auto &templateTypes = subclass->templateBaseClassInstantiations();
+ const AbstractMetaFunctionCList existingSubclassFuncs =
+ subclass->functions(); // Take copy
+ const auto &templateClassFunctions = templateClass->functions();
+ for (const auto &function : templateClassFunctions) {
+ if (doInheritTemplateFunction(function, existingSubclassFuncs,
+ templateClass, subclass)) {
+ AbstractMetaFunctionCPtr f = inheritTemplateMember(function, templateTypes,
+ templateClass, subclass);
+ if (f)
+ AbstractMetaClass::addFunction(subclass, f);
+ }
+ }
+
+ // Take copy
+ const AbstractMetaFieldList existingSubclassFields = subclass->fields();
+ const AbstractMetaFieldList &templateClassFields = templateClass->fields();
+ for (const AbstractMetaField &field : templateClassFields) {
+ // If the field is modified or the instantiation has a field named
+ // the same as an existing field we have shadowing, so we need to skip it.
+ if (field.isModifiedRemoved() || field.isStatic()
+ || AbstractMetaField::find(existingSubclassFields, field.name()).has_value()) {
+ continue;
+ }
+
+ AbstractMetaField f = field;
+ f.setEnclosingClass(subclass);
+ auto fieldType = inheritTemplateType(templateTypes, field.type());
+ if (!fieldType.has_value())
+ continue;
+ f.setType(fieldType.value());
+ subclass->addField(f);
+ }
+}
+
+void AbstractMetaBuilderPrivate::parseQ_Properties(const AbstractMetaClassPtr &metaClass,
+ const QStringList &declarations)
+{
+ const QStringList scopes = currentScope()->qualifiedName();
+ QString errorMessage;
+ int i = 0;
+ for (; i < declarations.size(); ++i) {
+ auto spec = QPropertySpec::parseQ_Property(this, metaClass, declarations.at(i), scopes, &errorMessage);
+ if (spec.has_value()) {
+ spec->setIndex(i);
+ metaClass->addPropertySpec(spec.value());
+ } else if (!errorMessage.isEmpty()) {
+ QString message;
+ QTextStream str(&message);
+ str << metaClass->sourceLocation() << errorMessage;
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ }
+ }
+
+ // User-added properties
+ auto typeEntry = metaClass->typeEntry();
+ for (const TypeSystemProperty &tp : typeEntry->properties()) {
+ std::optional<QPropertySpec> spec;
+ if (metaClass->propertySpecByName(tp.name))
+ errorMessage = msgPropertyExists(metaClass->name(), tp.name);
+ else
+ spec = QPropertySpec::fromTypeSystemProperty(this, metaClass, tp, scopes, &errorMessage);
+
+ if (spec.has_value()) {
+ spec->setIndex(i++);
+ metaClass->addPropertySpec(spec.value());
+ } else {
+ QString message;
+ QTextStream str(&message);
+ str << typeEntry->sourceLocation() << errorMessage;
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::setupExternalConversion(const AbstractMetaClassCPtr &cls)
+{
+ const auto &convOps = cls->operatorOverloads(OperatorQueryOption::ConversionOp);
+ for (const auto &func : convOps) {
+ if (func->isModifiedRemoved())
+ continue;
+ const auto metaClass =
+ AbstractMetaClass::findClass(m_metaClasses, func->type().typeEntry());
+ if (!metaClass)
+ continue;
+ metaClass->addExternalConversionOperator(func);
+ }
+ for (const auto &innerClass : cls->innerClasses())
+ setupExternalConversion(innerClass);
+}
+
+static void writeRejectLogFile(const QString &name,
+ const AbstractMetaBuilderPrivate::RejectSet &rejects)
+{
+ static const QHash<AbstractMetaBuilder::RejectReason, QByteArray> descriptions ={
+ {AbstractMetaBuilder::NotInTypeSystem, "Not in type system"_ba},
+ {AbstractMetaBuilder::GenerationDisabled, "Generation disabled by type system"_ba},
+ {AbstractMetaBuilder::RedefinedToNotClass, "Type redefined to not be a class"_ba},
+ {AbstractMetaBuilder::UnmatchedReturnType, "Unmatched return type"_ba},
+ {AbstractMetaBuilder::UnmatchedArgumentType, "Unmatched argument type"_ba},
+ {AbstractMetaBuilder::UnmatchedOperator, "Unmatched operator"_ba},
+ {AbstractMetaBuilder::Deprecated, "Deprecated"_ba}
+ };
+
+ QFile f(name);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qCWarning(lcShiboken, "%s", qPrintable(msgCannotOpenForWriting(f)));
+ return;
+ }
+
+ QTextStream s(&f);
+
+ int lastReason = -1;
+ for (const auto &e : rejects) {
+ if (e.reason != lastReason) {
+ const QByteArray description = descriptions.value(e.reason, "Unknown reason"_ba);
+ const QByteArray underline(description.size(), '*');
+ if (lastReason != -1)
+ s << '\n';
+ s << underline << '\n' << description << '\n' << underline << "\n\n";
+ lastReason = e.reason;
+ }
+
+ s << " - " << e << '\n';
+ }
+}
+
+void AbstractMetaBuilderPrivate::dumpLog() const
+{
+ writeRejectLogFile(m_logDirectory + u"mjb_rejected_classes.log"_s, m_rejectedClasses);
+ writeRejectLogFile(m_logDirectory + u"mjb_rejected_enums.log"_s, m_rejectedEnums);
+ writeRejectLogFile(m_logDirectory + u"mjb_rejected_functions.log"_s, m_rejectedFunctions);
+ writeRejectLogFile(m_logDirectory + u"mjb_rejected_fields.log"_s, m_rejectedFields);
+}
+
+// Topological sorting of classes. Templates for use with
+// AbstractMetaClassList/AbstractMetaClassCList.
+// Add a dependency of the class associated with typeEntry on clazz.
+template <class MetaClass>
+static bool addClassDependency(const QList<std::shared_ptr<MetaClass> > &classList,
+ const TypeEntryCPtr &typeEntry,
+ std::shared_ptr<MetaClass> clazz,
+ Graph<std::shared_ptr<MetaClass> > *graph)
+{
+ if (!typeEntry->isComplex() || typeEntry == clazz->typeEntry())
+ return false;
+ const auto c = AbstractMetaClass::findClass(classList, typeEntry);
+ if (c == nullptr || c->enclosingClass() == clazz)
+ return false;
+ return graph->addEdge(c, clazz);
+}
+
+template <class MetaClass>
+static QList<std::shared_ptr<MetaClass> >
+ topologicalSortHelper(const QList<std::shared_ptr<MetaClass> > &classList,
+ const Dependencies &additionalDependencies)
+{
+ Graph<std::shared_ptr<MetaClass> > graph(classList.cbegin(), classList.cend());
+
+ for (const auto &dep : additionalDependencies) {
+ if (!graph.addEdge(dep.parent, dep.child)) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "AbstractMetaBuilder::classesTopologicalSorted(): Invalid additional dependency: "
+ << dep.child->name() << " -> " << dep.parent->name() << '.';
+ }
+ }
+
+ for (const auto &clazz : classList) {
+ if (auto enclosingC = clazz->enclosingClass()) {
+ const auto enclosing = std::const_pointer_cast<MetaClass>(enclosingC);
+ graph.addEdge(enclosing, clazz);
+ }
+
+ for (const auto &baseClass : clazz->baseClasses())
+ graph.addEdge(std::const_pointer_cast<MetaClass>(baseClass), clazz);
+
+ for (const auto &func : clazz->functions()) {
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ // Check methods with default args: If a class is instantiated by value,
+ // ("QString s = QString()"), add a dependency.
+ if (!arg.originalDefaultValueExpression().isEmpty()
+ && arg.type().isValue()) {
+ addClassDependency(classList, arg.type().typeEntry(),
+ clazz, &graph);
+ }
+ }
+ }
+ // Member fields need to be initialized
+ for (const AbstractMetaField &field : clazz->fields()) {
+ auto typeEntry = field.type().typeEntry();
+ if (typeEntry->isEnum()) // Enum defined in class?
+ typeEntry = typeEntry->parent();
+ if (typeEntry != nullptr)
+ addClassDependency(classList, typeEntry, clazz, &graph);
+ }
+ }
+
+ const auto result = graph.topologicalSort();
+ if (!result.isValid() && graph.nodeCount()) {
+ QTemporaryFile tempFile(QDir::tempPath() + u"/cyclic_depXXXXXX.dot"_s);
+ tempFile.setAutoRemove(false);
+ const bool ok = tempFile.open();
+ if (ok) {
+ graph.dumpDot(tempFile.fileName(),
+ [] (const AbstractMetaClassCPtr &c) { return c->name(); });
+ }
+
+ QString message;
+ QTextStream str(&message);
+ str << "Cyclic dependency of classes found:";
+ for (const auto &c : result.cyclic)
+ str << ' ' << c->name();
+ str << '.';
+ if (ok) {
+ str << " Graph can be found at \""
+ << QDir::toNativeSeparators(tempFile.fileName()) << '"';
+ }
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ }
+
+ return result.result;
+}
+
+AbstractMetaClassList
+ AbstractMetaBuilderPrivate::classesTopologicalSorted(const AbstractMetaClassList &classList,
+ const Dependencies &additionalDependencies)
+{
+ return topologicalSortHelper(classList, additionalDependencies);
+}
+
+AbstractMetaClassCList
+ AbstractMetaBuilderPrivate::classesTopologicalSorted(const AbstractMetaClassCList &classList,
+ const Dependencies &additionalDependencies)
+{
+ return topologicalSortHelper(classList, additionalDependencies);
+}
+
+void AbstractMetaBuilderPrivate::pushScope(const NamespaceModelItem &item)
+{
+ // For purposes of type lookup, join all namespaces of the same name
+ // within the parent item.
+ QList<NamespaceModelItem> candidates;
+ const QString name = item->name();
+ if (!m_scopes.isEmpty()) {
+ for (const auto &n : m_scopes.constLast()->namespaces()) {
+ if (n->name() == name)
+ candidates.append(n);
+ }
+ }
+ if (candidates.size() > 1) {
+ auto joined = std::make_shared<_NamespaceModelItem>(m_scopes.constLast()->model(),
+ name, _CodeModelItem::Kind_Namespace);
+ joined->setScope(item->scope());
+ for (const auto &n : candidates)
+ joined->appendNamespace(*n);
+ m_scopes << joined;
+ } else {
+ m_scopes << item;
+ }
+}
+
+void AbstractMetaBuilder::setGlobalHeaders(const QFileInfoList &globalHeaders)
+{
+ d->m_globalHeaders = globalHeaders;
+}
+
+void AbstractMetaBuilder::setHeaderPaths(const HeaderPaths &hp)
+{
+ for (const auto & h: hp) {
+ if (h.type != HeaderType::Framework && h.type != HeaderType::FrameworkSystem)
+ d->m_headerPaths.append(QFile::decodeName(h.path));
+ }
+}
+
+void AbstractMetaBuilder::setUseGlobalHeader(bool h)
+{
+ AbstractMetaBuilderPrivate::m_useGlobalHeader = h;
+}
+
+void AbstractMetaBuilder::setSkipDeprecated(bool value)
+{
+ d->m_skipDeprecated = value;
+}
+
+void AbstractMetaBuilder::setApiExtractorFlags(ApiExtractorFlags flags)
+{
+ d->m_apiExtractorFlags = flags;
+}
+
+// PYSIDE-975: When receiving an absolute path name from the code model, try
+// to resolve it against the include paths set on shiboken in order to recreate
+// relative paths like #include <foo/bar.h>.
+
+static inline bool isFileSystemSlash(QChar c)
+{
+ return c == u'/' || c == u'\\';
+}
+
+static bool matchHeader(const QString &headerPath, const QString &fileName)
+{
+#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
+ static const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
+#else
+ static const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
+#endif
+ const auto pathSize = headerPath.size();
+ return fileName.size() > pathSize
+ && isFileSystemSlash(fileName.at(pathSize))
+ && fileName.startsWith(headerPath, caseSensitivity);
+}
+
+void AbstractMetaBuilderPrivate::setInclude(const TypeEntryPtr &te, const QString &path) const
+{
+ auto it = m_resolveIncludeHash.find(path);
+ if (it == m_resolveIncludeHash.end()) {
+ QFileInfo info(path);
+ const QString fileName = info.fileName();
+ if (!m_useGlobalHeader
+ && std::any_of(m_globalHeaders.cbegin(), m_globalHeaders.cend(),
+ [fileName] (const QFileInfo &fi) {
+ return fi.fileName() == fileName; })) {
+ return;
+ }
+
+ int bestMatchLength = 0;
+ for (const auto &headerPath : m_headerPaths) {
+ if (headerPath.size() > bestMatchLength && matchHeader(headerPath, path))
+ bestMatchLength = headerPath.size();
+ }
+ const QString include = bestMatchLength > 0
+ ? path.right(path.size() - bestMatchLength - 1) : fileName;
+ it = m_resolveIncludeHash.insert(path, {Include::IncludePath, include});
+ }
+ te->setInclude(it.value());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+template <class Container>
+static void debugFormatSequence(QDebug &d, const char *key, const Container& c,
+ const char *separator = ", ")
+{
+ if (c.isEmpty())
+ return;
+ const auto begin = c.begin();
+ const auto end = c.end();
+ d << "\n " << key << '[' << c.size() << "]=(";
+ for (auto it = begin; it != end; ++it) {
+ if (it != begin)
+ d << separator;
+ d << *it;
+ }
+ d << ')';
+}
+
+void AbstractMetaBuilder::formatDebug(QDebug &debug) const
+{
+ debug << "m_globalHeader=" << d->m_globalHeaders;
+ debugFormatSequence(debug, "globalEnums", d->m_globalEnums, "\n");
+ debugFormatSequence(debug, "globalFunctions", d->m_globalFunctions, "\n");
+ if (const auto scopeCount = d->m_scopes.size()) {
+ debug << "\n scopes[" << scopeCount << "]=(";
+ for (qsizetype i = 0; i < scopeCount; ++i) {
+ if (i)
+ debug << ", ";
+ _CodeModelItem::formatKind(debug, d->m_scopes.at(i)->kind());
+ debug << " \"" << d->m_scopes.at(i)->name() << '"';
+ }
+ debug << ')';
+ }
+ debugFormatSequence(debug, "classes", d->m_metaClasses, "\n");
+ debugFormatSequence(debug, "templates", d->m_templates, "\n");
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaBuilder &ab)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaBuilder(";
+ ab.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h
new file mode 100644
index 000000000..20261ed3c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETABUILDER_H
+#define ABSTRACTMETABUILDER_H
+
+#include "abstractmetalang_typedefs.h"
+#include "apiextractorflags.h"
+#include "header_paths.h"
+#include "typesystem_enums.h"
+#include "typesystem_typedefs.h"
+
+#include "clangparser/compilersupport.h"
+
+#include <QtCore/QFileInfoList>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QIODevice)
+
+class AbstractMetaBuilderPrivate;
+class AbstractMetaClass;
+class AbstractMetaType;
+class AbstractMetaEnumValue;
+class ComplexTypeEntry;
+class TypeInfo;
+class TypeEntry;
+
+class AbstractMetaBuilder
+{
+public:
+ Q_DISABLE_COPY_MOVE(AbstractMetaBuilder)
+
+ enum RejectReason {
+ NotInTypeSystem,
+ GenerationDisabled,
+ RedefinedToNotClass,
+ UnmatchedArgumentType,
+ UnmatchedReturnType,
+ UnmatchedOperator,
+ Deprecated,
+ NoReason
+ };
+
+ AbstractMetaBuilder();
+ virtual ~AbstractMetaBuilder();
+
+ const AbstractMetaClassList &classes() const;
+ AbstractMetaClassList takeClasses();
+ const AbstractMetaClassList &templates() const;
+ AbstractMetaClassList takeTemplates();
+ const AbstractMetaClassList &smartPointers() const;
+ AbstractMetaClassList takeSmartPointers();
+ const AbstractMetaFunctionCList &globalFunctions() const;
+ const AbstractMetaEnumList &globalEnums() const;
+ const QHash<TypeEntryCPtr, AbstractMetaEnum> &typeEntryToEnumsHash() const;
+ const QMultiHash<QString, QString> &typedefTargetToName() const;
+
+ bool build(const QByteArrayList &arguments,
+ ApiExtractorFlags apiExtractorFlags = {},
+ bool addCompilerSupportArguments = true,
+ LanguageLevel level = LanguageLevel::Default,
+ unsigned clangFlags = 0);
+ void setLogDirectory(const QString& logDir);
+
+ /**
+ * AbstractMetaBuilder should know what's the global header being used,
+ * so any class declared under this header wont have the include file
+ * filled.
+ */
+ void setGlobalHeaders(const QFileInfoList& globalHeaders);
+ void setHeaderPaths(const HeaderPaths &h);
+
+ static void setUseGlobalHeader(bool h);
+
+ void setSkipDeprecated(bool value);
+
+ void setApiExtractorFlags(ApiExtractorFlags flags);
+
+ enum TranslateTypeFlag {
+ DontResolveType = 0x1,
+ TemplateArgument = 0x2,
+ NoClassScopeLookup = 0x4
+ };
+ Q_DECLARE_FLAGS(TranslateTypeFlags, TranslateTypeFlag);
+
+ static std::optional<AbstractMetaType>
+ translateType(const TypeInfo &_typei, const AbstractMetaClassPtr &currentClass = {},
+ TranslateTypeFlags flags = {}, QString *errorMessage = nullptr);
+ static std::optional<AbstractMetaType>
+ translateType(const QString &t, const AbstractMetaClassPtr &currentClass = {},
+ TranslateTypeFlags flags = {}, QString *errorMessage = nullptr);
+
+ /// Performs a template specialization of the function.
+ /// \param function Function
+ /// \param templateTypes Instantiation types
+ /// \return Specialized copy of the function
+ static AbstractMetaFunctionPtr
+ inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes);
+
+ static AbstractMetaClassPtr
+ inheritTemplateClass(const ComplexTypeEntryPtr &te,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes);
+
+ /// Performs a template specialization of the member function.
+ /// \param function Member function
+ /// \param templateTypes Instantiation types
+ /// \param templateClass Template class
+ /// \param subclass Specialized class
+ /// \return Specialized copy of the function
+ static AbstractMetaFunctionPtr
+ inheritTemplateMember(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaClassPtr &subclass);
+
+ static QString getSnakeCaseName(const QString &name);
+ // Names under which an item will be registered to Python depending on snakeCase
+ static QStringList definitionNames(const QString &name,
+ TypeSystem::SnakeCase snakeCase);
+
+ static QString resolveScopePrefix(const AbstractMetaClassCPtr &scope,
+ QStringView value);
+
+ static bool dontFixDefaultValue(QStringView expr);
+
+ // For testing purposes
+ QString fixDefaultValue(const QString &expr, const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &) const;
+ QString fixEnumDefault(const AbstractMetaType &type, const QString &expr,
+ const AbstractMetaClassCPtr & = {}) const;
+
+ static void setCodeModelTestMode(bool b);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ friend class AbstractMetaBuilderPrivate;
+ AbstractMetaBuilderPrivate *d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaBuilder::TranslateTypeFlags);
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const AbstractMetaBuilder &ab);
+#endif
+
+#endif // ABSTRACTMETBUILDER_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder_helpers.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder_helpers.cpp
new file mode 100644
index 000000000..68eef737a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder_helpers.cpp
@@ -0,0 +1,202 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetabuilder.h"
+#include "abstractmetabuilder_p.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafield.h"
+#include "abstractmetalang.h"
+#include "enumtypeentry.h"
+#include "flagstypeentry.h"
+
+using namespace Qt::StringLiterals;
+
+using QStringViewList = QList<QStringView>;
+
+// Return a prefix to fully qualify value, eg:
+// resolveScopePrefix("Class::NestedClass::Enum::Value1", "Enum::Value1")
+// -> "Class::NestedClass::")
+static QString resolveScopePrefixHelper(const QStringViewList &scopeList,
+ QStringView value)
+{
+ QString name;
+ for (qsizetype i = scopeList.size() - 1 ; i >= 0; --i) {
+ const QString prefix = scopeList.at(i).toString() + u"::"_s;
+ if (value.startsWith(prefix))
+ name.clear();
+ else
+ name.prepend(prefix);
+ }
+ return name;
+}
+
+QString AbstractMetaBuilder::resolveScopePrefix(const AbstractMetaClassCPtr &scope,
+ QStringView value)
+{
+ if (!scope)
+ return {};
+ const QString &qualifiedCppName = scope->qualifiedCppName();
+ const QStringViewList scopeList =
+ QStringView{qualifiedCppName}.split(u"::"_s, Qt::SkipEmptyParts);
+ return resolveScopePrefixHelper(scopeList, value);
+}
+
+// Return the scope for fully qualifying the enumeration value
+static QString resolveEnumValueScopePrefix(const AbstractMetaEnum &metaEnum,
+ QStringView value)
+{
+ AbstractMetaClassCPtr scope = metaEnum.enclosingClass();
+ if (!scope)
+ return {}; // global enum, value should work as is
+ const QString &qualifiedCppName = scope->qualifiedCppName();
+ const QString &enumName = metaEnum.name();
+ QStringViewList parts =
+ QStringView{qualifiedCppName}.split(u"::"_s, Qt::SkipEmptyParts);
+ // Append the type (as required for enum classes) unless it is an anonymous enum.
+ if (!metaEnum.isAnonymous())
+ parts.append(QStringView{enumName});
+ return resolveScopePrefixHelper(parts, value);
+}
+
+bool AbstractMetaBuilderPrivate::isQualifiedCppIdentifier(QStringView e)
+{
+ return !e.isEmpty() && e.at(0).isLetter()
+ && std::all_of(e.cbegin() + 1, e.cend(),
+ [](QChar c) { return c.isLetterOrNumber() || c == u'_' || c == u':'; });
+}
+
+static bool isIntegerConstant(const QStringView expr)
+{
+ bool isNumber;
+ auto n = expr.toInt(&isNumber, /* guess base: 0x or decimal */ 0);
+ Q_UNUSED(n);
+ return isNumber;
+}
+
+static bool isFloatConstant(const QStringView expr)
+{
+ bool isNumber;
+ auto d = expr.toDouble(&isNumber);
+ Q_UNUSED(d);
+ return isNumber;
+}
+
+// Fix an enum default value: Add the enum/flag scope or fully qualified name
+// to the default value, making it usable from Python wrapper code outside the
+// owner class hierarchy. See TestEnum::testEnumDefaultValues().
+QString AbstractMetaBuilderPrivate::fixEnumDefault(const AbstractMetaType &type,
+ const QString &expr,
+ const AbstractMetaClassCPtr &klass) const
+{
+ // QFlags construct from integers, do not fix that
+ if (isIntegerConstant(expr))
+ return expr;
+
+ const QString field = qualifyStaticField(klass, expr);
+ if (!field.isEmpty())
+ return field;
+
+ const auto typeEntry = type.typeEntry();
+ EnumTypeEntryCPtr enumTypeEntry;
+ FlagsTypeEntryCPtr flagsTypeEntry;
+ if (typeEntry->isFlags()) {
+ flagsTypeEntry = std::static_pointer_cast<const FlagsTypeEntry>(typeEntry);
+ enumTypeEntry = flagsTypeEntry->originator();
+ } else {
+ Q_ASSERT(typeEntry->isEnum());
+ enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(typeEntry);
+ }
+ // Use the enum's qualified name (would otherwise be "QFlags<Enum>")
+ if (!enumTypeEntry->qualifiedCppName().contains(u"::"))
+ return expr; // Global enum, nothing to fix here
+
+ // This is a somehow scoped enum
+ AbstractMetaEnum metaEnum = m_enums.value(enumTypeEntry);
+
+ if (isQualifiedCppIdentifier(expr)) // A single enum value
+ return resolveEnumValueScopePrefix(metaEnum, expr) + expr;
+
+ QString result;
+ // Is this a cast from integer or other type ("Enum(-1)" or "Options(0x10|0x20)"?
+ // Prepend the scope (assuming enum and flags are in the same scope).
+ auto parenPos = expr.indexOf(u'(');
+ const bool typeCast = parenPos != -1 && expr.endsWith(u')')
+ && isQualifiedCppIdentifier(QStringView{expr}.left(parenPos));
+ if (typeCast) {
+ const QString prefix =
+ AbstractMetaBuilder::resolveScopePrefix(metaEnum.enclosingClass(), expr);
+ result += prefix;
+ parenPos += prefix.size();
+ }
+ result += expr;
+
+ // Extract "Option1 | Option2" from "Options(Option1 | Option2)"
+ QStringView innerExpression = typeCast
+ ? QStringView{result}.mid(parenPos + 1, result.size() - parenPos - 2)
+ : QStringView{result};
+
+ // Quick check for number "Options(0x4)"
+ if (isIntegerConstant(innerExpression))
+ return result;
+
+ // Quick check for single enum value "Options(Option1)"
+ if (isQualifiedCppIdentifier(innerExpression)) {
+ const QString prefix = resolveEnumValueScopePrefix(metaEnum, innerExpression);
+ result.insert(parenPos + 1, prefix);
+ return result;
+ }
+
+ // Tokenize simple "A | B" expressions and qualify the enum values therein.
+ // Anything more complicated is left as is ATM.
+ if (!innerExpression.contains(u'|') || innerExpression.contains(u'&')
+ || innerExpression.contains(u'^') || innerExpression.contains(u'(')
+ || innerExpression.contains(u'~')) {
+ return result;
+ }
+
+ const QList<QStringView> tokens = innerExpression.split(u'|', Qt::SkipEmptyParts);
+ QStringList qualifiedTokens;
+ qualifiedTokens.reserve(tokens.size());
+ for (const auto &tokenIn : tokens) {
+ const auto token = tokenIn.trimmed();
+ QString qualified = token.toString();
+ if (!isIntegerConstant(token) && isQualifiedCppIdentifier(token))
+ qualified.prepend(resolveEnumValueScopePrefix(metaEnum, token));
+ qualifiedTokens.append(qualified);
+ }
+ const QString qualifiedExpression = qualifiedTokens.join(u" | "_s);
+ if (!typeCast)
+ return qualifiedExpression;
+
+ result.replace(parenPos + 1, innerExpression.size(), qualifiedExpression);
+ return result;
+}
+
+bool AbstractMetaBuilder::dontFixDefaultValue(QStringView expr)
+{
+ return expr.isEmpty() || expr == u"{}" || expr == u"nullptr"
+ || expr == u"NULL" || expr == u"true" || expr == u"false"
+ || (expr.startsWith(u'{') && expr.startsWith(u'}')) // initializer list
+ || (expr.startsWith(u'[') && expr.startsWith(u']')) // array
+ || expr.startsWith(u"Qt::") // Qt namespace constant
+ || isIntegerConstant(expr) || isFloatConstant(expr);
+}
+
+QString AbstractMetaBuilderPrivate::qualifyStaticField(const AbstractMetaClassCPtr &c,
+ QStringView field)
+{
+ if (!c || c->fields().isEmpty())
+ return {};
+ // If there is a scope, ensure it matches the class
+ const auto lastQualifier = field.lastIndexOf(u"::");
+ if (lastQualifier != -1
+ && !c->qualifiedCppName().endsWith(field.left(lastQualifier))) {
+ return {};
+ }
+ const auto fieldName = lastQualifier != -1
+ ? field.mid(lastQualifier + 2) : field;
+ const auto fieldOpt = c->findField(fieldName);
+ if (!fieldOpt.has_value() || !fieldOpt.value().isStatic())
+ return {};
+ return AbstractMetaBuilder::resolveScopePrefix(c, field) + field.toString();
+}
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h
new file mode 100644
index 000000000..e65a4f176
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h
@@ -0,0 +1,254 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETABUILDER_P_H
+#define ABSTRACTMETABUILDER_P_H
+
+#include "abstractmetabuilder.h"
+#include "dependency.h"
+#include "parser/codemodel_fwd.h"
+#include "abstractmetalang.h"
+#include "abstractmetatype.h"
+#include "include.h"
+#include "typeparser.h"
+#include "modifications_typedefs.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QMultiHash>
+#include <QtCore/QSet>
+
+#include <optional>
+#include <set>
+
+class TypeDatabase;
+
+struct RejectEntry
+{
+ AbstractMetaBuilder::RejectReason reason;
+ QString signature;
+ QString sortkey;
+ QString message;
+};
+
+bool operator<(const RejectEntry &re1, const RejectEntry &re2);
+
+class AbstractMetaBuilderPrivate
+{
+public:
+ struct TypeClassEntry
+ {
+ AbstractMetaType type;
+ AbstractMetaClassCPtr klass;
+ };
+
+ using TranslateTypeFlags = AbstractMetaBuilder::TranslateTypeFlags;
+
+ Q_DISABLE_COPY(AbstractMetaBuilderPrivate)
+
+ AbstractMetaBuilderPrivate();
+
+ static FileModelItem buildDom(QByteArrayList arguments,
+ bool addCompilerSupportArguments,
+ LanguageLevel level,
+ unsigned clangFlags);
+ void traverseDom(const FileModelItem &dom, ApiExtractorFlags flags);
+
+ void dumpLog() const;
+
+ static AbstractMetaClassList
+ classesTopologicalSorted(const AbstractMetaClassList &classList,
+ const Dependencies &additionalDependencies = {});
+ static AbstractMetaClassCList
+ classesTopologicalSorted(const AbstractMetaClassCList &classList,
+ const Dependencies &additionalDependencies = {});
+
+ NamespaceModelItem popScope() { return m_scopes.takeLast(); }
+
+ void pushScope(const NamespaceModelItem &item);
+
+ NamespaceModelItem currentScope() const { return m_scopes.constLast(); }
+
+ AbstractMetaClassPtr argumentToClass(const ArgumentModelItem &,
+ const AbstractMetaClassCPtr &currentClass);
+
+ void addAbstractMetaClass(const AbstractMetaClassPtr &cls, const _CodeModelItem *item);
+ AbstractMetaClassPtr traverseTypeDef(const FileModelItem &dom,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr &currentClass);
+ AbstractMetaClassPtr traverseTypeDefHelper(const FileModelItem &dom,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr &currentClass);
+ void traverseTypesystemTypedefs();
+ AbstractMetaClassPtr traverseClass(const FileModelItem &dom,
+ const ClassModelItem &item,
+ const AbstractMetaClassPtr &currentClass);
+ void traverseScopeMembers(const ScopeModelItem &item,
+ const AbstractMetaClassPtr &metaClass);
+ void traverseClassMembers(const ClassModelItem &scopeItem);
+ void traverseUsingMembers(const AbstractMetaClassPtr &metaClass) const;
+ void traverseNamespaceMembers(const NamespaceModelItem &scopeItem);
+ bool setupInheritance(const AbstractMetaClassPtr &metaClass);
+ AbstractMetaClassPtr traverseNamespace(const FileModelItem &dom,
+ const NamespaceModelItem &item);
+ std::optional<AbstractMetaEnum> traverseEnum(const EnumModelItem &item,
+ const AbstractMetaClassPtr &enclosing,
+ const QSet<QString> &enumsDeclarations);
+ void traverseEnums(const ScopeModelItem &item, const AbstractMetaClassPtr &parent,
+ const QStringList &enumsDeclarations);
+ AbstractMetaFunctionRawPtrList classFunctionList(const ScopeModelItem &scopeItem,
+ AbstractMetaClass::Attributes *constructorAttributes,
+ const AbstractMetaClassPtr &currentClass);
+ void traverseFunctions(const ScopeModelItem& item,
+ const AbstractMetaClassPtr &parent);
+ static void applyFunctionModifications(AbstractMetaFunction *func);
+ void traverseFields(const ScopeModelItem &item, const AbstractMetaClassPtr &parent);
+ bool traverseStreamOperator(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr &currentClass);
+ void traverseOperatorFunction(const FunctionModelItem &item,
+ const AbstractMetaClassPtr &currentClass);
+ AbstractMetaFunction *traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
+ const AbstractMetaClassPtr &metaClass,
+ QString *errorMessage);
+ bool traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc,
+ QString *errorMessage);
+ bool traverseAddedMemberFunction(const AddedFunctionPtr &addedFunc,
+ const AbstractMetaClassPtr &metaClass,
+ QString *errorMessage);
+ void rejectFunction(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr &currentClass,
+ AbstractMetaBuilder::RejectReason reason,
+ const QString &rejectReason);
+ AbstractMetaFunction *traverseFunction(const FunctionModelItem &function,
+ const AbstractMetaClassPtr &currentClass);
+ std::optional<AbstractMetaField> traverseField(const VariableModelItem &field,
+ const AbstractMetaClassCPtr &cls);
+ void checkFunctionModifications() const;
+ void registerHashFunction(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr &currentClass);
+ void registerToStringCapabilityIn(const NamespaceModelItem &namespaceItem);
+ void registerToStringCapability(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr &currentClass);
+
+ /**
+ * A conversion operator function should not have its owner class as
+ * its return type, but unfortunately it does. This function fixes the
+ * return type of operator functions of this kind making the return type
+ * be the same as it is supposed to generate when used in C++.
+ * If the returned type is a wrapped C++ class, this method also adds the
+ * conversion operator to the collection of external conversions of the
+ * said class.
+ * \param metaFunction conversion operator function to be fixed.
+ */
+ static void fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction);
+
+ void parseQ_Properties(const AbstractMetaClassPtr &metaClass,
+ const QStringList &declarations);
+ void setupEquals(const AbstractMetaClassPtr &metaClass);
+ void setupComparable(const AbstractMetaClassPtr &metaClass);
+ void setupExternalConversion(const AbstractMetaClassCPtr &cls);
+
+ static bool isQualifiedCppIdentifier(QStringView e);
+ QString fixDefaultValue(QString expr, const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &) const;
+ QString fixSimpleDefaultValue(QStringView expr,
+ const AbstractMetaClassCPtr &klass) const;
+
+ QString fixEnumDefault(const AbstractMetaType &type, const QString &expr,
+ const AbstractMetaClassCPtr &) const;
+ /// Qualify a static field name for default value expressions
+ static QString qualifyStaticField(const AbstractMetaClassCPtr &c, QStringView field);
+
+ std::optional<AbstractMetaType>
+ translateType(const TypeInfo &type, const AbstractMetaClassCPtr &currentClass,
+ TranslateTypeFlags flags = {}, QString *errorMessage = nullptr);
+ static std::optional<AbstractMetaType>
+ translateTypeStatic(const TypeInfo &type, const AbstractMetaClassCPtr &current,
+ AbstractMetaBuilderPrivate *d = nullptr, TranslateTypeFlags flags = {},
+ QString *errorMessageIn = nullptr);
+ static TypeEntryCList findTypeEntriesHelper(const QString &qualifiedName, const QString &name,
+ TranslateTypeFlags flags = {},
+ const AbstractMetaClassCPtr &currentClass = {},
+ AbstractMetaBuilderPrivate *d = nullptr);
+ static TypeEntryCList findTypeEntries(const QString &qualifiedName, const QString &name,
+ TranslateTypeFlags flags = {},
+ const AbstractMetaClassCPtr &currentClass = {},
+ AbstractMetaBuilderPrivate *d = nullptr,
+ QString *errorMessage = nullptr);
+
+ qint64 findOutValueFromString(const QString &stringValue, bool &ok);
+
+ AbstractMetaClassPtr findTemplateClass(const QString& name, const AbstractMetaClassCPtr &context,
+ TypeInfo *info = nullptr,
+ ComplexTypeEntryPtr *baseContainerType = nullptr) const;
+ AbstractMetaClassCList getBaseClasses(const AbstractMetaClassCPtr &metaClass) const;
+
+ static bool inheritTemplate(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const TypeInfo &info);
+ static bool inheritTemplate(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes);
+
+ static AbstractMetaFunctionPtr
+ inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes);
+
+ static AbstractMetaFunctionPtr
+ inheritTemplateMember(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaClassPtr &subclass);
+
+ static void inheritTemplateFunctions(const AbstractMetaClassPtr &subclass);
+ static std::optional<AbstractMetaType>
+ inheritTemplateType(const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaType &metaType);
+
+ static bool isQObject(const FileModelItem &dom, const QString &qualifiedName);
+ static bool isEnum(const FileModelItem &dom, const QStringList &qualifiedName);
+
+ void sortLists();
+ void setInclude(const TypeEntryPtr &te, const QString &path) const;
+ static void fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods);
+
+ void fillAddedFunctions(const AbstractMetaClassPtr &metaClass);
+ AbstractMetaClassCPtr resolveTypeSystemTypeDef(const AbstractMetaType &t) const;
+
+ void fixSmartPointers();
+
+ AbstractMetaBuilder *q = nullptr;
+ AbstractMetaClassList m_metaClasses;
+ AbstractMetaClassList m_templates;
+ AbstractMetaClassList m_smartPointers;
+ QHash<const _CodeModelItem *, AbstractMetaClassPtr > m_itemToClass;
+ QHash<AbstractMetaClassCPtr, const _CodeModelItem *> m_classToItem;
+ AbstractMetaFunctionCList m_globalFunctions;
+ AbstractMetaEnumList m_globalEnums;
+
+ using RejectSet = std::set<RejectEntry>;
+
+ RejectSet m_rejectedClasses;
+ RejectSet m_rejectedEnums;
+ RejectSet m_rejectedFunctions;
+ RejectSet m_rejectedFields;
+
+ QHash<TypeEntryCPtr, AbstractMetaEnum> m_enums;
+
+ QList<NamespaceModelItem> m_scopes;
+
+ QString m_logDirectory;
+ QFileInfoList m_globalHeaders;
+ QStringList m_headerPaths;
+ mutable QHash<QString, Include> m_resolveIncludeHash;
+ QMultiHash<QString, QString> m_typedefTargetToName;
+ QList<TypeClassEntry> m_typeSystemTypeDefs; // look up metatype->class for type system typedefs
+ ApiExtractorFlags m_apiExtractorFlags;
+ bool m_skipDeprecated = false;
+ static bool m_useGlobalHeader;
+ static bool m_codeModelTestMode;
+};
+
+#endif // ABSTRACTMETBUILDER_P_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp b/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp
new file mode 100644
index 000000000..780170c22
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp
@@ -0,0 +1,423 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetaenum.h"
+#include "abstractmetalang.h"
+#include "documentation.h"
+#include "enumtypeentry.h"
+#include "parser/enumvalue.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+class AbstractMetaEnumValueData : public QSharedData
+{
+public:
+ QString m_name;
+ QString m_stringValue;
+ EnumValue m_value;
+ Documentation m_doc;
+ bool m_deprecated = false;
+};
+
+AbstractMetaEnumValue::AbstractMetaEnumValue() :
+ d(new AbstractMetaEnumValueData)
+{
+}
+
+AbstractMetaEnumValue::AbstractMetaEnumValue(const AbstractMetaEnumValue &) = default;
+AbstractMetaEnumValue &AbstractMetaEnumValue::operator=(const AbstractMetaEnumValue &) = default;
+AbstractMetaEnumValue::AbstractMetaEnumValue(AbstractMetaEnumValue &&) noexcept = default;
+AbstractMetaEnumValue &AbstractMetaEnumValue::operator=(AbstractMetaEnumValue &&) noexcept = default;
+AbstractMetaEnumValue::~AbstractMetaEnumValue() = default;
+
+EnumValue AbstractMetaEnumValue::value() const
+{
+ return d->m_value;
+}
+
+void AbstractMetaEnumValue::setValue(EnumValue value)
+{
+ if (d->m_value != value)
+ d->m_value = value;
+}
+
+QString AbstractMetaEnumValue::stringValue() const
+{
+ return d->m_stringValue;
+}
+
+void AbstractMetaEnumValue::setStringValue(const QString &v)
+{
+ if (d->m_stringValue != v)
+ d->m_stringValue = v;
+}
+
+QString AbstractMetaEnumValue::name() const
+{
+ return d->m_name;
+}
+
+void AbstractMetaEnumValue::setName(const QString &name)
+{
+ if (d->m_name != name)
+ d->m_name = name;
+}
+
+bool AbstractMetaEnumValue::isDeprecated() const
+{
+ return d->m_deprecated;
+}
+
+void AbstractMetaEnumValue::setDeprecated(bool deprecated)
+{
+ if (d->m_deprecated != deprecated)
+ d->m_deprecated = deprecated;
+}
+
+Documentation AbstractMetaEnumValue::documentation() const
+{
+ return d->m_doc;
+}
+
+void AbstractMetaEnumValue::setDocumentation(const Documentation &doc)
+{
+ if (d->m_doc != doc)
+ d->m_doc = doc;
+}
+
+// --------------------- AbstractMetaEnum
+
+class AbstractMetaEnumData : public QSharedData
+{
+public:
+ AbstractMetaEnumData() : m_deprecated(false),
+ m_hasQenumsDeclaration(false), m_signed(true)
+ {
+ }
+
+ int unsignedUsedBits() const;
+ int signedUsedBits() const;
+
+ AbstractMetaEnumValueList m_enumValues;
+
+ EnumTypeEntryCPtr m_typeEntry;
+ Documentation m_doc;
+ QString m_underlyingType;
+
+ EnumKind m_enumKind = CEnum;
+ Access m_access = Access::Public;
+ uint m_deprecated : 1;
+ uint m_hasQenumsDeclaration : 1;
+ uint m_signed : 1;
+};
+
+static int _usedBits(uint64_t v)
+{
+ return (v >> 32) ? 64 : (v >> 16) ? 32 : (v >> 8) ? 16 : 8;
+}
+
+static int _usedBits(int64_t v)
+{
+ return (v >> 31) ? 64 : (v >> 15) ? 32 : (v >> 7) ? 16 : 8;
+}
+
+int AbstractMetaEnumData::unsignedUsedBits() const
+{
+ uint64_t maxValue = 0;
+ for (const auto &v : m_enumValues) {
+ if (const auto uv = v.value().unsignedValue(); uv > maxValue)
+ maxValue = uv;
+ }
+ return _usedBits(maxValue);
+}
+
+int AbstractMetaEnumData::signedUsedBits() const
+{
+ int64_t maxValue = 0;
+ for (const auto &v : m_enumValues) {
+ const auto sv = v.value().value();
+ const auto absV = sv < 0 ? ~sv : sv;
+ if (absV > maxValue)
+ maxValue = absV;
+ }
+ return _usedBits(maxValue);
+}
+
+AbstractMetaEnum::AbstractMetaEnum() : d(new AbstractMetaEnumData)
+{
+}
+
+AbstractMetaEnum::AbstractMetaEnum(const AbstractMetaEnum &) = default;
+AbstractMetaEnum &AbstractMetaEnum::operator=(const AbstractMetaEnum&) = default;
+AbstractMetaEnum::AbstractMetaEnum(AbstractMetaEnum &&) noexcept = default;
+AbstractMetaEnum &AbstractMetaEnum::operator=(AbstractMetaEnum &&) noexcept = default;
+AbstractMetaEnum::~AbstractMetaEnum() = default;
+
+const AbstractMetaEnumValueList &AbstractMetaEnum::values() const
+{
+ return d->m_enumValues;
+}
+
+AbstractMetaEnumValueList AbstractMetaEnum::nonRejectedValues() const
+{
+ auto te = d->m_typeEntry;
+ AbstractMetaEnumValueList result = d->m_enumValues;
+ auto pred = [te](const AbstractMetaEnumValue &v) {
+ return te->isEnumValueRejected(v.name()); };
+ result.erase(std::remove_if(result.begin(), result.end(), pred), result.end());
+ return result;
+}
+
+void AbstractMetaEnum::addEnumValue(const AbstractMetaEnumValue &enumValue)
+{
+ d->m_enumValues << enumValue;
+}
+
+std::optional<AbstractMetaEnumValue>
+ findMatchingEnumValue(const AbstractMetaEnumValueList &list, QStringView value)
+{
+ for (const AbstractMetaEnumValue &enumValue : list) {
+ if (enumValue.name() == value)
+ return enumValue;
+ }
+ return {};
+}
+
+// Find enum values for "enum Enum { e1 }" either for "e1" or "Enum::e1"
+std::optional<AbstractMetaEnumValue>
+ AbstractMetaEnum::findEnumValue(QStringView value) const
+{
+ if (isAnonymous())
+ return findMatchingEnumValue(d->m_enumValues, value);
+ const int sepPos = value.indexOf(u"::");
+ if (sepPos == -1)
+ return findMatchingEnumValue(d->m_enumValues, value);
+ if (name() == value.left(sepPos))
+ return findMatchingEnumValue(d->m_enumValues, value.right(value.size() - sepPos - 2));
+ return {};
+}
+
+QString AbstractMetaEnum::name() const
+{
+ return d->m_typeEntry->targetLangEntryName();
+}
+
+QString AbstractMetaEnum::qualifiedCppName() const
+{
+ return enclosingClass()
+ ? enclosingClass()->qualifiedCppName() + u"::"_s + name()
+ : name();
+}
+
+Access AbstractMetaEnum::access() const
+{
+ return d->m_access;
+}
+
+void AbstractMetaEnum::setAccess(Access a)
+{
+ if (a != d->m_access)
+ d->m_access = a;
+}
+
+bool AbstractMetaEnum::isDeprecated() const
+{
+ return d->m_deprecated;
+}
+
+void AbstractMetaEnum::setDeprecated(bool deprecated)
+{
+ if (d->m_deprecated != deprecated)
+ d->m_deprecated = deprecated;
+}
+
+static bool isDeprecatedValue(const AbstractMetaEnumValue &v)
+{
+ return v.isDeprecated();
+};
+
+bool AbstractMetaEnum::hasDeprecatedValues() const
+{
+ return std::any_of(d->m_enumValues.cbegin(), d->m_enumValues.cend(),
+ isDeprecatedValue);
+}
+
+AbstractMetaEnumValueList AbstractMetaEnum::deprecatedValues() const
+{
+ AbstractMetaEnumValueList result;
+ std::copy_if(d->m_enumValues.cbegin(), d->m_enumValues.cend(),
+ std::back_inserter(result), isDeprecatedValue);
+ return result;
+}
+
+const Documentation &AbstractMetaEnum::documentation() const
+{
+ return d->m_doc;
+}
+
+void AbstractMetaEnum::setDocumentation(const Documentation &doc)
+{
+ if (d->m_doc != doc)
+ d->m_doc = doc;
+}
+
+QString AbstractMetaEnum::qualifier() const
+{
+ return d->m_typeEntry->targetLangQualifier();
+}
+
+QString AbstractMetaEnum::package() const
+{
+ return d->m_typeEntry->targetLangPackage();
+}
+
+QString AbstractMetaEnum::fullName() const
+{
+ return package() + u'.' + qualifier() + u'.' + name();
+}
+
+EnumKind AbstractMetaEnum::enumKind() const
+{
+ return d->m_enumKind;
+}
+
+void AbstractMetaEnum::setEnumKind(EnumKind kind)
+{
+ if (d->m_enumKind != kind)
+ d->m_enumKind = kind;
+}
+
+bool AbstractMetaEnum::isAnonymous() const
+{
+ return d->m_enumKind == AnonymousEnum;
+}
+
+bool AbstractMetaEnum::hasQEnumsDeclaration() const
+{
+ return d->m_hasQenumsDeclaration;
+}
+
+void AbstractMetaEnum::setHasQEnumsDeclaration(bool on)
+{
+ if (d->m_hasQenumsDeclaration != on)
+ d->m_hasQenumsDeclaration = on;
+}
+
+EnumTypeEntryCPtr AbstractMetaEnum::typeEntry() const
+{
+ return d->m_typeEntry;
+}
+
+void AbstractMetaEnum::setTypeEntry(const EnumTypeEntryCPtr &entry)
+{
+ if (d->m_typeEntry != entry)
+ d->m_typeEntry = entry;
+}
+
+bool AbstractMetaEnum::isSigned() const
+{
+ return d->m_signed;
+}
+
+void AbstractMetaEnum::setSigned(bool s)
+{
+ if (d->m_signed != s)
+ d->m_signed = s;
+}
+
+QString AbstractMetaEnum::underlyingType() const
+{
+ return d->m_underlyingType;
+}
+
+void AbstractMetaEnum::setUnderlyingType(const QString &underlyingType)
+{
+ if (d->m_underlyingType != underlyingType)
+ d->m_underlyingType = underlyingType;
+}
+
+int AbstractMetaEnum::usedBits() const
+{
+ return isSigned() ? d->signedUsedBits() : d->unsignedUsedBits();
+}
+
+QString AbstractMetaEnum::intTypeForSize(int usedBits, bool isSigned)
+{
+ QString result = u"int"_s + QString::number(usedBits) + u"_t"_s;
+ return isSigned ? result : u'u' + result;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+static void formatMetaEnumValue(QDebug &d, const AbstractMetaEnumValue &v, bool forceHex = false)
+{
+ d << v.name() << '=';
+ if (forceHex)
+ v.value().formatDebugHex(d);
+ else
+ v.value().formatDebug(d);
+ if (v.isDeprecated())
+ d << " (deprecated)";
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaEnumValue &v)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaEnumValue(";
+ formatMetaEnumValue(d, v);
+ d << ')';
+ return d;
+}
+
+static void formatMetaEnum(QDebug &d, const AbstractMetaEnum &e)
+{
+ d << '"' << e.fullName() << '"';
+ if (e.isDeprecated())
+ d << " (deprecated)";
+ d << " \"" << e.underlyingType() << '"';
+ if (!e.isSigned())
+ d << " (unsigned)";
+ d << " [";
+ const AbstractMetaEnumValueList &values = e.values();
+ const bool hasFlags = e.typeEntry()->flags() != nullptr;
+ for (qsizetype i = 0, count = values.size(); i < count; ++i) {
+ if (i)
+ d << ", ";
+ formatMetaEnumValue(d, values.at(i), hasFlags);
+ }
+ d << ']';
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaEnum *ae)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaEnum(";
+ if (ae)
+ formatMetaEnum(d, *ae);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaEnum &ae)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaEnum(";
+ formatMetaEnum(d, ae);
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetaenum.h b/sources/shiboken6/ApiExtractor/abstractmetaenum.h
new file mode 100644
index 000000000..03d7a3082
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetaenum.h
@@ -0,0 +1,125 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETAENUM_H
+#define ABSTRACTMETAENUM_H
+
+#include "abstractmetalang_typedefs.h"
+#include "enclosingclassmixin.h"
+#include "parser/codemodel_enums.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class AbstractMetaEnumData;
+class AbstractMetaEnumValueData;
+class Documentation;
+class EnumValue;
+class EnumTypeEntry;
+
+class AbstractMetaEnumValue
+{
+public:
+ AbstractMetaEnumValue();
+ AbstractMetaEnumValue(const AbstractMetaEnumValue &);
+ AbstractMetaEnumValue &operator=(const AbstractMetaEnumValue &);
+ AbstractMetaEnumValue(AbstractMetaEnumValue &&) noexcept;
+ AbstractMetaEnumValue &operator=(AbstractMetaEnumValue &&) noexcept;
+ ~AbstractMetaEnumValue();
+
+ EnumValue value() const;
+ void setValue(EnumValue value);
+
+ QString stringValue() const;
+ void setStringValue(const QString &v);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ bool isDeprecated() const;
+ void setDeprecated(bool deprecated);
+
+ Documentation documentation() const;
+ void setDocumentation(const Documentation& doc);
+
+ int usedBits() const;
+
+private:
+ QSharedDataPointer<AbstractMetaEnumValueData> d;
+};
+
+class AbstractMetaEnum : public EnclosingClassMixin
+{
+public:
+ AbstractMetaEnum();
+ AbstractMetaEnum(const AbstractMetaEnum &);
+ AbstractMetaEnum &operator=(const AbstractMetaEnum &);
+ AbstractMetaEnum(AbstractMetaEnum &&) noexcept;
+ AbstractMetaEnum &operator=(AbstractMetaEnum &&) noexcept;
+ ~AbstractMetaEnum();
+
+ const AbstractMetaEnumValueList &values() const;
+ AbstractMetaEnumValueList nonRejectedValues() const;
+ void addEnumValue(const AbstractMetaEnumValue &enumValue);
+
+ std::optional<AbstractMetaEnumValue> findEnumValue(QStringView value) const;
+
+ QString name() const;
+ QString qualifiedCppName() const;
+
+ Access access() const;
+ void setAccess(Access a);
+ bool isPrivate() const { return access() == Access::Private; }
+ bool isProtected() const { return access() == Access::Protected; }
+
+ bool isDeprecated() const;
+ void setDeprecated(bool deprecated);
+ bool hasDeprecatedValues() const;
+ AbstractMetaEnumValueList deprecatedValues() const;
+
+ const Documentation &documentation() const;
+ void setDocumentation(const Documentation& doc);
+
+ QString qualifier() const;
+
+ QString package() const;
+
+ QString fullName() const;
+
+ EnumKind enumKind() const;
+ void setEnumKind(EnumKind kind);
+
+ bool isAnonymous() const;
+
+ // Has the enum been declared inside a Q_ENUMS() macro in its enclosing class?
+ bool hasQEnumsDeclaration() const;
+ void setHasQEnumsDeclaration(bool on);
+
+ EnumTypeEntryCPtr typeEntry() const;
+ void setTypeEntry(const EnumTypeEntryCPtr &entry);
+
+ bool isSigned() const;
+ void setSigned(bool s);
+
+ QString underlyingType() const;
+ void setUnderlyingType(const QString &underlyingType);
+
+ static QString intTypeForSize(int usedBits, bool isSigned);
+ int usedBits() const;
+
+private:
+ QSharedDataPointer<AbstractMetaEnumData> d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const AbstractMetaEnumValue &ae);
+QDebug operator<<(QDebug d, const AbstractMetaEnum *ae);
+QDebug operator<<(QDebug d, const AbstractMetaEnum &ae);
+#endif
+
+#endif // ABSTRACTMETAENUM_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafield.cpp b/sources/shiboken6/ApiExtractor/abstractmetafield.cpp
new file mode 100644
index 000000000..27a76d04d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetafield.cpp
@@ -0,0 +1,254 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetafield.h"
+#include "abstractmetabuilder.h"
+#include "abstractmetalang.h"
+#include "abstractmetatype.h"
+#include "documentation.h"
+#include "modifications.h"
+#include "complextypeentry.h"
+#include "typesystemtypeentry.h"
+#include "parser/codemodel.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+class AbstractMetaFieldData : public QSharedData
+{
+public:
+ QString m_originalName;
+ QString m_name;
+ AbstractMetaType m_type;
+ Documentation m_doc;
+ bool m_setterEnabled = true; // Modifications
+ bool m_getterEnabled = true; // Modifications
+ bool m_static = false;
+ Access m_access = Access::Public;
+};
+
+AbstractMetaField::AbstractMetaField() : d(new AbstractMetaFieldData)
+{
+}
+
+AbstractMetaField::AbstractMetaField(const AbstractMetaField &) = default;
+AbstractMetaField &AbstractMetaField::operator=(const AbstractMetaField &) = default;
+AbstractMetaField::AbstractMetaField(AbstractMetaField &&) noexcept = default;
+AbstractMetaField &AbstractMetaField::operator=(AbstractMetaField &&) noexcept = default;
+AbstractMetaField::~AbstractMetaField() = default;
+
+// returned->setEnclosingClass(nullptr);
+
+std::optional<AbstractMetaField>
+ AbstractMetaField::find(const AbstractMetaFieldList &haystack,
+ QStringView needle)
+{
+ for (const auto &f : haystack) {
+ if (f.name() == needle)
+ return f;
+ }
+ return {};
+}
+
+/*******************************************************************************
+ * Indicates that this field has a modification that removes it
+ */
+bool AbstractMetaField::isModifiedRemoved() const
+{
+ const FieldModificationList &mods = modifications();
+ for (const FieldModification &mod : mods) {
+ if (mod.isRemoved())
+ return true;
+ }
+
+ return false;
+}
+
+bool AbstractMetaField::generateOpaqueContainer() const
+{
+ const FieldModificationList &mods = modifications();
+ for (const FieldModification &mod : mods) {
+ if (mod.isOpaqueContainer())
+ return true;
+ }
+ return false;
+}
+
+const AbstractMetaType &AbstractMetaField::type() const
+{
+ return d->m_type;
+}
+
+void AbstractMetaField::setType(const AbstractMetaType &type)
+{
+ if (d->m_type != type)
+ d->m_type = type;
+}
+
+QString AbstractMetaField::name() const
+{
+ return d->m_name;
+}
+
+void AbstractMetaField::setName(const QString &name)
+{
+ if (d->m_name != name)
+ d->m_name = name;
+}
+
+Access AbstractMetaField::access() const
+{
+ return d->m_access;
+}
+
+void AbstractMetaField::setAccess(Access a)
+{
+ if (a != d->m_access)
+ d->m_access = a;
+}
+
+bool AbstractMetaField::isStatic() const
+{
+ return d->m_static;
+}
+
+void AbstractMetaField::setStatic(bool s)
+{
+ if (s != d->m_static)
+ d->m_static = s;
+}
+
+QString AbstractMetaField::qualifiedCppName() const
+{
+ return enclosingClass()->qualifiedCppName() + u"::"_s
+ + originalName();
+}
+
+QStringList AbstractMetaField::definitionNames() const
+{
+ return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase());
+}
+
+QString AbstractMetaField::originalName() const
+{
+ return d->m_originalName.isEmpty() ? d->m_name : d->m_originalName;
+}
+
+void AbstractMetaField::setOriginalName(const QString &name)
+{
+ if (d->m_originalName != name)
+ d->m_originalName = name;
+}
+
+const Documentation &AbstractMetaField::documentation() const
+{
+ return d->m_doc;
+}
+
+void AbstractMetaField::setDocumentation(const Documentation &doc)
+{
+ if (d->m_doc != doc)
+ d->m_doc = doc;
+}
+
+bool AbstractMetaField::isGetterEnabled() const
+{
+ return d->m_getterEnabled;
+}
+
+void AbstractMetaField::setGetterEnabled(bool e)
+{
+ if (d->m_getterEnabled != e)
+ d->m_getterEnabled = e;
+}
+
+bool AbstractMetaField::isSetterEnabled() const
+{
+ return d->m_setterEnabled;
+}
+
+void AbstractMetaField::setSetterEnabled(bool e)
+{
+ if (e != d->m_setterEnabled)
+ d->m_setterEnabled = e;
+}
+
+bool AbstractMetaField::canGenerateGetter() const
+{
+ return d->m_getterEnabled && !isStatic() && !d->m_type.isArray();
+}
+
+bool AbstractMetaField::canGenerateSetter() const
+{
+ return d->m_setterEnabled && !isStatic()
+ && !d->m_type.isArray()
+ && (!d->m_type.isConstant() || d->m_type.isPointerToConst());
+}
+
+TypeSystem::SnakeCase AbstractMetaField::snakeCase() const
+{
+ // Renamed?
+ if (!d->m_originalName.isEmpty() && d->m_originalName != d->m_name)
+ return TypeSystem::SnakeCase::Disabled;
+
+ for (const auto &mod : modifications()) {
+ if (mod.snakeCase() != TypeSystem::SnakeCase::Unspecified)
+ return mod.snakeCase();
+ }
+
+ auto typeEntry = enclosingClass()->typeEntry();
+ const auto snakeCase = typeEntry->snakeCase();
+ return snakeCase != TypeSystem::SnakeCase::Unspecified
+ ? snakeCase : typeSystemTypeEntry(typeEntry)->snakeCase();
+}
+
+FieldModificationList AbstractMetaField::modifications() const
+{
+ const FieldModificationList &mods = enclosingClass()->typeEntry()->fieldModifications();
+ FieldModificationList returned;
+
+ for (const FieldModification &mod : mods) {
+ if (mod.name() == name())
+ returned += mod;
+ }
+
+ return returned;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void AbstractMetaField::formatDebug(QDebug &d) const
+{
+ if (isStatic())
+ d << "static ";
+ d << access() << ' ' << type().name() << " \"" << name() << '"';
+
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaField *af)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaField(";
+ if (af)
+ af->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaField &af)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaField(";
+ af.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafield.h b/sources/shiboken6/ApiExtractor/abstractmetafield.h
new file mode 100644
index 000000000..0fa858791
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetafield.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETAFIELD_H
+#define ABSTRACTMETAFIELD_H
+
+#include "abstractmetalang_typedefs.h"
+#include "parser/codemodel_enums.h"
+#include "typesystem_enums.h"
+#include "modifications_typedefs.h"
+#include "typesystem_typedefs.h"
+#include "enclosingclassmixin.h"
+
+#include <QtCore/QSharedDataPointer>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class Documentation;
+class AbstractMetaFieldData;
+
+class AbstractMetaField : public EnclosingClassMixin
+{
+public:
+ AbstractMetaField();
+ AbstractMetaField(const AbstractMetaField &);
+ AbstractMetaField &operator=(const AbstractMetaField &);
+ AbstractMetaField(AbstractMetaField &&) noexcept;
+ AbstractMetaField &operator=(AbstractMetaField &&) noexcept;
+ ~AbstractMetaField();
+
+ FieldModificationList modifications() const;
+
+ bool isModifiedRemoved() const;
+ bool generateOpaqueContainer() const;
+
+ const AbstractMetaType &type() const;
+ void setType(const AbstractMetaType &type);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ Access access() const;
+ void setAccess(Access a);
+ bool isPrivate() const { return access() == Access::Private; }
+ bool isProtected() const { return access() == Access::Protected; }
+
+ bool isStatic() const;
+ void setStatic(bool s);
+
+ QString qualifiedCppName() const;
+
+ // Names under which the field will be registered to Python.
+ QStringList definitionNames() const;
+
+ QString originalName() const;
+ void setOriginalName(const QString& name);
+
+ const Documentation &documentation() const;
+ void setDocumentation(const Documentation& doc);
+
+ bool isGetterEnabled() const; // Modifications
+ void setGetterEnabled(bool e);
+ bool isSetterEnabled() const; // Modifications
+ void setSetterEnabled(bool e);
+
+ bool canGenerateGetter() const;
+ bool canGenerateSetter() const;
+
+ TypeSystem::SnakeCase snakeCase() const;
+
+ static std::optional<AbstractMetaField>
+ find(const AbstractMetaFieldList &haystack, QStringView needle);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+private:
+ QSharedDataPointer<AbstractMetaFieldData> d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const AbstractMetaField *af);
+QDebug operator<<(QDebug d, const AbstractMetaField &af);
+#endif
+
+#endif // ABSTRACTMETAFIELD_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
new file mode 100644
index 000000000..11a02f154
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
@@ -0,0 +1,1707 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetafunction.h"
+#include "abstractmetaargument.h"
+#include "abstractmetabuilder.h"
+#include "abstractmetalang.h"
+#include "abstractmetalang_helpers.h"
+#include "abstractmetatype.h"
+#include "addedfunction.h"
+#include <codemodel.h>
+#include "documentation.h"
+#include "exception.h"
+#include "messages.h"
+#include "codesnip.h"
+#include "modifications.h"
+#include "reporthandler.h"
+#include "sourcelocation.h"
+#include "typedatabase.h"
+#include "complextypeentry.h"
+#include "containertypeentry.h"
+#include "functiontypeentry.h"
+#include "primitivetypeentry.h"
+#include "typesystemtypeentry.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QRegularExpression>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+// Cache FunctionModificationList in a flat list per class (0 for global
+// functions, or typically owner/implementing/declaring class.
+struct ModificationCacheEntry
+{
+ AbstractMetaClassCPtr klass;
+ FunctionModificationList modifications;
+};
+
+using ModificationCache = QList<ModificationCacheEntry>;
+
+class AbstractMetaFunctionPrivate
+{
+public:
+ AbstractMetaFunctionPrivate()
+ : m_constant(false),
+ m_reverse(false),
+ m_pointerOperator(false),
+ m_isCallOperator(false)
+ {
+ }
+
+ QString signature() const;
+ QString formatMinimalSignature(const AbstractMetaFunction *q,
+ bool comment) const;
+ QString modifiedName(const AbstractMetaFunction *q) const;
+ int overloadNumber(const AbstractMetaFunction *q) const;
+
+ const FunctionModificationList &modifications(const AbstractMetaFunction *q,
+ const AbstractMetaClassCPtr &implementor) const;
+
+ bool applyTypeModification(const AbstractMetaFunction *q,
+ const QString &type, int number, QString *errorMessage);
+
+ QString m_name;
+ QString m_originalName;
+ Documentation m_doc;
+ mutable QString m_cachedMinimalSignature;
+ mutable QString m_cachedSignature;
+ mutable QString m_cachedModifiedName;
+ QString m_unresolvedSignature;
+
+ FunctionTypeEntryPtr m_typeEntry;
+ AbstractMetaFunction::FunctionType m_functionType = AbstractMetaFunction::NormalFunction;
+ AbstractMetaType m_type;
+ QString m_modifiedTypeName;
+ AbstractMetaClassCPtr m_class;
+ AbstractMetaClassCPtr m_implementingClass;
+ AbstractMetaClassCPtr m_declaringClass;
+ mutable ModificationCache m_modificationCache;
+ int m_propertySpecIndex = -1;
+ AbstractMetaArgumentList m_arguments;
+ AddedFunctionPtr m_addedFunction;
+ SourceLocation m_sourceLocation;
+ AbstractMetaFunction::Attributes m_attributes;
+ FunctionAttributes m_cppAttributes;
+ AbstractMetaFunction::Flags m_flags;
+ uint m_constant : 1;
+ uint m_reverse : 1;
+ uint m_pointerOperator : 1;
+ uint m_isCallOperator : 1;
+ mutable int m_cachedOverloadNumber = TypeSystem::OverloadNumberUnset;
+ Access m_access = Access::Public;
+ Access m_originalAccess = Access::Public;
+ ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown;
+ TypeSystem::AllowThread m_allowThreadModification = TypeSystem::AllowThread::Unspecified;
+ TypeSystem::ExceptionHandling m_exceptionHandlingModification = TypeSystem::ExceptionHandling::Unspecified;
+};
+
+AbstractMetaFunction::AbstractMetaFunction(const QString &name) :
+ AbstractMetaFunction()
+{
+ d->m_originalName = d->m_name = name;
+}
+
+AbstractMetaFunction::AbstractMetaFunction(const AddedFunctionPtr &addedFunc) :
+ AbstractMetaFunction(addedFunc->name())
+{
+ d->m_addedFunction = addedFunc;
+ setConstant(addedFunc->isConstant());
+ switch (addedFunc->access()) {
+ case AddedFunction::Protected:
+ setAccess(Access::Protected);
+ break;
+ case AddedFunction::Public:
+ setAccess(Access::Public);
+ break;
+ }
+ AbstractMetaFunction::Attributes atts;
+ if (addedFunc->isStatic())
+ setCppAttribute(FunctionAttribute::Static);
+ if (addedFunc->isClassMethod())
+ atts |= AbstractMetaFunction::ClassMethod;
+ setAttributes(atts);
+}
+
+QString AbstractMetaFunction::name() const
+{
+ return d->m_name;
+}
+
+void AbstractMetaFunction::setName(const QString &name)
+{
+ d->m_name = name;
+}
+
+QString AbstractMetaFunction::originalName() const
+{
+ return d->m_originalName.isEmpty() ? name() : d->m_originalName;
+}
+
+void AbstractMetaFunction::setOriginalName(const QString &name)
+{
+ d->m_originalName = name;
+}
+
+Access AbstractMetaFunction::access() const
+{
+ return d->m_access;
+}
+
+void AbstractMetaFunction::setAccess(Access a)
+{
+ d->m_originalAccess = d->m_access = a;
+}
+
+void AbstractMetaFunction::modifyAccess(Access a)
+{
+ d->m_access = a;
+}
+
+bool AbstractMetaFunction::wasPrivate() const
+{
+ return d->m_originalAccess == Access::Private;
+}
+
+bool AbstractMetaFunction::wasProtected() const
+{
+ return d->m_originalAccess == Access::Protected;
+}
+
+bool AbstractMetaFunction::wasPublic() const
+{
+ return d->m_originalAccess == Access::Public;
+}
+
+QStringList AbstractMetaFunction::definitionNames() const
+{
+ return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase());
+}
+
+const Documentation &AbstractMetaFunction::documentation() const
+{
+ return d->m_doc;
+}
+
+void AbstractMetaFunction::setDocumentation(const Documentation &doc)
+{
+ d->m_doc = doc;
+}
+
+bool AbstractMetaFunction::isReverseOperator() const
+{
+ return d->m_reverse;
+}
+
+void AbstractMetaFunction::setReverseOperator(bool reverse)
+{
+ d->m_reverse = reverse;
+}
+
+bool AbstractMetaFunction::isPointerOperator() const
+{
+ return d->m_pointerOperator;
+}
+
+void AbstractMetaFunction::setPointerOperator(bool value)
+{
+ d->m_pointerOperator = value;
+}
+
+bool AbstractMetaFunction::isExplicit() const
+{
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Explicit);
+}
+
+void AbstractMetaFunction::setExplicit(bool isExplicit)
+{
+ d->m_cppAttributes.setFlag(FunctionAttribute::Explicit, isExplicit);
+}
+
+bool AbstractMetaFunction::returnsBool() const
+{
+ if (d->m_type.typeUsagePattern() != AbstractMetaType::PrimitivePattern)
+ return false;
+ return basicReferencedTypeEntry(d->m_type.typeEntry())->name() == u"bool";
+}
+
+bool AbstractMetaFunction::isOperatorBool() const
+{
+ return d->m_functionType == AbstractMetaFunction::ConversionOperator
+ && d->m_constant && returnsBool();
+}
+
+AbstractMetaFunction::AbstractMetaFunction() : d(new AbstractMetaFunctionPrivate)
+{
+}
+
+AbstractMetaFunction::~AbstractMetaFunction() = default;
+
+AbstractMetaFunction::Attributes AbstractMetaFunction::attributes() const
+{
+ return d->m_attributes;
+}
+
+void AbstractMetaFunction::setAttributes(Attributes attributes)
+{
+ d->m_attributes = attributes;
+}
+
+void AbstractMetaFunction::operator+=(AbstractMetaFunction::Attribute attribute)
+{
+ d->m_attributes.setFlag(attribute);
+}
+
+void AbstractMetaFunction::operator-=(AbstractMetaFunction::Attribute attribute)
+{
+ d->m_attributes.setFlag(attribute, false);
+}
+
+FunctionAttributes AbstractMetaFunction::cppAttributes() const
+{
+ return d->m_cppAttributes;
+}
+
+void AbstractMetaFunction::setCppAttributes(FunctionAttributes a)
+{
+ d->m_cppAttributes = a;
+}
+
+void AbstractMetaFunction::setCppAttribute(FunctionAttribute a, bool on)
+{
+ d->m_cppAttributes.setFlag(a, on);
+}
+
+AbstractMetaFunction::Flags AbstractMetaFunction::flags() const
+{
+ return d->m_flags;
+}
+
+void AbstractMetaFunction::setFlags(Flags f)
+{
+ d->m_flags = f;
+}
+
+/*******************************************************************************
+ * Indicates that this function has a modification that removes it
+ */
+bool AbstractMetaFunction::isModifiedRemoved(AbstractMetaClassCPtr cls) const
+{
+ if (!isInGlobalScope() && !cls)
+ cls = d->m_implementingClass;
+ for (const auto &mod : modifications(cls)) {
+ if (mod.isRemoved())
+ return true;
+ }
+
+ return false;
+}
+
+bool AbstractMetaFunction::isModifiedFinal(AbstractMetaClassCPtr cls) const
+{
+ if (!isInGlobalScope() && cls == nullptr)
+ cls = d->m_implementingClass;
+ for (const auto &mod : modifications(cls)) {
+ if (mod.modifiers().testFlag(FunctionModification::Final))
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaFunction::isVoid() const
+{
+ return d->m_type.isVoid();
+}
+
+const AbstractMetaType &AbstractMetaFunction::type() const
+{
+ return d->m_type;
+}
+
+void AbstractMetaFunction::setType(const AbstractMetaType &type)
+{
+ d->m_type = type;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::ownerClass() const
+{
+ return d->m_class;
+}
+
+void AbstractMetaFunction::setOwnerClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_class = cls;
+}
+
+bool AbstractMetaFunction::operator<(const AbstractMetaFunction &other) const
+{
+ return compareTo(&other) & NameLessThan;
+}
+
+
+/*!
+ Returns a mask of CompareResult describing how this function is
+ compares to another function
+*/
+AbstractMetaFunction::CompareResult AbstractMetaFunction::compareTo(const AbstractMetaFunction *other) const
+{
+ CompareResult result;
+
+ // Enclosing class...
+ if (ownerClass() == other->ownerClass())
+ result |= EqualImplementor;
+
+ // Attributes
+ if (attributes() == other->attributes() && cppAttributes() == other->cppAttributes())
+ result |= EqualAttributes;
+
+ // Compare types
+ if (type().name() == other->type().name())
+ result |= EqualReturnType;
+
+ // Compare names
+ int cmp = originalName().compare(other->originalName());
+
+ if (cmp < 0)
+ result |= NameLessThan;
+ else if (!cmp)
+ result |= EqualName;
+
+ // compare name after modification...
+ cmp = modifiedName().compare(other->modifiedName());
+ if (!cmp)
+ result |= EqualModifiedName;
+
+ // Compare arguments...
+ AbstractMetaArgumentList minArguments;
+ AbstractMetaArgumentList maxArguments;
+ if (arguments().size() < other->arguments().size()) {
+ minArguments = arguments();
+ maxArguments = other->arguments();
+ } else {
+ minArguments = other->arguments();
+ maxArguments = arguments();
+ }
+
+ const auto minCount = minArguments.size();
+ const auto maxCount = maxArguments.size();
+ bool same = true;
+ for (qsizetype i = 0; i < maxCount; ++i) {
+ if (i < minCount) {
+ const AbstractMetaArgument &min_arg = minArguments.at(i);
+ const AbstractMetaArgument &max_arg = maxArguments.at(i);
+ if (min_arg.type().name() != max_arg.type().name()
+ && (min_arg.defaultValueExpression().isEmpty() || max_arg.defaultValueExpression().isEmpty())) {
+ same = false;
+ break;
+ }
+ } else {
+ if (maxArguments.at(i).defaultValueExpression().isEmpty()) {
+ same = false;
+ break;
+ }
+ }
+ }
+
+ if (same)
+ result |= minCount == maxCount ? EqualArguments : EqualDefaultValueOverload;
+
+ return result;
+}
+
+// Is this the const overload of another function of equivalent return type?
+bool AbstractMetaFunction::isConstOverloadOf(const AbstractMetaFunction *other) const
+{
+ const auto argumentCount = d->m_arguments.size();
+ if (!isConstant() || other->isConstant() || name() != other->name()
+ || argumentCount != other->arguments().size()) {
+ return false;
+ }
+
+ // Match "const Foo &getFoo() const" / "Foo &getFoo()" / "Foo getFoo() const"
+ const auto otherType = other->type();
+ if (d->m_type.name() != otherType.name()
+ || d->m_type.indirectionsV() != otherType.indirectionsV()) {
+ return false;
+ }
+
+ const auto &otherArguments = other->arguments();
+ for (qsizetype a = 0; a < argumentCount; ++a) {
+ if (d->m_arguments.at(a).type() != otherArguments.at(a).type())
+ return false;
+ }
+ return true;
+}
+
+AbstractMetaFunction *AbstractMetaFunction::copy() const
+{
+ auto *cpy = new AbstractMetaFunction;
+ cpy->setAttributes(attributes());
+ auto ca = cppAttributes();
+ // Historical bug: explicit was not copied! (causing nontypetemplate_test.py fail)
+ ca.setFlag(FunctionAttribute::Explicit, false);
+ cpy->setCppAttributes(ca);
+ cpy->setFlags(flags());
+ cpy->setAccess(access());
+ cpy->setName(name());
+ cpy->setOriginalName(originalName());
+ cpy->setOwnerClass(ownerClass());
+ cpy->setImplementingClass(implementingClass());
+ cpy->setFunctionType(functionType());
+ cpy->setDeclaringClass(declaringClass());
+ cpy->setType(type());
+ cpy->setConstant(isConstant());
+ cpy->setExceptionSpecification(d->m_exceptionSpecification);
+ cpy->setAllowThreadModification(d->m_allowThreadModification);
+ cpy->setExceptionHandlingModification(d->m_exceptionHandlingModification);
+ cpy->d->m_modifiedTypeName = d->m_modifiedTypeName;
+ cpy->d->m_addedFunction = d->m_addedFunction;
+ cpy->d->m_arguments = d->m_arguments;
+
+ return cpy;
+}
+
+bool AbstractMetaFunction::usesRValueReferences() const
+{
+ if (d->m_functionType == MoveConstructorFunction || d->m_functionType == MoveAssignmentOperatorFunction)
+ return true;
+ if (d->m_type.referenceType() == RValueReference)
+ return true;
+ for (const AbstractMetaArgument &a : d->m_arguments) {
+ if (a.type().referenceType() == RValueReference)
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaFunction::generateBinding() const
+{
+ switch (d->m_functionType) {
+ case ConversionOperator:
+ if (d->m_name != u"operator int" && d->m_name != u"operator double")
+ return false;
+ break;
+ case AssignmentOperatorFunction:
+ case MoveAssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ return false;
+ default:
+ if (!isWhiteListed())
+ return false;
+ break;
+ }
+ // Can we access the wrapper in case of a protected method? If not,
+ // disable for consistency regardless of avoidProtectedHack.
+ if (isProtected()) {
+ const auto typeFlags = ownerClass()->typeEntry()->typeFlags();
+ if (typeFlags.testFlag(ComplexTypeEntry::DisableWrapper))
+ return false;
+ }
+ if (isPrivate() && d->m_functionType != EmptyFunction)
+ return false;
+ // RValue references only for user-specified
+ // functions (<add-function>/<declare-function>/<function>)
+ return d->m_name != u"qt_metacall" &&
+ (!usesRValueReferences() || d->m_addedFunction || d->m_typeEntry)
+ && !isModifiedRemoved();
+}
+
+bool AbstractMetaFunction::isWhiteListed() const
+{
+ switch (d->m_functionType) {
+ case NormalFunction:
+ case SignalFunction:
+ case SlotFunction:
+ if (auto dc = declaringClass()) {
+ const QSet<QString> &whiteList = dc->typeEntry()->generateFunctions();
+ return whiteList.isEmpty() || whiteList.contains(d->m_name)
+ || whiteList.contains(minimalSignature());
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+QString AbstractMetaFunctionPrivate::signature() const
+{
+ if (m_cachedSignature.isEmpty()) {
+ m_cachedSignature = m_originalName;
+
+ m_cachedSignature += u'(';
+
+ for (qsizetype i = 0; i < m_arguments.size(); ++i) {
+ const AbstractMetaArgument &a = m_arguments.at(i);
+ const AbstractMetaType &t = a.type();
+ if (i > 0)
+ m_cachedSignature += u", "_s;
+ m_cachedSignature += t.cppSignature();
+ // We need to have the argument names in the qdoc files
+ m_cachedSignature += u' ';
+ m_cachedSignature += a.name();
+ }
+ m_cachedSignature += u')';
+
+ if (m_constant)
+ m_cachedSignature += u" const"_s;
+ }
+ return m_cachedSignature;
+}
+
+QString AbstractMetaFunction::signature() const
+{
+ return d->signature();
+}
+
+QString AbstractMetaFunction::classQualifiedSignature() const
+{
+ QString result;
+ if (d->m_implementingClass)
+ result += d->m_implementingClass->qualifiedCppName() + u"::"_s;
+ result += signature();
+ return result;
+}
+
+QString AbstractMetaFunction::unresolvedSignature() const
+{
+ return d->m_unresolvedSignature;
+}
+
+void AbstractMetaFunction::setUnresolvedSignature(const QString &s)
+{
+ d->m_unresolvedSignature = s;
+}
+
+bool AbstractMetaFunction::isConstant() const
+{
+ return d->m_constant;
+}
+
+void AbstractMetaFunction::setConstant(bool constant)
+{
+ d->m_constant = constant;
+}
+
+bool AbstractMetaFunction::isUserAdded() const
+{
+ return d->m_addedFunction && !d->m_addedFunction->isDeclaration();
+}
+
+bool AbstractMetaFunction::isUserAddedPythonOverride() const
+{
+ return d->m_addedFunction && d->m_addedFunction->isPythonOverride();
+}
+
+bool AbstractMetaFunction::isUserDeclared() const
+{
+ return d->m_addedFunction && d->m_addedFunction->isDeclaration();
+}
+
+int AbstractMetaFunction::actualMinimumArgumentCount() const
+{
+ int count = 0;
+ for (qsizetype i = 0, size = d->m_arguments.size(); i < size; ++i && ++count) {
+ const auto &arg = d->m_arguments.at(i);
+ if (arg.isModifiedRemoved())
+ --count;
+ else if (!arg.defaultValueExpression().isEmpty())
+ break;
+ }
+
+ return count;
+}
+
+int AbstractMetaFunction::actualArgumentIndex(int index) const
+{
+ if (index < 0 || index >= int(d->m_arguments.size()))
+ throw Exception(msgArgumentIndexOutOfRange(this, index));
+ int result = 0;
+ for (int i = 0; i < index; ++i) {
+ if (!d->m_arguments.at(i).isModifiedRemoved())
+ ++result;
+ }
+ return result;
+}
+
+// Returns reference counts for argument at idx, or all arguments if idx == -2
+QList<ReferenceCount> AbstractMetaFunction::referenceCounts(const AbstractMetaClassCPtr &cls, int idx) const
+{
+ QList<ReferenceCount> returned;
+
+ for (const auto &mod : modifications(cls)) {
+ for (const ArgumentModification &argumentMod : mod.argument_mods()) {
+ if (argumentMod.index() != idx && idx != -2)
+ continue;
+ returned += argumentMod.referenceCounts();
+ }
+ }
+
+ return returned;
+}
+
+ArgumentOwner AbstractMetaFunction::argumentOwner(const AbstractMetaClassCPtr &cls, int idx) const
+{
+ for (const auto &mod : modifications(cls)) {
+ for (const ArgumentModification &argumentMod : mod.argument_mods()) {
+ if (argumentMod.index() != idx)
+ continue;
+ return argumentMod.owner();
+ }
+ }
+ return ArgumentOwner();
+}
+
+QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() != key)
+ continue;
+
+ for (const CodeSnip &snip : argumentModification.conversionRules()) {
+ if (snip.language == language && !snip.code().isEmpty())
+ return snip.code();
+ }
+ }
+ }
+
+ return QString();
+}
+
+bool AbstractMetaFunction::hasConversionRule(TypeSystem::Language language, int idx) const
+{
+ return !conversionRule(language, idx).isEmpty();
+}
+
+// FIXME If we remove a arg. in the method at the base class, it will not reflect here.
+bool AbstractMetaFunction::argumentRemoved(int key) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == key) {
+ if (argumentModification.isRemoved())
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::targetLangOwner() const
+{
+ return d->m_class && d->m_class->isInvisibleNamespace()
+ ? d->m_class->targetLangEnclosingClass() : d->m_class;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::declaringClass() const
+{
+ return d->m_declaringClass;
+}
+
+void AbstractMetaFunction::setDeclaringClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_declaringClass = cls;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::implementingClass() const
+{
+ return d->m_implementingClass;
+}
+
+void AbstractMetaFunction::setImplementingClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_implementingClass = cls;
+}
+
+const AbstractMetaArgumentList &AbstractMetaFunction::arguments() const
+{
+ return d->m_arguments;
+}
+
+AbstractMetaArgumentList &AbstractMetaFunction::arguments()
+{
+ return d->m_arguments;
+}
+
+void AbstractMetaFunction::setArguments(const AbstractMetaArgumentList &arguments)
+{
+ d->m_arguments = arguments;
+}
+
+void AbstractMetaFunction::addArgument(const AbstractMetaArgument &argument)
+{
+ d->m_arguments << argument;
+}
+
+static bool modifiedDeprecated(const FunctionModification &mod)
+{
+ return mod.modifiers().testFlag(FunctionModification::Deprecated);
+}
+
+static bool modifiedUndeprecated(const FunctionModification &mod)
+{
+ return mod.modifiers().testFlag(FunctionModification::Undeprecated);
+}
+
+bool AbstractMetaFunction::isDeprecated() const
+{
+ const auto &mods = modifications(declaringClass());
+
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Deprecated)
+ ? std::none_of(mods.cbegin(), mods.cend(), modifiedUndeprecated)
+ : std::any_of(mods.cbegin(), mods.cend(), modifiedDeprecated);
+}
+
+bool AbstractMetaFunction::isConstructor() const
+{
+ return d->m_functionType == ConstructorFunction || d->m_functionType == CopyConstructorFunction
+ || d->m_functionType == MoveConstructorFunction;
+}
+
+bool AbstractMetaFunction::isDefaultConstructor() const
+{
+ return d->m_functionType == ConstructorFunction
+ && (d->m_arguments.isEmpty()
+ || d->m_arguments.constFirst().hasDefaultValueExpression());
+}
+
+bool AbstractMetaFunction::needsReturnType() const
+{
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::ConstructorFunction:
+ case AbstractMetaFunction::CopyConstructorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ case AbstractMetaFunction::DestructorFunction:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool AbstractMetaFunction::isInGlobalScope() const
+{
+ return d->m_class == nullptr;
+}
+
+AbstractMetaFunction::FunctionType AbstractMetaFunction::functionType() const
+{
+ return d->m_functionType;
+}
+
+void AbstractMetaFunction::setFunctionType(AbstractMetaFunction::FunctionType type)
+{
+ d->m_functionType = type;
+}
+
+std::optional<AbstractMetaFunction::ComparisonOperatorType>
+AbstractMetaFunction::comparisonOperatorType() const
+{
+ if (d->m_functionType != ComparisonOperator)
+ return {};
+ static const QHash<QString, ComparisonOperatorType> mapping = {
+ {u"operator=="_s, OperatorEqual},
+ {u"operator!="_s, OperatorNotEqual},
+ {u"operator<"_s, OperatorLess},
+ {u"operator<="_s, OperatorLessEqual},
+ {u"operator>"_s, OperatorGreater},
+ {u"operator>="_s, OperatorGreaterEqual}
+ };
+ const auto it = mapping.constFind(originalName());
+ Q_ASSERT(it != mapping.constEnd());
+ return it.value();
+}
+
+// Auto-detect whether a function should be wrapped into
+// Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, that is, temporarily release
+// the GIL (global interpreter lock). Doing so is required for any thread-wait
+// functions, anything that might call a virtual function (potentially
+// reimplemented in Python), and recommended for lengthy I/O or similar.
+// It has performance costs, though.
+bool AbstractMetaFunction::autoDetectAllowThread() const
+{
+ // Disallow for simple getter functions.
+ return !maybeAccessor();
+}
+
+bool AbstractMetaFunction::maybeAccessor() const
+{
+ return d->m_functionType == NormalFunction && d->m_class != nullptr
+ && d->m_constant != 0 && !isVoid() && d->m_arguments.isEmpty();
+}
+
+SourceLocation AbstractMetaFunction::sourceLocation() const
+{
+ return d->m_sourceLocation;
+}
+
+void AbstractMetaFunction::setSourceLocation(const SourceLocation &sourceLocation)
+{
+ d->m_sourceLocation = sourceLocation;
+}
+
+static inline TypeSystem::AllowThread allowThreadMod(const AbstractMetaClassCPtr &klass)
+{
+ return klass->typeEntry()->allowThread();
+}
+
+static inline bool hasAllowThreadMod(const AbstractMetaClassCPtr &klass)
+{
+ return allowThreadMod(klass) != TypeSystem::AllowThread::Unspecified;
+}
+
+bool AbstractMetaFunction::allowThread() const
+{
+ auto allowThreadModification = d->m_allowThreadModification;
+ // If there is no modification on the function, check for a base class.
+ if (d->m_class && allowThreadModification == TypeSystem::AllowThread::Unspecified) {
+ if (auto base = recurseClassHierarchy(d->m_class, hasAllowThreadMod))
+ allowThreadModification = allowThreadMod(base);
+ }
+
+ bool result = true;
+ switch (allowThreadModification) {
+ case TypeSystem::AllowThread::Disallow:
+ result = false;
+ break;
+ case TypeSystem::AllowThread::Allow:
+ break;
+ case TypeSystem::AllowThread::Auto:
+ result = autoDetectAllowThread();
+ break;
+ case TypeSystem::AllowThread::Unspecified:
+ result = false;
+ break;
+ }
+ if (!result && ReportHandler::isDebug(ReportHandler::MediumDebug))
+ qCInfo(lcShiboken).noquote() << msgDisallowThread(this);
+ return result;
+}
+
+TypeSystem::Ownership AbstractMetaFunction::argumentTargetOwnership(const AbstractMetaClassCPtr &cls, int idx) const
+{
+ for (const auto &modification : modifications(cls)) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == idx)
+ return argumentModification.targetOwnerShip();
+ }
+ }
+
+ return TypeSystem::UnspecifiedOwnership;
+}
+
+const QString &AbstractMetaFunction::modifiedTypeName() const
+{
+ return d->m_modifiedTypeName;
+}
+
+bool AbstractMetaFunction::generateOpaqueContainerReturn() const
+{
+ if (!isTypeModified() || d->m_type.typeUsagePattern() != AbstractMetaType::ContainerPattern)
+ return false;
+ // Needs to be a reference to a container, allow by value only for spans
+ if (d->m_type.referenceType() != LValueReference) {
+ auto cte = std::static_pointer_cast<const ContainerTypeEntry>(d->m_type.typeEntry());
+ if (cte->containerKind() != ContainerTypeEntry::SpanContainer)
+ return false;
+ }
+ return d->m_type.generateOpaqueContainerForGetter(d->m_modifiedTypeName);
+}
+
+bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == argumentIndex && argumentModification.isArray())
+ return true;
+ }
+ }
+ return false;
+}
+
+// Note: The declaring class must be correctly set for this to work.
+bool AbstractMetaFunctionPrivate::applyTypeModification(const AbstractMetaFunction *q,
+ const QString &type,
+ int number, QString *errorMessage)
+{
+ if (number < 0 || number > m_arguments.size()) {
+ *errorMessage =
+ msgTypeModificationFailed(type, number, q,
+ msgArgumentOutOfRange(number, 0, m_arguments.size()));
+ return false;
+ }
+
+ // Modified return types may have unparseable types like Python tuples
+ if (number == 0) {
+ m_modifiedTypeName = type;
+ return true;
+ }
+
+ auto typeOpt = AbstractMetaType::fromString(type, errorMessage);
+ if (!typeOpt.has_value()) {
+ *errorMessage = msgTypeModificationFailed(type, number, q, *errorMessage);
+ return false;
+ }
+ m_arguments[number - 1].setModifiedType(typeOpt.value());
+ return true;
+}
+
+void AbstractMetaFunction::applyTypeModifications()
+{
+ QString errorMessage;
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &am : modification.argument_mods()) {
+ const int n = am.index();
+ if (am.isTypeModified()
+ && !d->applyTypeModification(this, am.modifiedType(),
+ n, &errorMessage)) {
+ throw Exception(errorMessage);
+ } else if (am.isRemoved() && n != 0) {
+ if (n < 1 || n > d->m_arguments.size()) {
+ errorMessage =
+ msgArgumentRemovalFailed(this, n,
+ msgArgumentOutOfRange(n, 1, d->m_arguments.size()));
+ throw Exception(errorMessage);
+ }
+ d->m_arguments[n - 1].setModifiedRemoved(true);
+ }
+ }
+ }
+}
+
+QString AbstractMetaFunction::pyiTypeReplaced(int argumentIndex) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == argumentIndex) {
+ QString type = argumentModification.pyiType();
+ if (!type.isEmpty())
+ return type;
+ type = argumentModification.modifiedType();
+ if (!type.isEmpty())
+ return type;
+ }
+ }
+ }
+
+ return {};
+}
+
+// Parameter 'comment' indicates usage as a code comment of the overload decisor
+QString AbstractMetaFunctionPrivate::formatMinimalSignature(const AbstractMetaFunction *q,
+ bool comment) const
+{
+ QString result = m_originalName + u'(';
+ for (qsizetype i = 0; i < m_arguments.size(); ++i) {
+ const auto &argument = m_arguments.at(i);
+ if (i > 0)
+ result += u',';
+
+ const auto &type = comment ? argument.modifiedType() : argument.type();
+ result += type.minimalSignature();
+ if (comment && argument.hasDefaultValueExpression())
+ result += u'=';
+ }
+ result += u')';
+ if (m_constant)
+ result += u"const"_s;
+ result = TypeDatabase::normalizedSignature(result);
+
+ if (comment && !q->isVoid()) {
+ result += u"->"_s;
+ result += q->isTypeModified()
+ ? q->modifiedTypeName() : q->type().minimalSignature();
+ }
+ return result;
+}
+
+QString AbstractMetaFunction::minimalSignature() const
+{
+ if (d->m_cachedMinimalSignature.isEmpty())
+ d->m_cachedMinimalSignature = d->formatMinimalSignature(this, false);
+ return d->m_cachedMinimalSignature;
+}
+
+QStringList AbstractMetaFunction::modificationSignatures() const
+{
+ QStringList result{minimalSignature()};
+ if (d->m_unresolvedSignature != result.constFirst())
+ result.append(d->m_unresolvedSignature);
+ return result;
+}
+
+QString AbstractMetaFunction::signatureComment() const
+{
+ return d->formatMinimalSignature(this, true);
+}
+
+QString AbstractMetaFunction::debugSignature() const
+{
+ QString result;
+ const auto attributes = cppAttributes();
+ const bool isOverride = attributes.testFlag(FunctionAttribute::Override);
+ const bool isFinal = attributes.testFlag(FunctionAttribute::Final);
+ if (!isOverride && !isFinal && (attributes.testFlag(FunctionAttribute::Virtual)))
+ result += u"virtual "_s;
+ if (d->m_implementingClass)
+ result += d->m_implementingClass->qualifiedCppName() + u"::"_s;
+ result += minimalSignature();
+ if (isOverride)
+ result += u" override"_s;
+ if (isFinal)
+ result += u" final"_s;
+ return result;
+}
+
+FunctionModificationList AbstractMetaFunction::findClassModifications(const AbstractMetaFunction *f,
+ AbstractMetaClassCPtr implementor)
+{
+ const auto signatures = f->modificationSignatures();
+ FunctionModificationList mods;
+ while (implementor) {
+ mods += implementor->typeEntry()->functionModifications(signatures);
+ if ((implementor == implementor->baseClass()) ||
+ (implementor == f->implementingClass() && !mods.isEmpty())) {
+ break;
+ }
+ implementor = implementor->baseClass();
+ }
+ return mods;
+}
+
+FunctionModificationList AbstractMetaFunction::findGlobalModifications(const AbstractMetaFunction *f)
+{
+ auto *td = TypeDatabase::instance();
+ return td->globalFunctionModifications(f->modificationSignatures());
+}
+
+const FunctionModificationList &
+ AbstractMetaFunctionPrivate::modifications(const AbstractMetaFunction *q,
+ const AbstractMetaClassCPtr &implementor) const
+{
+ if (m_addedFunction)
+ return m_addedFunction->modifications();
+ for (const auto &ce : m_modificationCache) {
+ if (ce.klass == implementor)
+ return ce.modifications;
+ }
+ auto modifications = m_class == nullptr
+ ? AbstractMetaFunction::findGlobalModifications(q)
+ : AbstractMetaFunction::findClassModifications(q, implementor);
+
+ m_modificationCache.append({implementor, modifications});
+ return m_modificationCache.constLast().modifications;
+}
+
+const FunctionModificationList &
+ AbstractMetaFunction::modifications(AbstractMetaClassCPtr implementor) const
+{
+ if (!implementor)
+ implementor = d->m_class;
+ return d->modifications(this, implementor);
+}
+
+void AbstractMetaFunction::clearModificationsCache()
+{
+ d->m_modificationCache.clear();
+}
+
+const DocModificationList AbstractMetaFunction::addedFunctionDocModifications() const
+{
+ return d->m_addedFunction
+ ? d->m_addedFunction->docModifications() : DocModificationList{};
+}
+
+QString AbstractMetaFunction::argumentName(int index,
+ bool /* create */,
+ AbstractMetaClassCPtr /* implementor */) const
+{
+ return d->m_arguments[--index].name();
+}
+
+int AbstractMetaFunction::propertySpecIndex() const
+{
+ return d->m_propertySpecIndex;
+}
+
+void AbstractMetaFunction::setPropertySpecIndex(int i)
+{
+ d->m_propertySpecIndex = i;
+}
+
+FunctionTypeEntryPtr AbstractMetaFunction::typeEntry() const
+{
+ return d->m_typeEntry;
+}
+
+void AbstractMetaFunction::setTypeEntry(const FunctionTypeEntryPtr &typeEntry)
+{
+ d->m_typeEntry = typeEntry;
+}
+
+QString AbstractMetaFunction::targetLangPackage() const
+{
+ if (d->m_addedFunction != nullptr)
+ return d->m_addedFunction->targetLangPackage();
+ if (d->m_class != nullptr)
+ return d->m_class->typeEntry()->targetLangPackage();
+ if (d->m_typeEntry != nullptr)
+ return d->m_typeEntry->targetLangPackage();
+ return {};
+}
+
+bool AbstractMetaFunction::isCallOperator() const
+{
+ return d->m_name == u"operator()";
+}
+
+bool AbstractMetaFunction::hasInjectedCode() const
+{
+ const FunctionModificationList &mods = modifications(ownerClass());
+ for (const FunctionModification &mod : mods) {
+ if (mod.isCodeInjection())
+ return true;
+ }
+ return false;
+}
+
+// Traverse the code snippets, return true if predicate returns true
+template <class Predicate>
+bool AbstractMetaFunction::traverseCodeSnips(Predicate predicate,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ for (const FunctionModification &mod : modifications(ownerClass())) {
+ if (mod.isCodeInjection()) {
+ for (const CodeSnip &snip : mod.snips()) {
+ if ((snip.language & language) != 0
+ && (snip.position == position || position == TypeSystem::CodeSnipPositionAny)
+ && predicate(snip)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+CodeSnipList AbstractMetaFunction::injectedCodeSnips(TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ CodeSnipList result;
+ traverseCodeSnips([&result] (const CodeSnip &s) {
+ result.append(s);
+ return false;
+ }, position, language);
+ return result;
+}
+
+bool AbstractMetaFunction::injectedCodeContains(const QRegularExpression &pattern,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ return traverseCodeSnips([pattern] (const CodeSnip &s) {
+ return s.code().contains(pattern);
+ }, position, language);
+}
+
+bool AbstractMetaFunction::injectedCodeContains(QStringView pattern,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ return traverseCodeSnips([pattern] (const CodeSnip &s) {
+ return s.code().contains(pattern);
+ }, position, language);
+}
+
+bool AbstractMetaFunction::hasSignatureModifications() const
+{
+ const FunctionModificationList &mods = modifications();
+ for (const FunctionModification &mod : mods) {
+ if (mod.isRenameModifier())
+ return true;
+ for (const ArgumentModification &argmod : mod.argument_mods()) {
+ // since zero represents the return type and we're
+ // interested only in checking the function arguments,
+ // it will be ignored.
+ if (argmod.index() > 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AbstractMetaFunction::isConversionOperator(const QString &funcName)
+{
+ return funcName.startsWith(u"operator ");
+}
+
+ExceptionSpecification AbstractMetaFunction::exceptionSpecification() const
+{
+ return d->m_exceptionSpecification;
+}
+
+void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e)
+{
+ d->m_exceptionSpecification = e;
+}
+
+static inline TypeSystem::ExceptionHandling exceptionMod(const AbstractMetaClassCPtr &klass)
+{
+ return klass->typeEntry()->exceptionHandling();
+}
+
+static inline bool hasExceptionMod(const AbstractMetaClassCPtr &klass)
+{
+ return exceptionMod(klass) != TypeSystem::ExceptionHandling::Unspecified;
+}
+
+bool AbstractMetaFunction::generateExceptionHandling() const
+{
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::CopyConstructorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveAssignmentOperatorFunction:
+ case AbstractMetaFunction::DestructorFunction:
+ return false;
+ default:
+ break;
+ }
+
+ auto exceptionHandlingModification = d->m_exceptionHandlingModification;
+ // If there is no modification on the function, check for a base class.
+ if (d->m_class && exceptionHandlingModification == TypeSystem::ExceptionHandling::Unspecified) {
+ if (auto base = recurseClassHierarchy(d->m_class, hasExceptionMod))
+ exceptionHandlingModification = exceptionMod(base);
+ }
+
+ bool result = false;
+ switch (exceptionHandlingModification) {
+ case TypeSystem::ExceptionHandling::On:
+ result = true;
+ break;
+ case TypeSystem::ExceptionHandling::AutoDefaultToOn:
+ result = d->m_exceptionSpecification != ExceptionSpecification::NoExcept;
+ break;
+ case TypeSystem::ExceptionHandling::AutoDefaultToOff:
+ result = d->m_exceptionSpecification == ExceptionSpecification::Throws;
+ break;
+ case TypeSystem::ExceptionHandling::Unspecified:
+ case TypeSystem::ExceptionHandling::Off:
+ break;
+ }
+ return result;
+}
+
+bool AbstractMetaFunction::isConversionOperator() const
+{
+ return d->m_functionType == ConversionOperator;
+}
+
+bool AbstractMetaFunction::isOperatorOverload(const QString &funcName)
+{
+ if (isConversionOperator(funcName))
+ return true;
+
+ static const QRegularExpression opRegEx(u"^operator([+\\-\\*/%=&\\|\\^\\<>!][=]?"
+ "|\\+\\+|\\-\\-|&&|\\|\\||<<[=]?|>>[=]?|~"
+ "|\\[\\]|\\s+delete\\[?\\]?"
+ "|\\(\\)"
+ "|\\s+new\\[?\\]?)$"_s);
+ Q_ASSERT(opRegEx.isValid());
+ return opRegEx.match(funcName).hasMatch();
+}
+
+bool AbstractMetaFunction::isOperatorOverload() const
+{
+ return d->m_functionType == AssignmentOperatorFunction
+ || (d->m_functionType >= FirstOperator && d->m_functionType <= LastOperator);
+}
+
+bool AbstractMetaFunction::isArithmeticOperator() const
+{
+ return d->m_functionType == ArithmeticOperator;
+}
+
+bool AbstractMetaFunction::isBitwiseOperator() const
+{
+ return d->m_functionType == BitwiseOperator
+ || d->m_functionType == ShiftOperator;
+}
+
+bool AbstractMetaFunction::isComparisonOperator() const
+{
+ return d->m_functionType == ComparisonOperator;
+}
+
+bool AbstractMetaFunction::isSymmetricalComparisonOperator() const
+{
+ if (d->m_functionType != ComparisonOperator || d->m_class == nullptr)
+ return false;
+ AbstractMetaType classType(d->m_class->typeEntry());
+ classType.decideUsagePattern();
+ return std::all_of(d->m_arguments.constBegin(), d->m_arguments.constEnd(),
+ [classType](const AbstractMetaArgument &a) {
+ return a.type().isEquivalent(classType);});
+}
+
+bool AbstractMetaFunction::isIncDecrementOperator() const
+{
+ return d->m_functionType == IncrementOperator
+ || d->m_functionType == DecrementOperator;
+}
+bool AbstractMetaFunction::isLogicalOperator() const
+{
+ return d->m_functionType == LogicalOperator;
+}
+
+bool AbstractMetaFunction::isAssignmentOperator() const
+{
+ return d->m_functionType == AssignmentOperatorFunction
+ || d->m_functionType == MoveAssignmentOperatorFunction;
+}
+
+bool AbstractMetaFunction::isGetter() const
+{
+ return d->m_functionType == NormalFunction && !isVoid()
+ && d->m_constant && d->m_access == Access::Public
+ && d->m_arguments.isEmpty();
+}
+
+bool AbstractMetaFunction::isQtIsNullMethod() const
+{
+ return isGetter() && d->m_name == u"isNull" && returnsBool();
+}
+
+int AbstractMetaFunction::arityOfOperator() const
+{
+ if (!isOperatorOverload() || isCallOperator())
+ return -1;
+
+ int arity = d->m_arguments.size();
+
+ // Operator overloads that are class members
+ // implicitly includes the instance and have
+ // one parameter less than their arity,
+ // so we increment it.
+ if (ownerClass() && arity < 2)
+ arity++;
+
+ return arity;
+}
+
+bool AbstractMetaFunction::isInplaceOperator() const
+{
+ static const QSet<QStringView> inplaceOperators =
+ {
+ u"operator+=", u"operator&=", u"operator-=", u"operator|=",
+ u"operator*=", u"operator^=", u"operator/=", u"operator<<=",
+ u"operator%=", u"operator>>="
+ };
+
+ return isOperatorOverload() && inplaceOperators.contains(originalName());
+}
+
+bool AbstractMetaFunction::isVirtual() const
+{
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Virtual);
+}
+
+QString AbstractMetaFunctionPrivate::modifiedName(const AbstractMetaFunction *q) const
+{
+ if (m_cachedModifiedName.isEmpty()) {
+ for (const auto &mod : q->modifications(q->implementingClass())) {
+ if (mod.isRenameModifier()) {
+ m_cachedModifiedName = mod.renamedToName();
+ break;
+ }
+ }
+ if (m_cachedModifiedName.isEmpty())
+ m_cachedModifiedName = m_name;
+ }
+ return m_cachedModifiedName;
+}
+
+QString AbstractMetaFunction::modifiedName() const
+{
+ return d->modifiedName(this);
+}
+
+AbstractMetaFunctionCPtr
+AbstractMetaFunction::find(const AbstractMetaFunctionCList &haystack,
+ QAnyStringView needle)
+{
+ for (const auto &f : haystack) {
+ if (f->name() == needle)
+ return f;
+ }
+ return {};
+}
+
+bool AbstractMetaFunction::matches(OperatorQueryOptions query) const
+{
+ bool result = false;
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ result = query.testFlag(OperatorQueryOption::AssignmentOp);
+ break;
+ case AbstractMetaFunction::ConversionOperator:
+ result = query.testFlag(OperatorQueryOption::ConversionOp);
+ break;
+ case AbstractMetaFunction::ArithmeticOperator:
+ result = query.testFlag(OperatorQueryOption::ArithmeticOp);
+ break;
+ case AbstractMetaFunction::IncrementOperator:
+ case AbstractMetaFunction::DecrementOperator:
+ result = query.testFlag(OperatorQueryOption::IncDecrementOp);
+ break;
+ case AbstractMetaFunction::BitwiseOperator:
+ case AbstractMetaFunction::ShiftOperator:
+ result = query.testFlag(OperatorQueryOption::BitwiseOp);
+ break;
+ case AbstractMetaFunction::LogicalOperator:
+ result = query.testFlag(OperatorQueryOption::LogicalOp);
+ break;
+ case AbstractMetaFunction::SubscriptOperator:
+ result = query.testFlag(OperatorQueryOption::SubscriptionOp);
+ break;
+ case AbstractMetaFunction::ComparisonOperator:
+ result = query.testFlag(OperatorQueryOption::ComparisonOp);
+ if (!result && query.testFlag(OperatorQueryOption::SymmetricalComparisonOp))
+ result = isSymmetricalComparisonOperator();
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+void AbstractMetaFunction::setAllowThreadModification(TypeSystem::AllowThread am)
+{
+ d->m_allowThreadModification = am;
+}
+
+void AbstractMetaFunction::setExceptionHandlingModification(TypeSystem::ExceptionHandling em)
+{
+ d->m_exceptionHandlingModification = em;
+}
+
+int AbstractMetaFunctionPrivate::overloadNumber(const AbstractMetaFunction *q) const
+{
+ if (m_cachedOverloadNumber == TypeSystem::OverloadNumberUnset) {
+ m_cachedOverloadNumber = TypeSystem::OverloadNumberDefault;
+ for (const auto &mod : q->modifications(q->implementingClass())) {
+ if (mod.overloadNumber() != TypeSystem::OverloadNumberUnset) {
+ m_cachedOverloadNumber = mod.overloadNumber();
+ break;
+ }
+ }
+ }
+ return m_cachedOverloadNumber;
+}
+
+int AbstractMetaFunction::overloadNumber() const
+{
+ return d->overloadNumber(this);
+}
+
+TypeSystem::SnakeCase AbstractMetaFunction::snakeCase() const
+{
+ if (isUserAdded())
+ return TypeSystem::SnakeCase::Disabled;
+ // Renamed?
+ if (!d->m_originalName.isEmpty() && d->m_originalName != d->m_name)
+ return TypeSystem::SnakeCase::Disabled;
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::NormalFunction:
+ case AbstractMetaFunction::SignalFunction:
+ case AbstractMetaFunction::EmptyFunction:
+ case AbstractMetaFunction::SlotFunction:
+ break;
+ default:
+ return TypeSystem::SnakeCase::Disabled;
+ }
+
+ for (const auto &mod : modifications()) {
+ if (mod.snakeCase() != TypeSystem::SnakeCase::Unspecified)
+ return mod.snakeCase();
+ }
+
+ if (d->m_typeEntry) // Global function
+ return typeSystemTypeEntry(d->m_typeEntry)->snakeCase();
+
+ if (d->m_class) {
+ auto typeEntry = d->m_class->typeEntry();
+ const auto snakeCase = typeEntry->snakeCase();
+ return snakeCase != TypeSystem::SnakeCase::Unspecified
+ ? snakeCase : typeSystemTypeEntry(typeEntry)->snakeCase();
+ }
+ return TypeSystem::SnakeCase::Disabled;
+}
+
+// Query functions for generators
+bool AbstractMetaFunction::injectedCodeUsesPySelf() const
+{
+ return injectedCodeContains(u"%PYSELF", TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode);
+}
+
+bool AbstractMetaFunction::injectedCodeCallsPythonOverride() const
+{
+ static const QRegularExpression
+ overrideCallRegexCheck(R"(PyObject_Call\s*\(\s*%PYTHON_METHOD_OVERRIDE\s*,)"_L1);
+ Q_ASSERT(overrideCallRegexCheck.isValid());
+ return injectedCodeContains(overrideCallRegexCheck, TypeSystem::CodeSnipPositionAny,
+ TypeSystem::NativeCode);
+}
+
+bool AbstractMetaFunction::injectedCodeHasReturnValueAttribution(TypeSystem::Language language) const
+{
+ if (language == TypeSystem::TargetLangCode) {
+ static const QRegularExpression
+ retValAttributionRegexCheck_target(R"(%PYARG_0\s*=[^=]\s*.+)"_L1);
+ Q_ASSERT(retValAttributionRegexCheck_target.isValid());
+ return injectedCodeContains(retValAttributionRegexCheck_target, TypeSystem::CodeSnipPositionAny, language);
+ }
+
+ static const QRegularExpression
+ retValAttributionRegexCheck_native(R"(%0\s*=[^=]\s*.+)"_L1);
+ Q_ASSERT(retValAttributionRegexCheck_native.isValid());
+ return injectedCodeContains(retValAttributionRegexCheck_native, TypeSystem::CodeSnipPositionAny, language);
+}
+
+bool AbstractMetaFunction::injectedCodeUsesArgument(int argumentIndex) const
+{
+ const QRegularExpression argRegEx = CodeSnipAbstract::placeHolderRegex(argumentIndex + 1);
+
+ return traverseCodeSnips([argRegEx](const CodeSnip &s) {
+ const QString code = s.code();
+ return code.contains(u"%ARGUMENT_NAMES") || code.contains(argRegEx);
+ }, TypeSystem::CodeSnipPositionAny);
+}
+
+bool AbstractMetaFunction::isVisibilityModifiedToPrivate() const
+{
+ for (const auto &mod : modifications()) {
+ if (mod.modifiers().testFlag(FunctionModification::Private))
+ return true;
+ }
+ return false;
+}
+
+struct ComparisonOperator
+{
+ const char *cppOperator;
+ const char *pythonOpCode;
+};
+
+using ComparisonOperatorMapping =
+ QHash<AbstractMetaFunction::ComparisonOperatorType, ComparisonOperator>;
+
+static const ComparisonOperatorMapping &comparisonOperatorMapping()
+{
+ static const ComparisonOperatorMapping result = {
+ {AbstractMetaFunction::OperatorEqual, {"==", "Py_EQ"}},
+ {AbstractMetaFunction::OperatorNotEqual, {"!=", "Py_NE"}},
+ {AbstractMetaFunction::OperatorLess, {"<", "Py_LT"}},
+ {AbstractMetaFunction::OperatorLessEqual, {"<=", "Py_LE"}},
+ {AbstractMetaFunction::OperatorGreater, {">", "Py_GT"}},
+ {AbstractMetaFunction::OperatorGreaterEqual, {">=", "Py_GE"}}
+ };
+ return result;
+}
+
+const char * AbstractMetaFunction::pythonRichCompareOpCode(ComparisonOperatorType ct)
+{
+ return comparisonOperatorMapping().value(ct).pythonOpCode;
+}
+
+const char * AbstractMetaFunction::cppComparisonOperator(ComparisonOperatorType ct)
+{
+ return comparisonOperatorMapping().value(ct).cppOperator;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void AbstractMetaFunction::formatDebugBrief(QDebug &debug) const
+{
+ debug << '"' << debugSignature() << '"';
+}
+
+void AbstractMetaFunction::formatDebugVerbose(QDebug &debug) const
+{
+ debug << d->m_functionType << ' ';
+ if (d->m_class)
+ debug << d->m_access << ' ';
+ debug << d->m_type << ' ' << d->m_name;
+ switch (d->m_exceptionSpecification) {
+ case ExceptionSpecification::Unknown:
+ break;
+ case ExceptionSpecification::NoExcept:
+ debug << " noexcept";
+ break;
+ case ExceptionSpecification::Throws:
+ debug << " throw(...)";
+ break;
+ }
+ if (d->m_exceptionHandlingModification != TypeSystem::ExceptionHandling::Unspecified)
+ debug << " exeption-mod " << int(d->m_exceptionHandlingModification);
+ debug << '(';
+ for (qsizetype i = 0, count = d->m_arguments.size(); i < count; ++i) {
+ if (i)
+ debug << ", ";
+ debug << d->m_arguments.at(i);
+ }
+ const QString signature = minimalSignature();
+ debug << "), signature=\"" << signature << '"';
+ if (signature != d->m_unresolvedSignature)
+ debug << ", unresolvedSignature=\"" << d->m_unresolvedSignature << '"';
+ if (d->m_constant)
+ debug << " [const]";
+ if (d->m_reverse)
+ debug << " [reverse]";
+ if (isUserAdded())
+ debug << " [userAdded]";
+ if (isUserDeclared())
+ debug << " [userDeclared]";
+ if (d->m_cppAttributes.testFlag(FunctionAttribute::Explicit))
+ debug << " [explicit]";
+ if (d->m_cppAttributes.testFlag(FunctionAttribute::Deprecated))
+ debug << " [deprecated]";
+ if (d->m_pointerOperator)
+ debug << " [operator->]";
+ if (d->m_isCallOperator)
+ debug << " [operator()]";
+ if (d->m_class)
+ debug << " class: " << d->m_class->name();
+ if (d->m_implementingClass)
+ debug << " implementing class: " << d->m_implementingClass->name();
+ if (d->m_declaringClass)
+ debug << " declaring class: " << d->m_declaringClass->name();
+}
+
+QDebug operator<<(QDebug debug, const AbstractMetaFunction *af)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "AbstractMetaFunction(";
+ if (af) {
+ if (debug.verbosity() > 2) {
+ af->formatDebugVerbose(debug);
+ } else {
+ debug << "signature=";
+ af->formatDebugBrief(debug);
+ }
+ } else {
+ debug << '0';
+ }
+ debug << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.h b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
new file mode 100644
index 000000000..e252e439d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
@@ -0,0 +1,487 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETAFUNCTION_H
+#define ABSTRACTMETAFUNCTION_H
+
+#include "abstractmetalang_enums.h"
+#include "abstractmetalang_typedefs.h"
+#include "typesystem_enums.h"
+#include "modifications_typedefs.h"
+#include "typesystem_typedefs.h"
+#include "parser/codemodel_enums.h"
+
+#include <QtCore/QMetaObject>
+#include <QtCore/QScopedPointer>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QRegularExpression)
+
+class AbstractMetaFunctionPrivate;
+class AbstractMetaType;
+class FunctionTypeEntry;
+class Documentation;
+class SourceLocation;
+
+struct ArgumentOwner;
+struct ReferenceCount;
+
+class AbstractMetaFunction
+{
+ Q_GADGET
+public:
+ Q_DISABLE_COPY_MOVE(AbstractMetaFunction)
+
+ enum FunctionType {
+ ConstructorFunction,
+ CopyConstructorFunction,
+ MoveConstructorFunction,
+ AssignmentOperatorFunction,
+ MoveAssignmentOperatorFunction,
+ DestructorFunction,
+ NormalFunction,
+ SignalFunction,
+ EmptyFunction,
+ SlotFunction,
+ GetAttroFunction,
+ SetAttroFunction,
+ CallOperator,
+ FirstOperator = CallOperator,
+ ConversionOperator,
+ DereferenceOperator, // Iterator's operator *
+ ReferenceOperator, // operator &
+ ArrowOperator,
+ ArithmeticOperator,
+ IncrementOperator,
+ DecrementOperator,
+ BitwiseOperator,
+ LogicalOperator,
+ ShiftOperator,
+ SubscriptOperator,
+ ComparisonOperator,
+ LastOperator = ComparisonOperator
+ };
+ Q_ENUM(FunctionType)
+
+ enum ComparisonOperatorType {
+ OperatorEqual, OperatorNotEqual, OperatorLess, OperatorLessEqual,
+ OperatorGreater, OperatorGreaterEqual
+ };
+ Q_ENUM(ComparisonOperatorType)
+
+ enum CompareResultFlag {
+ EqualName = 0x00000001,
+ EqualArguments = 0x00000002,
+ EqualAttributes = 0x00000004,
+ EqualImplementor = 0x00000008,
+ EqualReturnType = 0x00000010,
+ EqualDefaultValueOverload = 0x00000020,
+ EqualModifiedName = 0x00000040,
+
+ NameLessThan = 0x00001000,
+
+ PrettySimilar = EqualName | EqualArguments,
+ Equal = 0x0000001f,
+ NotEqual = 0x00001000
+ };
+ Q_DECLARE_FLAGS(CompareResult, CompareResultFlag)
+ Q_FLAG(CompareResultFlag)
+
+ enum Attribute {
+ None = 0x00000000,
+
+ ClassMethod = 0x00000008,
+
+ GetterFunction = 0x00000020,
+ SetterFunction = 0x00000040,
+
+ PropertyReader = 0x00000100,
+ PropertyWriter = 0x00000200,
+ PropertyResetter = 0x00000400,
+ PropertyNotify = 0x00000800,
+
+ // Add by meta builder (implicit constructors, inherited methods, etc)
+ AddedMethod = 0x001000000,
+ };
+ Q_DECLARE_FLAGS(Attributes, Attribute)
+ Q_FLAG(Attribute)
+
+ Attributes attributes() const;
+ void setAttributes(Attributes attributes);
+
+ void operator+=(Attribute attribute);
+ void operator-=(Attribute attribute);
+
+ FunctionAttributes cppAttributes() const;
+ void setCppAttributes(FunctionAttributes a);
+ void setCppAttribute(FunctionAttribute a, bool on = true);
+
+ enum class Flag { // Internal flags not relevant for comparing functions
+ // Binary operator whose leading/trailing argument was removed by metabuilder
+ OperatorLeadingClassArgumentRemoved = 0x1,
+ OperatorTrailingClassArgumentRemoved = 0x2,
+ OperatorClassArgumentByValue = 0x4, // The removed class argument was passed by value
+ InheritedFromTemplate = 0x8, // Inherited from a template in metabuilder
+ HiddenFriend = 0x10,
+ PrivateSignal = 0x20 // Private Qt signal (cannot emit from client code)
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ Flags flags() const;
+ void setFlags(Flags f);
+
+ bool isAbstract() const;
+ bool isClassMethod() const;
+ bool isStatic() const;
+ bool isPropertyReader() const;
+ bool isPropertyWriter() const;
+ bool isPropertyResetter() const;
+
+ AbstractMetaFunction();
+ explicit AbstractMetaFunction(const QString &name);
+ explicit AbstractMetaFunction(const AddedFunctionPtr &addedFunc);
+ ~AbstractMetaFunction();
+
+ QString name() const;
+ void setName(const QString &name);
+
+ // Names under which the function will be registered to Python.
+ QStringList definitionNames() const;
+
+ QString originalName() const;
+
+ void setOriginalName(const QString &name);
+
+ Access access() const;
+ void setAccess(Access a);
+ void modifyAccess(Access a);
+
+ bool isPrivate() const { return access() == Access::Private; }
+ bool isProtected() const { return access() == Access::Protected; }
+ bool isPublic() const { return access() == Access::Public; }
+ bool wasPrivate() const;
+ bool wasProtected() const;
+ bool wasPublic() const;
+
+ const Documentation &documentation() const;
+ void setDocumentation(const Documentation& doc);
+
+ bool isReverseOperator() const;
+ void setReverseOperator(bool reverse);
+
+ /// Returns true if this is a binary operator and the "self" operand is a
+ /// pointer, e.g. class Foo {}; operator+(SomeEnum, Foo*);
+ /// (not to be mixed up with DereferenceOperator).
+ bool isPointerOperator() const;
+ void setPointerOperator(bool value);
+
+
+ /**
+ * Says if the function (a constructor) was declared as explicit in C++.
+ * \return true if the function was declared as explicit in C++
+ */
+ bool isExplicit() const;
+ void setExplicit(bool isExplicit);
+
+ bool returnsBool() const;
+ bool isOperatorBool() const;
+ static bool isConversionOperator(const QString& funcName);
+
+ ExceptionSpecification exceptionSpecification() const;
+ void setExceptionSpecification(ExceptionSpecification e);
+
+ bool generateExceptionHandling() const;
+
+ bool isConversionOperator() const;
+
+ static bool isOperatorOverload(const QString& funcName);
+ bool isOperatorOverload() const;
+
+ bool isArithmeticOperator() const;
+ bool isBitwiseOperator() const; // Includes shift operator
+ bool isComparisonOperator() const;
+ /// Returns whether this is a comparison accepting owner class
+ /// (bool operator==(QByteArray,QByteArray) but not bool operator==(QByteArray,const char *)
+ bool isSymmetricalComparisonOperator() const;
+ bool isIncDecrementOperator() const;
+ bool isLogicalOperator() const;
+ bool isAssignmentOperator() const; // Assignment or move assignment
+ bool isGetter() const;
+ /// Returns whether it is a Qt-style isNull() method suitable for nb_bool
+ bool isQtIsNullMethod() const;
+
+ /**
+ * Informs the arity of the operator or -1 if the function is not
+ * an operator overload.
+ * /return the arity of the operator or -1
+ */
+ int arityOfOperator() const;
+ bool isUnaryOperator() const { return arityOfOperator() == 1; }
+ bool isBinaryOperator() const { return arityOfOperator() == 2; }
+ bool isInplaceOperator() const;
+
+ bool isVirtual() const;
+ bool allowThread() const;
+ QString modifiedName() const;
+
+ QString minimalSignature() const;
+ /// List of signatures matched for modifications
+ QStringList modificationSignatures() const;
+ // Signature with replaced argument types and return type for overload
+ // decisor comment.
+ QString signatureComment() const;
+ QString debugSignature() const; // including virtual/override/final, etc., for debugging only.
+
+ bool isModifiedRemoved(AbstractMetaClassCPtr cls = {}) const;
+ bool isModifiedFinal(AbstractMetaClassCPtr cls = {}) const;
+
+ bool isVoid() const;
+
+ const AbstractMetaType &type() const;
+ void setType(const AbstractMetaType &type);
+
+ // The class that has this function as a member.
+ AbstractMetaClassCPtr ownerClass() const;
+ void setOwnerClass(const AbstractMetaClassCPtr &cls);
+
+ // Owner excluding invisible namespaces
+ AbstractMetaClassCPtr targetLangOwner() const;
+
+ // The first class in a hierarchy that declares the function
+ AbstractMetaClassCPtr declaringClass() const;
+ void setDeclaringClass(const AbstractMetaClassCPtr &cls);
+
+ // The class that actually implements this function
+ AbstractMetaClassCPtr implementingClass() const;
+ void setImplementingClass(const AbstractMetaClassCPtr &cls);
+
+ const AbstractMetaArgumentList &arguments() const;
+ AbstractMetaArgumentList &arguments();
+ void setArguments(const AbstractMetaArgumentList &arguments);
+ void addArgument(const AbstractMetaArgument &argument);
+ int actualMinimumArgumentCount() const;
+ // Return the argument index accounting for the isModifiedRemoved arguments [0..n-1]
+ int actualArgumentIndex(int index) const;
+
+ bool isDeprecated() const;
+ bool isDestructor() const { return functionType() == DestructorFunction; }
+ bool isConstructor() const;
+ bool isCopyConstructor() const { return functionType() == CopyConstructorFunction; }
+ bool isDefaultConstructor() const;
+ bool needsReturnType() const;
+ bool isInGlobalScope() const;
+ bool isSignal() const { return functionType() == SignalFunction; }
+ bool isSlot() const { return functionType() == SlotFunction; }
+ bool isEmptyFunction() const { return functionType() == EmptyFunction; }
+ bool maybeAccessor() const;
+ FunctionType functionType() const;
+ void setFunctionType(FunctionType type);
+
+ std::optional<ComparisonOperatorType> comparisonOperatorType() const;
+
+ bool usesRValueReferences() const;
+ bool generateBinding() const;
+ // Returns whether the function is contained in the positive list of the
+ // type entry if one is specified.
+ bool isWhiteListed() const;
+
+ QString signature() const;
+ /// Return a signature qualified by class name, for error reporting.
+ QString classQualifiedSignature() const;
+
+ /// Signature with unresolved typedefs as seen by the code parser
+ QString unresolvedSignature() const;
+ void setUnresolvedSignature(const QString &);
+
+ bool isConstant() const;
+ void setConstant(bool constant);
+
+ /// Returns true if the AbstractMetaFunction was added by the user via the type system description.
+ bool isUserAdded() const;
+ bool isUserAddedPythonOverride() const;
+ /// Returns true if the AbstractMetaFunction was declared by the user via
+ /// the type system description.
+ bool isUserDeclared() const;
+
+ CompareResult compareTo(const AbstractMetaFunction *other) const;
+ bool isConstOverloadOf(const AbstractMetaFunction *other) const;
+
+ bool operator <(const AbstractMetaFunction &a) const;
+
+ AbstractMetaFunction *copy() const;
+
+ QString conversionRule(TypeSystem::Language language, int idx) const;
+ bool hasConversionRule(TypeSystem::Language language, int idx) const;
+ QList<ReferenceCount>
+ referenceCounts(const AbstractMetaClassCPtr &cls, int idx = -2) const;
+ ArgumentOwner argumentOwner(const AbstractMetaClassCPtr &cls, int idx) const;
+
+ // Returns the ownership rules for the given argument (target lang).
+ TypeSystem::Ownership
+ argumentTargetOwnership(const AbstractMetaClassCPtr &cls, int idx) const;
+
+ const QString &modifiedTypeName() const;
+ bool isTypeModified() const { return !modifiedTypeName().isEmpty(); }
+ bool generateOpaqueContainerReturn() const;
+
+ bool isModifiedToArray(int argumentIndex) const;
+
+ void applyTypeModifications();
+
+ /// Return the (modified) type for the signature; modified-pyi-type, modified-type
+ QString pyiTypeReplaced(int argumentIndex) const;
+
+ bool argumentRemoved(int) const;
+ /**
+ * Verifies if any modification to the function is an inject code.
+ * \return true if there is inject code modifications to the function.
+ */
+ bool hasInjectedCode() const;
+ /**
+ * Returns a list of code snips for this function.
+ * The code snips can be filtered by position and language.
+ * \return list of code snips
+ */
+ CodeSnipList injectedCodeSnips(TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionAny,
+ TypeSystem::Language language = TypeSystem::All) const;
+ bool injectedCodeContains(const QRegularExpression &pattern,
+ TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionAny,
+ TypeSystem::Language language = TypeSystem::All) const;
+ bool injectedCodeContains(QStringView pattern,
+ TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionAny,
+ TypeSystem::Language language = TypeSystem::All) const;
+
+ /**
+ * Verifies if any modification to the function alters/removes its
+ * arguments types or default values.
+ * \return true if there is some modification to function signature
+ */
+ bool hasSignatureModifications() const;
+
+ const FunctionModificationList &modifications(AbstractMetaClassCPtr implementor = {}) const;
+ void clearModificationsCache();
+
+ const DocModificationList addedFunctionDocModifications() const;
+
+ static FunctionModificationList findClassModifications(const AbstractMetaFunction *f,
+ AbstractMetaClassCPtr implementor);
+ static FunctionModificationList findGlobalModifications(const AbstractMetaFunction *f);
+
+ /**
+ * Return the argument name if there is a modification the renamed value will be returned
+ */
+ QString argumentName(int index, bool create = true, AbstractMetaClassCPtr cl = {}) const;
+
+ int propertySpecIndex() const;
+ void setPropertySpecIndex(int i);
+
+ FunctionTypeEntryPtr typeEntry() const;
+ void setTypeEntry(const FunctionTypeEntryPtr &typeEntry);
+
+ QString targetLangPackage() const;
+
+ bool isCallOperator() const;
+
+ static AbstractMetaFunctionCPtr
+ find(const AbstractMetaFunctionCList &haystack, QAnyStringView needle);
+
+ bool matches(OperatorQueryOptions) const;
+
+ // for the meta builder only
+ void setAllowThreadModification(TypeSystem::AllowThread am);
+ void setExceptionHandlingModification(TypeSystem::ExceptionHandling em);
+
+ int overloadNumber() const;
+
+ TypeSystem::SnakeCase snakeCase() const;
+
+ // Query functions for generators
+ /// Verifies if any of the function's code injections of the "native"
+ /// type needs the type system variable "%PYSELF".
+ /// \return true if the function's native code snippets use "%PYSELF"
+ bool injectedCodeUsesPySelf() const;
+
+ /// Verifies if any of the function's code injections of the "native" class makes a
+ /// call to the C++ method. This is used by the generator to avoid writing calls to
+ /// Python overrides of C++ virtual methods when the user custom code already does this.
+ /// \param func the function to check
+ /// \return true if the function's code snippets call the Python override for a C++ virtual method
+ bool injectedCodeCallsPythonOverride() const;
+
+ /// Verifies if any of the function's code injections attributes values to
+ /// the return variable (%0 or %PYARG_0).
+ /// \param language the kind of code snip
+ /// \return true if the function's code attributes values to "%0" or "%PYARG_0"
+ bool injectedCodeHasReturnValueAttribution(TypeSystem::Language language =
+ TypeSystem::TargetLangCode) const;
+
+ /// Verifies if any of the function's code injections uses the type system variable
+ /// for function arguments of a given index.
+ bool injectedCodeUsesArgument(int argumentIndex) const;
+
+ bool isVisibilityModifiedToPrivate() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebugBrief(QDebug &debug) const;
+ void formatDebugVerbose(QDebug &debug) const;
+#endif
+
+ SourceLocation sourceLocation() const;
+ void setSourceLocation(const SourceLocation &sourceLocation);
+
+ static const char *pythonRichCompareOpCode(ComparisonOperatorType ct);
+ static const char *cppComparisonOperator(ComparisonOperatorType ct);
+
+private:
+ template <class Predicate>
+ bool traverseCodeSnips(Predicate predicate,
+ TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionAny,
+ TypeSystem::Language language = TypeSystem::All) const;
+ bool autoDetectAllowThread() const;
+
+ QScopedPointer<AbstractMetaFunctionPrivate> d;
+};
+
+inline bool AbstractMetaFunction::isAbstract() const
+{
+ return cppAttributes().testFlag(FunctionAttribute::Abstract);
+}
+
+inline bool AbstractMetaFunction::isStatic() const
+{
+ return cppAttributes().testFlag(FunctionAttribute::Static);
+}
+
+inline bool AbstractMetaFunction::isClassMethod() const
+{
+ return attributes().testFlag(ClassMethod);
+}
+
+inline bool AbstractMetaFunction::isPropertyReader() const
+{
+ return attributes().testFlag(PropertyReader);
+}
+
+inline bool AbstractMetaFunction::isPropertyWriter() const
+{
+ return attributes().testFlag(PropertyWriter);
+}
+
+inline bool AbstractMetaFunction::isPropertyResetter() const
+{
+ return attributes().testFlag(PropertyResetter);
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult)
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::Attributes);
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::Flags);
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const AbstractMetaFunction *af);
+#endif
+
+#endif // ABSTRACTMETAFUNCTION_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.cpp b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp
new file mode 100644
index 000000000..fb49cc9d0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp
@@ -0,0 +1,1983 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetalang.h"
+#include "anystringview_helpers.h"
+#include "abstractmetalang_helpers.h"
+#include "abstractmetaargument.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafunction.h"
+#include "abstractmetatype.h"
+#include "abstractmetafield.h"
+#include "parser/codemodel.h"
+#include "documentation.h"
+#include "messages.h"
+#include "modifications.h"
+#include "propertyspec.h"
+#include "reporthandler.h"
+#include "sourcelocation.h"
+#include "typedatabase.h"
+#include "enumtypeentry.h"
+#include "namespacetypeentry.h"
+#include "usingmember.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+bool function_sorter(const AbstractMetaFunctionCPtr &a, const AbstractMetaFunctionCPtr &b)
+{
+ return a->signature() < b->signature();
+}
+
+class AbstractMetaClassPrivate
+{
+public:
+ AbstractMetaClassPrivate()
+ : m_hasVirtuals(false),
+ m_isPolymorphic(false),
+ m_hasNonpublic(false),
+ m_hasNonPrivateConstructor(false),
+ m_hasPrivateConstructor(false),
+ m_hasDeletedDefaultConstructor(false),
+ m_hasDeletedCopyConstructor(false),
+ m_functionsFixed(false),
+ m_inheritanceDone(false),
+ m_hasPrivateDestructor(false),
+ m_hasProtectedDestructor(false),
+ m_hasVirtualDestructor(false),
+ m_isTypeDef(false),
+ m_hasToStringCapability(false),
+ m_valueTypeWithCopyConstructorOnly(false),
+ m_hasCachedWrapper(false)
+ {
+ }
+
+ void addFunction(const AbstractMetaFunctionCPtr &function);
+ static AbstractMetaFunction *
+ createFunction(const QString &name, AbstractMetaFunction::FunctionType t,
+ Access access, const AbstractMetaArgumentList &arguments,
+ const AbstractMetaType &returnType, const AbstractMetaClassPtr &q);
+ void addConstructor(AbstractMetaFunction::FunctionType t,
+ Access access,
+ const AbstractMetaArgumentList &arguments,
+ const AbstractMetaClassPtr &q);
+ void addUsingConstructors(const AbstractMetaClassPtr &q);
+ void sortFunctions();
+ void setFunctions(const AbstractMetaFunctionCList &functions,
+ const AbstractMetaClassCPtr &q);
+ bool isUsingMember(const AbstractMetaClassCPtr &c, const QString &memberName,
+ Access minimumAccess) const;
+ bool hasConstructors() const;
+ qsizetype indexOfProperty(const QString &name) const;
+
+ uint m_hasVirtuals : 1;
+ uint m_isPolymorphic : 1;
+ uint m_hasNonpublic : 1;
+ uint m_hasNonPrivateConstructor : 1;
+ uint m_hasPrivateConstructor : 1;
+ uint m_hasDeletedDefaultConstructor : 1;
+ uint m_hasDeletedCopyConstructor : 1;
+ uint m_functionsFixed : 1;
+ uint m_inheritanceDone : 1; // m_baseClasses has been populated from m_baseClassNames
+ uint m_hasPrivateDestructor : 1;
+ uint m_hasProtectedDestructor : 1;
+ uint m_hasVirtualDestructor : 1;
+ uint m_isTypeDef : 1;
+ uint m_hasToStringCapability : 1;
+ uint m_valueTypeWithCopyConstructorOnly : 1;
+ mutable uint m_hasCachedWrapper : 1;
+
+ Documentation m_doc;
+
+ AbstractMetaClassCPtr m_enclosingClass;
+ AbstractMetaClassCPtr m_defaultSuperclass;
+ AbstractMetaClassCList m_baseClasses; // Real base classes after setting up inheritance
+ AbstractMetaTypeList m_baseTemplateInstantiations;
+ AbstractMetaClassCPtr m_extendedNamespace;
+
+ AbstractMetaClassCPtr m_templateBaseClass;
+ AbstractMetaFunctionCList m_functions;
+ AbstractMetaFunctionCList m_userAddedPythonOverrides;
+ AbstractMetaFieldList m_fields;
+ AbstractMetaEnumList m_enums;
+ QList<QPropertySpec> m_propertySpecs;
+ AbstractMetaClassCList m_innerClasses;
+ QString m_hashFunction;
+
+ AbstractMetaFunctionCList m_externalConversionOperators;
+
+ QStringList m_baseClassNames; // Base class names from C++, including rejected
+ TypeEntryCList m_templateArgs;
+ ComplexTypeEntryPtr m_typeEntry;
+ SourceLocation m_sourceLocation;
+ UsingMembers m_usingMembers;
+
+ mutable AbstractMetaClass::CppWrapper m_cachedWrapper;
+ AbstractMetaClass::Attributes m_attributes;
+
+ bool m_stream = false;
+ uint m_toStringCapabilityIndirections = 0;
+};
+
+AbstractMetaClass::AbstractMetaClass() : d(new AbstractMetaClassPrivate)
+{
+}
+
+AbstractMetaClass::~AbstractMetaClass() = default;
+
+AbstractMetaClass::Attributes AbstractMetaClass::attributes() const
+{
+ return d->m_attributes;
+}
+
+void AbstractMetaClass::setAttributes(Attributes attributes)
+{
+ d->m_attributes = attributes;
+}
+
+void AbstractMetaClass::operator+=(AbstractMetaClass::Attribute attribute)
+{
+ d->m_attributes.setFlag(attribute);
+}
+
+void AbstractMetaClass::operator-=(AbstractMetaClass::Attribute attribute)
+{
+ d->m_attributes.setFlag(attribute, false);
+}
+
+bool AbstractMetaClass::isPolymorphic() const
+{
+ return d->m_isPolymorphic;
+}
+
+/*******************************************************************************
+ * Returns a list of all the functions with a given name
+ */
+AbstractMetaFunctionCList AbstractMetaClass::queryFunctionsByName(const QString &name) const
+{
+ AbstractMetaFunctionCList returned;
+ for (const auto &function : d->m_functions) {
+ if (function->name() == name)
+ returned.append(function);
+ }
+
+ return returned;
+}
+
+/*******************************************************************************
+ * Returns a list of all the functions retrieved during parsing which should
+ * be added to the API.
+ */
+AbstractMetaFunctionCList AbstractMetaClass::functionsInTargetLang() const
+{
+ FunctionQueryOptions default_flags = FunctionQueryOption::NormalFunctions
+ | FunctionQueryOption::Visible | FunctionQueryOption::NotRemoved;
+
+ // Constructors
+ AbstractMetaFunctionCList returned = queryFunctions(FunctionQueryOption::AnyConstructor
+ | default_flags);
+
+ returned += queryFunctions(FunctionQueryOption::NonStaticFunctions
+ | default_flags);
+
+ // Static functions
+ returned += queryFunctions(FunctionQueryOption::StaticFunctions
+ | default_flags);
+
+ // Empty, private functions, since they aren't caught by the other ones
+ returned += queryFunctions(FunctionQueryOption::Empty | FunctionQueryOption::Invisible);
+
+ return returned;
+}
+
+AbstractMetaFunctionCList AbstractMetaClass::implicitConversions() const
+{
+ if (!isCopyConstructible() && !hasExternalConversionOperators())
+ return {};
+
+ AbstractMetaFunctionCList returned;
+ const auto list = queryFunctions(FunctionQueryOption::Constructors) + externalConversionOperators();
+
+ // Exclude anything that uses rvalue references, be it a move
+ // constructor "QPolygon(QPolygon &&)" or something else like
+ // "QPolygon(QVector<QPoint> &&)".
+ for (const auto &f : list) {
+ if ((f->actualMinimumArgumentCount() == 1 || f->arguments().size() == 1 || f->isConversionOperator())
+ && !f->isExplicit()
+ && !f->usesRValueReferences()
+ && !f->isModifiedRemoved()
+ && f->wasPublic()) {
+ returned += f;
+ }
+ }
+ return returned;
+}
+
+AbstractMetaFunctionCList AbstractMetaClass::operatorOverloads(OperatorQueryOptions query) const
+{
+ const auto &list = queryFunctions(FunctionQueryOption::OperatorOverloads
+ | FunctionQueryOption::Visible);
+ AbstractMetaFunctionCList returned;
+ for (const auto &f : list) {
+ if (f->matches(query))
+ returned += f;
+ }
+
+ return returned;
+}
+
+bool AbstractMetaClass::hasArithmeticOperatorOverload() const
+{
+ for (const auto & f: d->m_functions) {
+ if (f->ownerClass() == f->implementingClass() && f->isArithmeticOperator() && !f->isPrivate())
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaClass::hasIncDecrementOperatorOverload() const
+{
+ for (const auto & f: d->m_functions) {
+ if (f->ownerClass() == f->implementingClass()
+ && f->isIncDecrementOperator() && !f->isPrivate()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AbstractMetaClass::hasBitwiseOperatorOverload() const
+{
+ for (const auto & f: d->m_functions) {
+ if (f->ownerClass() == f->implementingClass() && f->isBitwiseOperator() && !f->isPrivate())
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaClass::hasComparisonOperatorOverload() const
+{
+ for (const auto &f : d->m_functions) {
+ if (f->ownerClass() == f->implementingClass() && f->isComparisonOperator() && !f->isPrivate())
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaClass::hasLogicalOperatorOverload() const
+{
+ for (const auto &f : d->m_functions) {
+ if (f->ownerClass() == f->implementingClass() && f->isLogicalOperator() && !f->isPrivate())
+ return true;
+ }
+ return false;
+}
+
+const AbstractMetaFieldList &AbstractMetaClass::fields() const
+{
+ return d->m_fields;
+}
+
+AbstractMetaFieldList &AbstractMetaClass::fields()
+{
+ return d->m_fields;
+}
+
+void AbstractMetaClass::setFields(const AbstractMetaFieldList &fields)
+{
+ d->m_fields = fields;
+}
+
+void AbstractMetaClass::addField(const AbstractMetaField &field)
+{
+ d->m_fields << field;
+}
+
+bool AbstractMetaClass::hasStaticFields() const
+{
+ return std::any_of(d->m_fields.cbegin(), d->m_fields.cend(),
+ [](const AbstractMetaField &f) { return f.isStatic(); });
+}
+
+void AbstractMetaClass::sortFunctions()
+{
+ d->sortFunctions();
+}
+
+AbstractMetaClassCPtr AbstractMetaClass::templateBaseClass() const
+{
+ return d->m_templateBaseClass;
+}
+
+void AbstractMetaClass::setTemplateBaseClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_templateBaseClass = cls;
+}
+
+const AbstractMetaFunctionCList &AbstractMetaClass::functions() const
+{
+ return d->m_functions;
+}
+
+const AbstractMetaFunctionCList &AbstractMetaClass::userAddedPythonOverrides() const
+{
+ return d->m_userAddedPythonOverrides;
+}
+
+void AbstractMetaClassPrivate::sortFunctions()
+{
+ std::sort(m_functions.begin(), m_functions.end(), function_sorter);
+}
+
+void AbstractMetaClassPrivate::setFunctions(const AbstractMetaFunctionCList &functions,
+ const AbstractMetaClassCPtr &q)
+{
+ m_functions = functions;
+
+ // Functions must be sorted by name before next loop
+ sortFunctions();
+
+ for (const auto &f : std::as_const(m_functions)) {
+ std::const_pointer_cast<AbstractMetaFunction>(f)->setOwnerClass(q);
+ if (!f->isPublic())
+ m_hasNonpublic = true;
+ }
+}
+
+const QList<QPropertySpec> &AbstractMetaClass::propertySpecs() const
+{
+ return d->m_propertySpecs;
+}
+
+void AbstractMetaClass::addPropertySpec(const QPropertySpec &spec)
+{
+ d->m_propertySpecs << spec;
+}
+
+void AbstractMetaClass::setPropertyDocumentation(const QString &name, const Documentation &doc)
+{
+ const auto index = d->indexOfProperty(name);
+ if (index >= 0)
+ d->m_propertySpecs[index].setDocumentation(doc);
+}
+
+void AbstractMetaClassPrivate::addFunction(const AbstractMetaFunctionCPtr &function)
+{
+ Q_ASSERT(!function->signature().startsWith(u'('));
+
+ if (!function->isDestructor())
+ m_functions << function;
+ else
+ Q_ASSERT(false); //memory leak
+
+ m_hasVirtuals |= function->isVirtual();
+ m_isPolymorphic |= m_hasVirtuals;
+ m_hasNonpublic |= !function->isPublic();
+ m_hasNonPrivateConstructor |= !function->isPrivate()
+ && function->functionType() == AbstractMetaFunction::ConstructorFunction;
+}
+
+void AbstractMetaClass::addFunction(const AbstractMetaClassPtr &klass,
+ const AbstractMetaFunctionCPtr &function)
+{
+ auto nonConstF = std::const_pointer_cast<AbstractMetaFunction>(function);
+ nonConstF->setOwnerClass(klass);
+
+ // Set the default value of the declaring class. This may be changed
+ // in fixFunctions later on
+ nonConstF->setDeclaringClass(klass);
+
+ // Some of the queries below depend on the implementing class being set
+ // to function properly. Such as function modifications
+ nonConstF->setImplementingClass(klass);
+
+ if (function->isUserAddedPythonOverride()) {
+ nonConstF->setConstant(false);
+ nonConstF->setCppAttribute(FunctionAttribute::Static);
+ klass->d->m_userAddedPythonOverrides.append(function);
+ } else {
+ klass->d->addFunction(function);
+ }
+}
+
+bool AbstractMetaClass::hasSignal(const AbstractMetaFunction *other) const
+{
+ if (!other->isSignal())
+ return false;
+
+ for (const auto &f : d->m_functions) {
+ if (f->isSignal() && f->compareTo(other) & AbstractMetaFunction::EqualName)
+ return other->modifiedName() == f->modifiedName();
+ }
+
+ return false;
+}
+
+
+QString AbstractMetaClass::name() const
+{
+ return d->m_typeEntry->targetLangEntryName();
+}
+
+const Documentation &AbstractMetaClass::documentation() const
+{
+ return d->m_doc;
+}
+
+void AbstractMetaClass::setDocumentation(const Documentation &doc)
+{
+ d->m_doc = doc;
+}
+
+QString AbstractMetaClass::baseClassName() const
+{
+ return d->m_baseClasses.isEmpty() ? QString() : d->m_baseClasses.constFirst()->name();
+}
+
+// Attribute "default-superclass"
+AbstractMetaClassCPtr AbstractMetaClass::defaultSuperclass() const
+{
+ return d->m_defaultSuperclass;
+}
+
+void AbstractMetaClass::setDefaultSuperclass(const AbstractMetaClassPtr &s)
+{
+ d->m_defaultSuperclass = s;
+}
+
+AbstractMetaClassCPtr AbstractMetaClass::baseClass() const
+{
+ return d->m_baseClasses.value(0, nullptr);
+}
+
+const AbstractMetaClassCList &AbstractMetaClass::baseClasses() const
+{
+ Q_ASSERT(inheritanceDone() || !needsInheritanceSetup());
+ return d->m_baseClasses;
+}
+
+// base classes including "defaultSuperclass".
+AbstractMetaClassCList AbstractMetaClass::typeSystemBaseClasses() const
+{
+ AbstractMetaClassCList result = d->m_baseClasses;
+ if (d->m_defaultSuperclass) {
+ result.removeAll(d->m_defaultSuperclass);
+ result.prepend(d->m_defaultSuperclass);
+ }
+ return result;
+}
+
+// Recursive list of all base classes including defaultSuperclass
+AbstractMetaClassCList AbstractMetaClass::allTypeSystemAncestors() const
+{
+ AbstractMetaClassCList result;
+ const auto baseClasses = typeSystemBaseClasses();
+ for (const auto &base : baseClasses) {
+ result.append(base);
+ result.append(base->allTypeSystemAncestors());
+ }
+ return result;
+}
+
+void AbstractMetaClass::addBaseClass(const AbstractMetaClassCPtr &baseClass)
+{
+ Q_ASSERT(baseClass);
+ d->m_baseClasses.append(baseClass);
+ d->m_isPolymorphic |= baseClass->isPolymorphic();
+}
+
+void AbstractMetaClass::setBaseClass(const AbstractMetaClassCPtr &baseClass)
+{
+ if (baseClass) {
+ d->m_baseClasses.prepend(baseClass);
+ d->m_isPolymorphic |= baseClass->isPolymorphic();
+ }
+}
+
+AbstractMetaClassCPtr AbstractMetaClass::extendedNamespace() const
+{
+ return d->m_extendedNamespace;
+}
+
+void AbstractMetaClass::setExtendedNamespace(const AbstractMetaClassCPtr &e)
+{
+ d->m_extendedNamespace = e;
+}
+
+const AbstractMetaClassCList &AbstractMetaClass::innerClasses() const
+{
+ return d->m_innerClasses;
+}
+
+void AbstractMetaClass::addInnerClass(const AbstractMetaClassPtr &cl)
+{
+ d->m_innerClasses << cl;
+}
+
+void AbstractMetaClass::setInnerClasses(const AbstractMetaClassCList &innerClasses)
+{
+ d->m_innerClasses = innerClasses;
+}
+
+QString AbstractMetaClass::package() const
+{
+ return d->m_typeEntry->targetLangPackage();
+}
+
+bool AbstractMetaClass::isNamespace() const
+{
+ return d->m_typeEntry->isNamespace();
+}
+
+// Is an invisible namespaces whose functions/enums
+// should be mapped to the global space.
+bool AbstractMetaClass::isInvisibleNamespace() const
+{
+ return d->m_typeEntry->isNamespace() && d->m_typeEntry->generateCode()
+ && !NamespaceTypeEntry::isVisibleScope(d->m_typeEntry);
+}
+
+bool AbstractMetaClass::isInlineNamespace() const
+{
+ bool result = false;
+ if (d->m_typeEntry->isNamespace()) {
+ const auto nte = std::static_pointer_cast<const NamespaceTypeEntry>(d->m_typeEntry);
+ result = nte->isInlineNamespace();
+ }
+ return result;
+}
+
+bool AbstractMetaClass::isQtNamespace() const
+{
+ return isNamespace() && name() == u"Qt";
+}
+
+QString AbstractMetaClass::qualifiedCppName() const
+{
+ return d->m_typeEntry->qualifiedCppName();
+}
+
+bool AbstractMetaClass::hasFunction(const QString &str) const
+{
+ return bool(findFunction(str));
+}
+
+AbstractMetaFunctionCPtr AbstractMetaClass::findFunction(QAnyStringView functionName) const
+{
+ return AbstractMetaFunction::find(d->m_functions, functionName);
+}
+
+AbstractMetaFunctionCList AbstractMetaClass::findFunctions(QAnyStringView functionName) const
+{
+ AbstractMetaFunctionCList result;
+ std::copy_if(d->m_functions.cbegin(), d->m_functions.cend(),
+ std::back_inserter(result),
+ [&functionName](const AbstractMetaFunctionCPtr &f) {
+ return f->name() == functionName;
+ });
+ return result;
+}
+
+AbstractMetaFunctionCPtr AbstractMetaClass::findOperatorBool() const
+{
+ auto it = std::find_if(d->m_functions.cbegin(), d->m_functions.cend(),
+ [](const AbstractMetaFunctionCPtr &f) {
+ return f->isOperatorBool();
+ });
+ if (it == d->m_functions.cend())
+ return {};
+ return *it;
+}
+
+AbstractMetaFunctionCPtr AbstractMetaClass::findQtIsNullMethod() const
+{
+ auto it = std::find_if(d->m_functions.cbegin(), d->m_functions.cend(),
+ [](const AbstractMetaFunctionCPtr &f) {
+ return f->isQtIsNullMethod();
+ });
+ if (it == d->m_functions.cend())
+ return {};
+ return *it;
+}
+
+bool AbstractMetaClass::hasProtectedFields() const
+{
+ for (const AbstractMetaField &field : d->m_fields) {
+ if (field.isProtected())
+ return true;
+ }
+ return false;
+}
+
+const TypeEntryCList &AbstractMetaClass::templateArguments() const
+{
+ return d->m_templateArgs;
+}
+
+void AbstractMetaClass::setTemplateArguments(const TypeEntryCList &args)
+{
+ d->m_templateArgs = args;
+}
+
+const QStringList &AbstractMetaClass::baseClassNames() const
+{
+ return d->m_baseClassNames;
+}
+
+void AbstractMetaClass::setBaseClassNames(const QStringList &names)
+{
+ d->m_baseClassNames = names;
+}
+
+ComplexTypeEntryCPtr AbstractMetaClass::typeEntry() const
+{
+ return d->m_typeEntry;
+}
+
+ComplexTypeEntryPtr AbstractMetaClass::typeEntry()
+{
+ return d->m_typeEntry;
+}
+
+void AbstractMetaClass::setTypeEntry(const ComplexTypeEntryPtr &type)
+{
+ d->m_typeEntry = type;
+}
+
+QString AbstractMetaClass::hashFunction() const
+{
+ return d->m_hashFunction;
+}
+
+void AbstractMetaClass::setHashFunction(const QString &f)
+{
+ d->m_hashFunction = f;
+}
+
+bool AbstractMetaClass::hasHashFunction() const
+{
+ return !d->m_hashFunction.isEmpty();
+}
+
+// Search whether a functions is a property setter/getter/reset
+AbstractMetaClass::PropertyFunctionSearchResult
+ AbstractMetaClass::searchPropertyFunction(const QString &name) const
+{
+ for (qsizetype i = 0, size = d->m_propertySpecs.size(); i < size; ++i) {
+ const auto &propertySpec = d->m_propertySpecs.at(i);
+ if (name == propertySpec.read())
+ return PropertyFunctionSearchResult{i, PropertyFunction::Read};
+ if (name == propertySpec.write())
+ return PropertyFunctionSearchResult{i, PropertyFunction::Write};
+ if (name == propertySpec.reset())
+ return PropertyFunctionSearchResult{i, PropertyFunction::Reset};
+ if (name == propertySpec.notify())
+ return PropertyFunctionSearchResult{i, PropertyFunction::Notify};
+ }
+ return PropertyFunctionSearchResult{-1, PropertyFunction::Read};
+}
+
+std::optional<QPropertySpec>
+ AbstractMetaClass::propertySpecByName(const QString &name) const
+{
+ const auto index = d->indexOfProperty(name);
+ if (index >= 0)
+ return d->m_propertySpecs.at(index);
+ return {};
+}
+
+const AbstractMetaFunctionCList &AbstractMetaClass::externalConversionOperators() const
+{
+ return d->m_externalConversionOperators;
+}
+
+void AbstractMetaClass::addExternalConversionOperator(const AbstractMetaFunctionCPtr &conversionOp)
+{
+ if (!d->m_externalConversionOperators.contains(conversionOp))
+ d->m_externalConversionOperators.append(conversionOp);
+}
+
+bool AbstractMetaClass::hasExternalConversionOperators() const
+{
+ return !d->m_externalConversionOperators.isEmpty();
+}
+
+bool AbstractMetaClass::hasTemplateBaseClassInstantiations() const
+{
+ return d->m_templateBaseClass != nullptr && !d->m_baseTemplateInstantiations.isEmpty();
+}
+
+const AbstractMetaTypeList &AbstractMetaClass::templateBaseClassInstantiations() const
+{
+ return d->m_baseTemplateInstantiations;
+}
+
+void AbstractMetaClass::setTemplateBaseClassInstantiations(const AbstractMetaTypeList &instantiations)
+{
+ Q_ASSERT(d->m_templateBaseClass != nullptr);
+ d->m_baseTemplateInstantiations = instantiations;
+}
+
+void AbstractMetaClass::setTypeDef(bool typeDef)
+{
+ d->m_isTypeDef = typeDef;
+}
+
+bool AbstractMetaClass::isTypeDef() const
+{
+ return d->m_isTypeDef;
+}
+
+bool AbstractMetaClass::isStream() const
+{
+ return d->m_stream;
+}
+
+void AbstractMetaClass::setStream(bool stream)
+{
+ d->m_stream = stream;
+}
+
+bool AbstractMetaClass::hasToStringCapability() const
+{
+ return d->m_hasToStringCapability;
+}
+
+void AbstractMetaClass::setToStringCapability(bool value, uint indirections)
+{
+ d->m_hasToStringCapability = value;
+ d->m_toStringCapabilityIndirections = indirections;
+}
+
+uint AbstractMetaClass::toStringCapabilityIndirections() const
+{
+ return d->m_toStringCapabilityIndirections;
+}
+
+// Does any of the base classes require deletion in the main thread?
+bool AbstractMetaClass::deleteInMainThread() const
+{
+ return typeEntry()->deleteInMainThread()
+ || (!d->m_baseClasses.isEmpty() && d->m_baseClasses.constFirst()->deleteInMainThread());
+}
+
+bool AbstractMetaClassPrivate::hasConstructors() const
+{
+ return AbstractMetaClass::queryFirstFunction(m_functions,
+ FunctionQueryOption::AnyConstructor) != nullptr;
+}
+
+qsizetype AbstractMetaClassPrivate::indexOfProperty(const QString &name) const
+{
+ for (qsizetype i = 0; i < m_propertySpecs.size(); ++i) {
+ if (m_propertySpecs.at(i).name() == name)
+ return i;
+ }
+ return -1;
+}
+
+bool AbstractMetaClass::hasConstructors() const
+{
+ return d->hasConstructors();
+}
+
+AbstractMetaFunctionCPtr AbstractMetaClass::copyConstructor() const
+{
+ for (const auto &f : d->m_functions) {
+ if (f->functionType() == AbstractMetaFunction::CopyConstructorFunction)
+ return f;
+ }
+ return {};
+}
+
+bool AbstractMetaClass::hasCopyConstructor() const
+{
+ return copyConstructor() != nullptr;
+}
+
+bool AbstractMetaClass::hasPrivateCopyConstructor() const
+{
+ const auto copyCt = copyConstructor();
+ return copyCt && copyCt->isPrivate();
+}
+
+void AbstractMetaClassPrivate::addConstructor(AbstractMetaFunction::FunctionType t,
+ Access access,
+ const AbstractMetaArgumentList &arguments,
+ const AbstractMetaClassPtr &q)
+{
+ auto *f = createFunction(q->name(), t, access, arguments, AbstractMetaType::createVoid(), q);
+ if (access != Access::Private)
+ m_hasNonPrivateConstructor = true;
+ f->setAttributes(AbstractMetaFunction::AddedMethod);
+ addFunction(AbstractMetaFunctionCPtr(f));
+}
+
+void AbstractMetaClass::addDefaultConstructor(const AbstractMetaClassPtr &klass)
+{
+ klass->d->addConstructor(AbstractMetaFunction::ConstructorFunction,
+ Access::Public, {}, klass);
+}
+
+void AbstractMetaClass::addDefaultCopyConstructor(const AbstractMetaClassPtr &klass)
+{
+ AbstractMetaType argType(klass->typeEntry());
+ argType.setReferenceType(LValueReference);
+ argType.setConstant(true);
+ argType.setTypeUsagePattern(AbstractMetaType::ValuePattern);
+
+ AbstractMetaArgument arg;
+ arg.setType(argType);
+ arg.setName(klass->name());
+
+ klass->d->addConstructor(AbstractMetaFunction::CopyConstructorFunction,
+ Access::Public, {arg}, klass);
+}
+
+AbstractMetaFunction *
+ AbstractMetaClassPrivate::createFunction(const QString &name,
+ AbstractMetaFunction::FunctionType t,
+ Access access,
+ const AbstractMetaArgumentList &arguments,
+ const AbstractMetaType &returnType,
+ const AbstractMetaClassPtr &q)
+{
+ auto *f = new AbstractMetaFunction(name);
+ f->setType(returnType);
+ f->setOwnerClass(q);
+ f->setFunctionType(t);
+ f->setArguments(arguments);
+ f->setDeclaringClass(q);
+ f->setAccess(access);
+ f->setImplementingClass(q);
+ return f;
+}
+
+static AbstractMetaType boolType()
+{
+ auto boolType = TypeDatabase::instance()->findType(u"bool"_s);
+ Q_ASSERT(boolType);
+ AbstractMetaType result(boolType);
+ result.decideUsagePattern();
+ return result;
+}
+
+// Helper to synthesize comparison operators from a spaceship operator. Since
+// shiboken also generates code for comparing to different types, this fits
+// better than of handling it in the generator code.
+void AbstractMetaClass::addSynthesizedComparisonOperators(const AbstractMetaClassPtr &c)
+{
+ static const auto returnType = boolType();
+
+ AbstractMetaType selfType(c->typeEntry());
+ selfType.setConstant(true);
+ selfType.setReferenceType(LValueReference);
+ selfType.decideUsagePattern();
+ AbstractMetaArgument selfArgument;
+ selfArgument.setType(selfType);
+ selfArgument.setName(u"rhs"_s);
+ AbstractMetaArgumentList arguments(1, selfArgument);
+
+ static const char *operators[]
+ = {"operator==", "operator!=", "operator<", "operator<=", "operator>", "operator>="};
+ for (auto *op : operators) {
+ auto *f = AbstractMetaClassPrivate::createFunction(QLatin1StringView(op),
+ AbstractMetaFunction::ComparisonOperator,
+ Access::Public, arguments,
+ returnType, c);
+ c->d->addFunction(AbstractMetaFunctionCPtr(f));
+ }
+}
+
+bool AbstractMetaClass::hasNonPrivateConstructor() const
+{
+ return d->m_hasNonPrivateConstructor;
+}
+
+void AbstractMetaClass::setHasNonPrivateConstructor(bool value)
+{
+ d->m_hasNonPrivateConstructor = value;
+}
+
+bool AbstractMetaClass::hasPrivateConstructor() const
+{
+ return d->m_hasPrivateConstructor;
+}
+
+void AbstractMetaClass::setHasPrivateConstructor(bool value)
+{
+ d->m_hasPrivateConstructor = value;
+}
+
+bool AbstractMetaClass::hasDeletedDefaultConstructor() const
+{
+ return d->m_hasDeletedDefaultConstructor;
+}
+
+void AbstractMetaClass::setHasDeletedDefaultConstructor(bool value)
+{
+ d->m_hasDeletedDefaultConstructor = value;
+}
+
+bool AbstractMetaClass::hasDeletedCopyConstructor() const
+{
+ return d->m_hasDeletedCopyConstructor;
+}
+
+void AbstractMetaClass::setHasDeletedCopyConstructor(bool value)
+{
+ d->m_hasDeletedCopyConstructor = value;
+}
+
+bool AbstractMetaClass::hasPrivateDestructor() const
+{
+ return d->m_hasPrivateDestructor;
+}
+
+void AbstractMetaClass::setHasPrivateDestructor(bool value)
+{
+ d->m_hasPrivateDestructor = value;
+}
+
+bool AbstractMetaClass::hasProtectedDestructor() const
+{
+ return d->m_hasProtectedDestructor;
+}
+
+void AbstractMetaClass::setHasProtectedDestructor(bool value)
+{
+ d->m_hasProtectedDestructor = value;
+}
+
+bool AbstractMetaClass::hasVirtualDestructor() const
+{
+ return d->m_hasVirtualDestructor;
+}
+
+void AbstractMetaClass::setHasVirtualDestructor(bool value)
+{
+ d->m_hasVirtualDestructor = value;
+ if (value)
+ d->m_hasVirtuals = d->m_isPolymorphic = 1;
+}
+
+bool AbstractMetaClass::isDefaultConstructible() const
+{
+ // Private constructors are skipped by the builder.
+ if (hasDeletedDefaultConstructor() || hasPrivateConstructor())
+ return false;
+ const AbstractMetaFunctionCList ctors =
+ queryFunctions(FunctionQueryOption::Constructors);
+ for (const auto &ct : ctors) {
+ if (ct->isDefaultConstructor())
+ return ct->isPublic();
+ }
+ return ctors.isEmpty() && isImplicitlyDefaultConstructible();
+}
+
+// Non-comprehensive check for default constructible field
+// (non-ref or not const value).
+static bool defaultConstructibleField(const AbstractMetaField &f)
+{
+ if (f.isStatic())
+ return true;
+ const auto &type = f.type();
+ return type.referenceType() == NoReference
+ && !(type.indirections() == 0 && type.isConstant()); // no const values
+}
+
+bool AbstractMetaClass::isImplicitlyDefaultConstructible() const
+{
+ return std::all_of(d->m_fields.cbegin(), d->m_fields.cend(),
+ defaultConstructibleField)
+ && std::all_of(d->m_baseClasses.cbegin(), d->m_baseClasses.cend(),
+ [] (const AbstractMetaClassCPtr &c) {
+ return c->isDefaultConstructible();
+ });
+}
+
+static bool canAddDefaultConstructorHelper(const AbstractMetaClass *cls)
+{
+ return !cls->isNamespace()
+ && !cls->hasDeletedDefaultConstructor()
+ && !cls->attributes().testFlag(AbstractMetaClass::HasRejectedConstructor)
+ && !cls->hasPrivateDestructor();
+}
+
+bool AbstractMetaClass::canAddDefaultConstructor() const
+{
+ return canAddDefaultConstructorHelper(this) && !hasConstructors()
+ && !hasPrivateConstructor() && isImplicitlyDefaultConstructible();
+}
+
+bool AbstractMetaClass::isCopyConstructible() const
+{
+ // Private constructors are skipped by the builder.
+ if (hasDeletedCopyConstructor() || hasPrivateCopyConstructor())
+ return false;
+ const AbstractMetaFunctionCList copyCtors =
+ queryFunctions(FunctionQueryOption::CopyConstructor);
+ return copyCtors.isEmpty()
+ ? isImplicitlyCopyConstructible()
+ : copyCtors.constFirst()->isPublic();
+}
+
+bool AbstractMetaClass::isImplicitlyCopyConstructible() const
+{
+ // Fields are currently not considered
+ return std::all_of(d->m_baseClasses.cbegin(), d->m_baseClasses.cend(),
+ [] (const AbstractMetaClassCPtr &c) {
+ return c->isCopyConstructible();
+ });
+}
+
+bool AbstractMetaClass::canAddDefaultCopyConstructor() const
+{
+ if (!canAddDefaultConstructorHelper(this)
+ || !d->m_typeEntry->isValue() || isAbstract()
+ || hasPrivateCopyConstructor() || hasCopyConstructor()) {
+ return false;
+ }
+ return isImplicitlyCopyConstructible();
+}
+
+static bool classHasParentManagement(const AbstractMetaClassCPtr &c)
+{
+ const auto flags = c->typeEntry()->typeFlags();
+ return flags.testFlag(ComplexTypeEntry::ParentManagement);
+}
+
+TypeEntryCPtr parentManagementEntry(const AbstractMetaClassCPtr &klass)
+{
+ if (klass->typeEntry()->isObject()) {
+ if (auto c = recurseClassHierarchy(klass, classHasParentManagement))
+ return c->typeEntry();
+ }
+ return nullptr;
+}
+
+bool AbstractMetaClass::generateExceptionHandling() const
+{
+ return queryFirstFunction(d->m_functions, FunctionQueryOption::Visible
+ | FunctionQueryOption::GenerateExceptionHandling) != nullptr;
+}
+
+static bool needsProtectedWrapper(const AbstractMetaFunctionCPtr &func)
+{
+ return func->isProtected()
+ && !(func->isSignal() || func->isModifiedRemoved())
+ && !func->isOperatorOverload();
+}
+
+static AbstractMetaClass::CppWrapper determineCppWrapper(const AbstractMetaClass *metaClass)
+{
+
+ AbstractMetaClass::CppWrapper result;
+
+ if (metaClass->isNamespace()
+ || metaClass->attributes().testFlag(AbstractMetaClass::FinalCppClass)
+ || metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) {
+ return result;
+ }
+
+#ifndef Q_CC_MSVC
+ // PYSIDE-504: When C++ 11 is used, then the destructor must always be
+ // declared. Only MSVC can handle this, the others generate a link error.
+ // See also HeaderGenerator::generateClass().
+ if (metaClass->hasPrivateDestructor())
+ return result;
+#endif
+
+ // Need checking for Python overrides?
+ if (metaClass->isPolymorphic())
+ result |= AbstractMetaClass::CppVirtualMethodWrapper;
+
+ // Is there anything protected that needs to be made accessible?
+ if (metaClass->hasProtectedFields() || metaClass->hasProtectedDestructor()
+ || std::any_of(metaClass->functions().cbegin(), metaClass->functions().cend(),
+ needsProtectedWrapper)) {
+ result |= AbstractMetaClass::CppProtectedHackWrapper;
+ }
+ return result;
+}
+
+AbstractMetaClass::CppWrapper AbstractMetaClass::cppWrapper() const
+{
+ if (!d->m_hasCachedWrapper) {
+ d->m_cachedWrapper = determineCppWrapper(this);
+ d->m_hasCachedWrapper = true;
+ }
+ return d->m_cachedWrapper;
+}
+
+const UsingMembers &AbstractMetaClass::usingMembers() const
+{
+ return d->m_usingMembers;
+}
+
+void AbstractMetaClass::addUsingMember(const UsingMember &um)
+{
+ d->m_usingMembers.append(um);
+}
+
+bool AbstractMetaClassPrivate::isUsingMember(const AbstractMetaClassCPtr &c,
+ const QString &memberName,
+ Access minimumAccess) const
+{
+ auto it = std::find_if(m_usingMembers.cbegin(), m_usingMembers.cend(),
+ [c, &memberName](const UsingMember &um) {
+ return um.baseClass == c && um.memberName == memberName;
+ });
+ return it != m_usingMembers.cend() && it->access >= minimumAccess;
+}
+
+bool AbstractMetaClass::isUsingMember(const AbstractMetaClassCPtr &c,
+ const QString &memberName,
+ Access minimumAccess) const
+{
+ return d->isUsingMember(c, memberName, minimumAccess);
+}
+
+bool AbstractMetaClass::hasUsingMemberFor(const QString &memberName) const
+{
+ return std::any_of(d->m_usingMembers.cbegin(), d->m_usingMembers.cend(),
+ [&memberName](const UsingMember &um) {
+ return um.memberName == memberName;
+ });
+}
+
+/* Goes through the list of functions and returns a list of all
+ functions matching all of the criteria in \a query.
+ */
+
+bool AbstractMetaClass::queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query)
+{
+ if ((query.testFlag(FunctionQueryOption::NotRemoved))) {
+ if (f->isModifiedRemoved())
+ return false;
+ if (f->isVirtual() && f->isModifiedRemoved(f->declaringClass()))
+ return false;
+ }
+
+ if (query.testFlag(FunctionQueryOption::Visible) && f->isPrivate())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::Invisible) && !f->isPrivate())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::Empty) && !f->isEmptyFunction())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::ClassImplements) && f->ownerClass() != f->implementingClass())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::VirtualInCppFunctions) && !f->isVirtual())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::Signals) && (!f->isSignal()))
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::AnyConstructor)
+ && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) {
+ return false;
+ }
+
+ if (query.testFlag(FunctionQueryOption::Constructors)
+ && (f->functionType() != AbstractMetaFunction::ConstructorFunction
+ || f->ownerClass() != f->implementingClass())) {
+ return false;
+ }
+
+ if (query.testFlag(FunctionQueryOption::CopyConstructor)
+ && (!f->isCopyConstructor() || f->ownerClass() != f->implementingClass())) {
+ return false;
+ }
+
+ // Destructors are never included in the functions of a class currently
+ /*
+ if ((query & Destructors) && (!f->isDestructor()
+ || f->ownerClass() != f->implementingClass())
+ || f->isDestructor() && (query & Destructors) == 0) {
+ return false;
+ }*/
+
+ if (query.testFlag(FunctionQueryOption::StaticFunctions) && (!f->isStatic() || f->isSignal()))
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::NonStaticFunctions) && (f->isStatic()))
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::NormalFunctions) && (f->isSignal()))
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::OperatorOverloads) && !f->isOperatorOverload())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::GenerateExceptionHandling) && !f->generateExceptionHandling())
+ return false;
+
+ if (query.testFlag(FunctionQueryOption::GetAttroFunction)
+ && f->functionType() != AbstractMetaFunction::GetAttroFunction) {
+ return false;
+ }
+
+ if (query.testFlag(FunctionQueryOption::SetAttroFunction)
+ && f->functionType() != AbstractMetaFunction::SetAttroFunction) {
+ return false;
+ }
+
+ return true;
+}
+
+AbstractMetaFunctionCList AbstractMetaClass::queryFunctionList(const AbstractMetaFunctionCList &list,
+ FunctionQueryOptions query)
+{
+ AbstractMetaFunctionCList result;
+ for (const auto &f : list) {
+ if (queryFunction(f.get(), query))
+ result.append(f);
+ }
+ return result;
+}
+
+AbstractMetaFunctionCPtr AbstractMetaClass::queryFirstFunction(const AbstractMetaFunctionCList &list,
+ FunctionQueryOptions query)
+{
+ for (const auto &f : list) {
+ if (queryFunction(f.get(), query))
+ return f;
+ }
+ return {};
+}
+
+AbstractMetaFunctionCList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const
+{
+ return AbstractMetaClass::queryFunctionList(d->m_functions, query);
+}
+
+bool AbstractMetaClass::hasSignals() const
+{
+ return queryFirstFunction(d->m_functions,
+ FunctionQueryOption::Signals
+ | FunctionQueryOption::Visible
+ | FunctionQueryOption::NotRemoved) != nullptr;
+}
+
+AbstractMetaFunctionCList AbstractMetaClass::cppSignalFunctions() const
+{
+ return queryFunctions(FunctionQueryOption::Signals
+ | FunctionQueryOption::Visible
+ | FunctionQueryOption::NotRemoved);
+}
+
+std::optional<AbstractMetaField>
+ AbstractMetaClass::findField(QStringView name) const
+{
+ return AbstractMetaField::find(d->m_fields, name);
+}
+
+const AbstractMetaEnumList &AbstractMetaClass::enums() const
+{
+ return d->m_enums;
+}
+
+AbstractMetaEnumList &AbstractMetaClass::enums()
+{
+ return d->m_enums;
+}
+
+void AbstractMetaClass::setEnums(const AbstractMetaEnumList &enums)
+{
+ d->m_enums = enums;
+}
+
+void AbstractMetaClass::addEnum(const AbstractMetaEnum &e)
+{
+ d->m_enums << e;
+}
+
+std::optional<AbstractMetaEnum>
+ AbstractMetaClass::findEnum(const QString &enumName) const
+{
+ for (const auto &e : d->m_enums) {
+ if (e.name() == enumName)
+ return e;
+ }
+ return {};
+}
+
+/*! Recursively searches for the enum value named \a enumValueName in
+ this class and its superclasses and interfaces.
+*/
+std::optional<AbstractMetaEnumValue>
+ AbstractMetaClass::findEnumValue(const QString &enumValueName) const
+{
+ for (const AbstractMetaEnum &e : std::as_const(d->m_enums)) {
+ auto v = e.findEnumValue(enumValueName);
+ if (v.has_value())
+ return v;
+ }
+ if (baseClass())
+ return baseClass()->findEnumValue(enumValueName);
+
+ return {};
+}
+
+void AbstractMetaClass::getEnumsToBeGenerated(AbstractMetaEnumList *enumList) const
+{
+ for (const AbstractMetaEnum &metaEnum : d->m_enums) {
+ if (!metaEnum.isPrivate() && metaEnum.typeEntry()->generateCode())
+ enumList->append(metaEnum);
+ }
+}
+
+void AbstractMetaClass::getEnumsFromInvisibleNamespacesToBeGenerated(AbstractMetaEnumList *enumList) const
+{
+ if (isNamespace()) {
+ invisibleNamespaceRecursion([enumList](const AbstractMetaClassCPtr &c) {
+ c->getEnumsToBeGenerated(enumList);
+ });
+ }
+}
+
+void AbstractMetaClass::getFunctionsFromInvisibleNamespacesToBeGenerated(AbstractMetaFunctionCList *funcList) const
+{
+ if (isNamespace()) {
+ invisibleNamespaceRecursion([funcList](const AbstractMetaClassCPtr &c) {
+ funcList->append(c->functions());
+ });
+ }
+}
+
+QString AbstractMetaClass::fullName() const
+{
+ return package() + u'.' + d->m_typeEntry->targetLangName();
+}
+
+static void addExtraIncludeForType(const AbstractMetaClassPtr &metaClass,
+ const AbstractMetaType &type)
+{
+
+ Q_ASSERT(metaClass);
+ const auto entry = type.typeEntry();
+
+ if (entry && entry->include().isValid()) {
+ const auto class_entry = metaClass->typeEntry();
+ class_entry->addArgumentInclude(entry->include());
+ }
+
+ if (type.hasInstantiations()) {
+ for (const AbstractMetaType &instantiation : type.instantiations())
+ addExtraIncludeForType(metaClass, instantiation);
+ }
+}
+
+static void addExtraIncludesForFunction(const AbstractMetaClassPtr &metaClass,
+ const AbstractMetaFunctionCPtr &meta_function)
+{
+ Q_ASSERT(metaClass);
+ Q_ASSERT(meta_function);
+ addExtraIncludeForType(metaClass, meta_function->type());
+
+ const AbstractMetaArgumentList &arguments = meta_function->arguments();
+ for (const AbstractMetaArgument &argument : arguments) {
+ const auto &type = argument.type();
+ addExtraIncludeForType(metaClass, type);
+ if (argument.modifiedType() != type)
+ addExtraIncludeForType(metaClass, argument.modifiedType());
+ }
+}
+
+static bool addSuperFunction(const AbstractMetaFunctionCPtr &f)
+{
+ switch (f->functionType()) {
+ case AbstractMetaFunction::ConstructorFunction:
+ case AbstractMetaFunction::CopyConstructorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveAssignmentOperatorFunction:
+ case AbstractMetaFunction::DestructorFunction:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+// Add constructors imported via "using" from the base classes. This is not
+// needed for normal hidden inherited member functions since we generate a
+// cast to the base class to call them into binding code.
+void AbstractMetaClassPrivate::addUsingConstructors(const AbstractMetaClassPtr &q)
+{
+ // Restricted to the non-constructor case currently to avoid
+ // having to compare the parameter lists of existing constructors.
+ if (m_baseClasses.isEmpty() || m_usingMembers.isEmpty()
+ || hasConstructors()) {
+ return;
+ }
+
+ for (const auto &superClass : m_baseClasses) {
+ // Find any "using base-constructor" directives
+ if (isUsingMember(superClass, superClass->name(), Access::Protected)) {
+ // Add to derived class with parameter lists.
+ const auto ctors = superClass->queryFunctions(FunctionQueryOption::Constructors);
+ for (const auto &ctor : ctors) {
+ if (!ctor->isPrivate()) {
+ addConstructor(AbstractMetaFunction::ConstructorFunction,
+ ctor->access(), ctor->arguments(), q);
+ }
+ }
+ }
+ }
+}
+
+static inline bool isSignal(const AbstractMetaFunctionCPtr &f)
+{
+ return f->isSignal();
+}
+
+void AbstractMetaClass::fixFunctions(const AbstractMetaClassPtr &klass)
+{
+ auto *d = klass->d.data();
+ if (d->m_functionsFixed)
+ return;
+
+ d->m_functionsFixed = true;
+
+ AbstractMetaFunctionCList funcs = klass->functions();
+ AbstractMetaFunctionCList nonRemovedFuncs;
+ nonRemovedFuncs.reserve(funcs.size());
+
+ d->addUsingConstructors(klass);
+
+ for (const auto &f : std::as_const(funcs)) {
+ // Fishy: Setting up of implementing/declaring/base classes changes
+ // the applicable modifications; clear cached ones.
+ std::const_pointer_cast<AbstractMetaFunction>(f)->clearModificationsCache();
+ if (!f->isModifiedRemoved())
+ nonRemovedFuncs.append(f);
+ }
+
+ for (const auto &superClassC : d->m_baseClasses) {
+ for (const auto &pof : superClassC->userAddedPythonOverrides()) {
+ auto *clonedPof = pof->copy();
+ clonedPof->setOwnerClass(klass);
+ d->m_userAddedPythonOverrides.append(AbstractMetaFunctionCPtr{clonedPof});
+ }
+
+ auto superClass = std::const_pointer_cast<AbstractMetaClass>(superClassC);
+ AbstractMetaClass::fixFunctions(superClass);
+ // Since we always traverse the complete hierarchy we are only
+ // interrested in what each super class implements, not what
+ // we may have propagated from their base classes again.
+ AbstractMetaFunctionCList superFuncs;
+ superFuncs = superClass->queryFunctions(FunctionQueryOption::ClassImplements);
+ // We are not interested in signals as no bindings are generated for them;
+ // they cause documentation warnings.
+ superFuncs.erase(std::remove_if(superFuncs.begin(), superFuncs.end(), isSignal),
+ superFuncs.end());
+ const auto virtuals = superClass->queryFunctions(FunctionQueryOption::VirtualInCppFunctions);
+ superFuncs += virtuals;
+
+ QSet<AbstractMetaFunctionCPtr> funcsToAdd;
+ for (const auto &sf : std::as_const(superFuncs)) {
+ if (sf->isModifiedRemoved())
+ continue;
+
+ // skip functions added in base classes
+ if (sf->isUserAdded() && sf->declaringClass() != klass)
+ continue;
+
+ // Skip base class comparison operators declared as members (free
+ // operators are added later by traverseOperatorFunction().
+ if (sf->isComparisonOperator())
+ continue;
+
+ // we generally don't care about private functions, but we have to get the ones that are
+ // virtual in case they override abstract functions.
+ bool add = addSuperFunction(sf);
+ for (const auto &cf : std::as_const(nonRemovedFuncs)) {
+ AbstractMetaFunctionPtr f(std::const_pointer_cast<AbstractMetaFunction>(cf));
+ const AbstractMetaFunction::CompareResult cmp = cf->compareTo(sf.get());
+
+ if (cmp & AbstractMetaFunction::EqualModifiedName) {
+ add = false;
+ if (cmp & AbstractMetaFunction::EqualArguments) {
+ // Set "override" in case it was not spelled out (since it
+ // is then not detected by clang parsing).
+ const auto attributes = cf->cppAttributes();
+ if (attributes.testFlag(FunctionAttribute::Virtual)
+ && !attributes.testFlag(FunctionAttribute::Override)
+ && !attributes.testFlag(FunctionAttribute::Final)) {
+ f->setCppAttribute(FunctionAttribute::Override);
+ }
+
+ if (f->access() != sf->access()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgFunctionVisibilityModified(klass, f.get())));
+#if 0
+ // If new visibility is private, we can't
+ // do anything. If it isn't, then we
+ // prefer the parent class's visibility
+ // setting for the function.
+ if (!f->isPrivate() && !sf->isPrivate())
+ f->setVisibility(sf->visibility());
+#endif
+ // Private overrides of abstract functions have to go into the class or
+ // the subclasses will not compile as non-abstract classes.
+ // But they don't need to be implemented, since they can never be called.
+ if (f->isPrivate())
+ f->setFunctionType(AbstractMetaFunction::EmptyFunction);
+ }
+
+ // Set the class which first declares this function, afawk
+ f->setDeclaringClass(sf->declaringClass());
+ }
+
+ if (cmp & AbstractMetaFunction::EqualDefaultValueOverload) {
+ AbstractMetaArgumentList arguments;
+ if (f->arguments().size() < sf->arguments().size())
+ arguments = sf->arguments();
+ else
+ arguments = f->arguments();
+ //TODO: fix this
+ //for (int i=0; i<arguments.size(); ++i)
+ // arguments[i]->setDefaultValueExpression("<#>" + QString());
+ }
+
+
+ // Otherwise we have function shadowing and we can
+ // skip the thing...
+ } else if (cmp & AbstractMetaFunction::EqualName && !sf->isSignal()) {
+ // In the case of function shadowing where the function name has been altered to
+ // avoid conflict, we don't copy in the original.
+ add = false;
+ }
+ }
+
+ if (add)
+ funcsToAdd << sf;
+ }
+
+ for (const auto &f : std::as_const(funcsToAdd)) {
+ AbstractMetaFunction *copy = f->copy();
+ (*copy) += AbstractMetaFunction::AddedMethod;
+ funcs.append(AbstractMetaFunctionCPtr(copy));
+ }
+ }
+
+ bool hasPrivateConstructors = false;
+ bool hasPublicConstructors = false;
+ // Apply modifications after the declaring class has been set
+ for (const auto &func : std::as_const(funcs)) {
+ auto ncFunc = std::const_pointer_cast<AbstractMetaFunction>(func);
+ for (const auto &mod : func->modifications(klass)) {
+ if (mod.isRenameModifier())
+ ncFunc->setName(mod.renamedToName());
+ }
+ ncFunc->applyTypeModifications();
+
+ // Make sure class is abstract if one of the functions is
+ if (func->isAbstract()) {
+ (*klass) += AbstractMetaClass::Abstract;
+ (*klass) -= AbstractMetaClass::FinalInTargetLang;
+ }
+
+ if (func->isConstructor()) {
+ if (func->isPrivate())
+ hasPrivateConstructors = true;
+ else
+ hasPublicConstructors = true;
+ }
+
+
+
+ // Make sure that we include files for all classes that are in use
+ addExtraIncludesForFunction(klass, func);
+ }
+
+ if (hasPrivateConstructors && !hasPublicConstructors) {
+ (*klass) += AbstractMetaClass::Abstract;
+ (*klass) -= AbstractMetaClass::FinalInTargetLang;
+ }
+
+ d->setFunctions(funcs, klass);
+}
+
+bool AbstractMetaClass::needsInheritanceSetup() const
+{
+ if (d->m_typeEntry != nullptr) {
+ switch (d->m_typeEntry->type()) {
+ case TypeEntry::NamespaceType:
+ case TypeEntry::SmartPointerType:
+ case TypeEntry::ContainerType:
+ return false;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+void AbstractMetaClass::setInheritanceDone(bool b)
+{
+ d->m_inheritanceDone = b;
+}
+
+bool AbstractMetaClass::inheritanceDone() const
+{
+ return d->m_inheritanceDone;
+}
+
+/*******************************************************************************
+ * Other stuff...
+ */
+
+std::optional<AbstractMetaEnumValue>
+ AbstractMetaClass::findEnumValue(const AbstractMetaClassList &classes,
+ const QString &name)
+{
+ const auto lst = QStringView{name}.split(u"::");
+
+ if (lst.size() > 1) {
+ const auto &prefixName = lst.at(0);
+ const auto &enumName = lst.at(1);
+ if (auto cl = findClass(classes, prefixName))
+ return cl->findEnumValue(enumName.toString());
+ }
+
+ for (const auto &metaClass : classes) {
+ auto enumValue = metaClass->findEnumValue(name);
+ if (enumValue.has_value())
+ return enumValue;
+ }
+
+ qCWarning(lcShiboken, "no matching enum '%s'", qPrintable(name));
+ return {};
+}
+
+/// Searches the list after a class that matches \a name; either as C++,
+/// Target language base name or complete Target language package.class name.
+
+template <class It>
+static It findClassHelper(It begin, It end, QAnyStringView name)
+{
+ if (name.isEmpty() || begin == end)
+ return end;
+
+ if (asv_contains(name,'.')) { // Search target lang name
+ for (auto it = begin; it != end; ++it) {
+ if ((*it)->fullName() == name)
+ return it;
+ }
+ return end;
+ }
+
+ for (auto it = begin; it != end; ++it) {
+ if ((*it)->qualifiedCppName() == name)
+ return it;
+ }
+
+ if (asv_contains(name, "::")) // Qualified, cannot possibly match name
+ return end;
+
+ for (auto it = begin; it != end; ++it) {
+ if ((*it)->name() == name)
+ return it;
+ }
+
+ return end;
+}
+
+AbstractMetaClassPtr AbstractMetaClass::findClass(const AbstractMetaClassList &classes,
+ QAnyStringView name)
+{
+ auto it =findClassHelper(classes.cbegin(), classes.cend(), name);
+ return it != classes.cend() ? *it : nullptr;
+}
+
+AbstractMetaClassCPtr AbstractMetaClass::findClass(const AbstractMetaClassCList &classes,
+ QAnyStringView name)
+{
+ auto it = findClassHelper(classes.cbegin(), classes.cend(), name);
+ return it != classes.cend() ? *it : nullptr;
+}
+
+AbstractMetaClassPtr AbstractMetaClass::findClass(const AbstractMetaClassList &classes,
+ const TypeEntryCPtr &typeEntry)
+{
+ for (AbstractMetaClassPtr c : classes) {
+ if (c->typeEntry() == typeEntry)
+ return c;
+ }
+ return nullptr;
+}
+
+AbstractMetaClassCPtr AbstractMetaClass::findClass(const AbstractMetaClassCList &classes,
+ const TypeEntryCPtr &typeEntry)
+{
+ for (auto c : classes) {
+ if (c->typeEntry() == typeEntry)
+ return c;
+ }
+ return nullptr;
+}
+
+/// Returns true if this class is a subclass of the given class
+bool inheritsFrom(const AbstractMetaClassCPtr &c, const AbstractMetaClassCPtr &cls)
+{
+ Q_ASSERT(cls != nullptr);
+
+ if (c == cls || c->templateBaseClass() == cls)
+ return true;
+
+ return bool(recurseClassHierarchy(c, [cls](const AbstractMetaClassCPtr &c) {
+ return cls.get() == c.get();
+ }));
+}
+
+bool inheritsFrom(const AbstractMetaClassCPtr &c, QAnyStringView name)
+{
+ if (c->qualifiedCppName() == name)
+ return true;
+
+ if (c->templateBaseClass() != nullptr
+ && c->templateBaseClass()->qualifiedCppName() == name) {
+ return true;
+ }
+
+ return bool(recurseClassHierarchy(c, [&name](const AbstractMetaClassCPtr &c) {
+ return c->qualifiedCppName() == name;
+ }));
+}
+
+AbstractMetaClassCPtr findBaseClass(const AbstractMetaClassCPtr &c,
+ const QString &qualifiedName)
+{
+ auto tp = c->templateBaseClass();
+ if (tp && tp->qualifiedCppName() == qualifiedName)
+ return tp;
+
+ return recurseClassHierarchy(c, [&qualifiedName](const AbstractMetaClassCPtr &c) {
+ return c->qualifiedCppName() == qualifiedName;
+ });
+}
+
+// Query functions for generators
+bool AbstractMetaClass::isObjectType() const
+{
+ return d->m_typeEntry->isObject();
+}
+
+bool AbstractMetaClass::isCopyable() const
+{
+ if (isNamespace() || d->m_typeEntry->isObject())
+ return false;
+ auto copyable = d->m_typeEntry->copyable();
+ return copyable == ComplexTypeEntry::CopyableSet
+ || (copyable == ComplexTypeEntry::Unknown && isCopyConstructible());
+}
+
+bool AbstractMetaClass::isValueTypeWithCopyConstructorOnly() const
+{
+ return d->m_valueTypeWithCopyConstructorOnly;
+}
+
+void AbstractMetaClass::setValueTypeWithCopyConstructorOnly(bool v)
+{
+ d->m_valueTypeWithCopyConstructorOnly = v;
+}
+
+bool AbstractMetaClass::determineValueTypeWithCopyConstructorOnly(const AbstractMetaClassCPtr &c,
+ bool avoidProtectedHack)
+{
+
+ if (!c->typeEntry()->isValue())
+ return false;
+ if (c->attributes().testFlag(AbstractMetaClass::HasRejectedDefaultConstructor))
+ return false;
+ const auto ctors = c->queryFunctions(FunctionQueryOption::AnyConstructor);
+ bool copyConstructorFound = false;
+ for (const auto &ctor : ctors) {
+ switch (ctor->functionType()) {
+ case AbstractMetaFunction::ConstructorFunction:
+ if (!ctor->isPrivate() && (ctor->isPublic() || !avoidProtectedHack))
+ return false;
+ break;
+ case AbstractMetaFunction::CopyConstructorFunction:
+ copyConstructorFound = true;
+ break;
+ case AbstractMetaFunction::MoveConstructorFunction:
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+ return copyConstructorFound;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+void AbstractMetaClass::format(QDebug &debug) const
+{
+ if (debug.verbosity() > 2)
+ debug << static_cast<const void *>(this) << ", ";
+ debug << '"' << qualifiedCppName();
+ if (const auto count = d->m_templateArgs.size()) {
+ for (qsizetype i = 0; i < count; ++i)
+ debug << (i ? ',' : '<') << d->m_templateArgs.at(i)->qualifiedCppName();
+ debug << '>';
+ }
+ debug << '"';
+ if (isNamespace())
+ debug << " [namespace]";
+ if (attributes().testFlag(AbstractMetaClass::FinalCppClass))
+ debug << " [final]";
+ if (attributes().testFlag(AbstractMetaClass::Deprecated))
+ debug << " [deprecated]";
+
+ if (d->m_hasPrivateConstructor)
+ debug << " [private constructor]";
+ if (d->m_hasDeletedDefaultConstructor)
+ debug << " [deleted default constructor]";
+ if (d->m_hasDeletedCopyConstructor)
+ debug << " [deleted copy constructor]";
+ if (d->m_hasPrivateDestructor)
+ debug << " [private destructor]";
+ if (d->m_hasProtectedDestructor)
+ debug << " [protected destructor]";
+ if (d->m_hasVirtualDestructor)
+ debug << " [virtual destructor]";
+ if (d->m_valueTypeWithCopyConstructorOnly)
+ debug << " [value type with copy constructor only]";
+
+ if (!d->m_baseClasses.isEmpty()) {
+ debug << ", inherits ";
+ for (const auto &b : d->m_baseClasses)
+ debug << " \"" << b->name() << '"';
+ }
+
+ if (const qsizetype count = d->m_usingMembers.size()) {
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ debug << ", ";
+ debug << d->m_usingMembers.at(i);
+ }
+ }
+
+ if (auto templateBase = templateBaseClass()) {
+ const auto &instantiatedTypes = templateBaseClassInstantiations();
+ debug << ", instantiates \"" << templateBase->name();
+ for (qsizetype i = 0, count = instantiatedTypes.size(); i < count; ++i)
+ debug << (i ? ',' : '<') << instantiatedTypes.at(i).name();
+ debug << ">\"";
+ }
+ if (const auto count = d->m_propertySpecs.size()) {
+ debug << ", properties (" << count << "): [";
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ debug << ", ";
+ d->m_propertySpecs.at(i).formatDebug(debug);
+ }
+ debug << ']';
+ }
+}
+
+void AbstractMetaClass::formatMembers(QDebug &debug) const
+{
+ if (!d->m_enums.isEmpty())
+ debug << ", enums[" << d->m_enums.size() << "]=" << d->m_enums;
+ if (!d->m_functions.isEmpty()) {
+ const auto count = d->m_functions.size();
+ debug << ", functions=[" << count << "](";
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ debug << ", ";
+ d->m_functions.at(i)->formatDebugBrief(debug);
+ }
+ debug << ')';
+ }
+ if (const auto count = d->m_fields.size()) {
+ debug << ", fields=[" << count << "](";
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ debug << ", ";
+ d->m_fields.at(i).formatDebug(debug);
+ }
+ debug << ')';
+ }
+}
+
+SourceLocation AbstractMetaClass::sourceLocation() const
+{
+ return d->m_sourceLocation;
+}
+
+void AbstractMetaClass::setSourceLocation(const SourceLocation &sourceLocation)
+{
+ d->m_sourceLocation = sourceLocation;
+}
+
+AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr metaClass)
+{
+ AbstractMetaClassCList result;
+ recurseClassHierarchy(metaClass, [&result] (const AbstractMetaClassCPtr &c) {
+ if (!result.contains(c))
+ result.append(c);
+ return false;
+ });
+ result.removeFirst(); // remove self
+ return result;
+}
+
+QDebug operator<<(QDebug debug, const UsingMember &d)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "UsingMember(" << d.access << ' '
+ << d.baseClass->qualifiedCppName() << "::" << d.memberName << ')';
+ return debug;
+}
+
+void formatMetaClass(QDebug &ddebug, const AbstractMetaClass *ac)
+{
+ QDebugStateSaver saver(ddebug);
+ ddebug.noquote();
+ ddebug.nospace();
+ ddebug << "AbstractMetaClass(";
+ if (ac != nullptr) {
+ ac->format(ddebug);
+ if (ddebug.verbosity() > 2)
+ ac->formatMembers(ddebug);
+ } else {
+ ddebug << '0';
+ }
+ ddebug << ')';
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaClassCPtr &ac)
+{
+ formatMetaClass(d, ac.get());
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaClassPtr &ac)
+{
+ formatMetaClass(d, ac.get());
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaClass *ac)
+{
+ formatMetaClass(d, ac);
+ return d;
+}
+
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.h b/sources/shiboken6/ApiExtractor/abstractmetalang.h
new file mode 100644
index 000000000..3dc876690
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetalang.h
@@ -0,0 +1,393 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETALANG_H
+#define ABSTRACTMETALANG_H
+
+#include "abstractmetalang_enums.h"
+#include "abstractmetalang_typedefs.h"
+#include "enclosingclassmixin.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QStringList>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+enum class Access;
+class AbstractMetaClassPrivate;
+class ComplexTypeEntry;
+class Documentation;
+class EnumTypeEntry;
+class QPropertySpec;
+class SourceLocation;
+struct UsingMember;
+
+class AbstractMetaClass : public EnclosingClassMixin
+{
+ Q_GADGET
+public:
+ Q_DISABLE_COPY_MOVE(AbstractMetaClass)
+
+ enum CppWrapperFlag {
+ NoCppWrapper = 0x0,
+ CppProtectedHackWrapper = 0x1,// Make protected functions accessible
+ CppVirtualMethodWrapper = 0x2 // Need C++ wrapper for calling Python overrides
+ };
+ Q_DECLARE_FLAGS(CppWrapper, CppWrapperFlag)
+
+ enum Attribute {
+ None = 0x00000000,
+
+ Abstract = 0x00000001,
+ FinalInTargetLang = 0x00000002,
+
+ HasRejectedConstructor = 0x00000010,
+ HasRejectedDefaultConstructor = 0x00000020,
+
+ FinalCppClass = 0x00000100,
+ Deprecated = 0x00000200,
+ Struct = 0x00000400
+ };
+ Q_DECLARE_FLAGS(Attributes, Attribute)
+ Q_FLAG(Attribute)
+
+ Attributes attributes() const;
+ void setAttributes(Attributes attributes);
+
+ void operator+=(Attribute attribute);
+ void operator-=(Attribute attribute);
+
+ bool isFinalInTargetLang() const;
+ bool isAbstract() const;
+
+ AbstractMetaClass();
+ ~AbstractMetaClass();
+
+ const AbstractMetaFunctionCList &functions() const;
+ const AbstractMetaFunctionCList &userAddedPythonOverrides() const;
+ void setFunctions(const AbstractMetaFunctionCList &functions);
+ static void addFunction(const AbstractMetaClassPtr &klass,
+ const AbstractMetaFunctionCPtr &function);
+ bool hasFunction(const QString &str) const;
+ AbstractMetaFunctionCPtr findFunction(QAnyStringView functionName) const;
+ AbstractMetaFunctionCList findFunctions(QAnyStringView functionName) const;
+ AbstractMetaFunctionCPtr findOperatorBool() const;
+ // Find a Qt-style isNull() method suitable for nb_bool
+ AbstractMetaFunctionCPtr findQtIsNullMethod() const;
+ bool hasSignal(const AbstractMetaFunction *f) const;
+
+ bool hasConstructors() const;
+ AbstractMetaFunctionCPtr copyConstructor() const;
+ bool hasCopyConstructor() const;
+ bool hasPrivateCopyConstructor() const;
+
+ static void addDefaultConstructor(const AbstractMetaClassPtr &klass);
+ static void addDefaultCopyConstructor(const AbstractMetaClassPtr &klass);
+
+ bool hasNonPrivateConstructor() const;
+ void setHasNonPrivateConstructor(bool value);
+
+ bool hasPrivateConstructor() const;
+ void setHasPrivateConstructor(bool value);
+
+ bool hasDeletedDefaultConstructor() const;
+ void setHasDeletedDefaultConstructor(bool value);
+
+ bool hasDeletedCopyConstructor() const;
+ void setHasDeletedCopyConstructor(bool value);
+
+ bool hasPrivateDestructor() const;
+ void setHasPrivateDestructor(bool value);
+
+ bool hasProtectedDestructor() const;
+ void setHasProtectedDestructor(bool value);
+
+ bool hasVirtualDestructor() const;
+ void setHasVirtualDestructor(bool value);
+
+ bool isDefaultConstructible() const;
+ bool isImplicitlyDefaultConstructible() const;
+ bool canAddDefaultConstructor() const;
+
+ bool isCopyConstructible() const;
+ bool isImplicitlyCopyConstructible() const;
+ bool canAddDefaultCopyConstructor() const;
+
+ static void addSynthesizedComparisonOperators(const AbstractMetaClassPtr &c);
+
+ bool generateExceptionHandling() const;
+
+ CppWrapper cppWrapper() const;
+
+ const UsingMembers &usingMembers() const;
+ void addUsingMember(const UsingMember &um);
+ bool isUsingMember(const AbstractMetaClassCPtr &c, const QString &memberName,
+ Access minimumAccess) const;
+ bool hasUsingMemberFor(const QString &memberName) const;
+
+ AbstractMetaFunctionCList queryFunctionsByName(const QString &name) const;
+ static bool queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query);
+ static AbstractMetaFunctionCList queryFunctionList(const AbstractMetaFunctionCList &list,
+ FunctionQueryOptions query);
+ static AbstractMetaFunctionCPtr queryFirstFunction(const AbstractMetaFunctionCList &list,
+ FunctionQueryOptions query);
+
+ AbstractMetaFunctionCList queryFunctions(FunctionQueryOptions query) const;
+ AbstractMetaFunctionCList functionsInTargetLang() const;
+ AbstractMetaFunctionCList cppSignalFunctions() const;
+ AbstractMetaFunctionCList implicitConversions() const;
+
+ /**
+ * Retrieves all class' operator overloads that meet
+ * query criteria defined with the OperatorQueryOption
+ * enum.
+ * /param query composition of OperatorQueryOption enum values
+ * /return list of operator overload methods that meet the
+ * query criteria
+ */
+ AbstractMetaFunctionCList operatorOverloads(OperatorQueryOptions query) const;
+
+ bool hasArithmeticOperatorOverload() const;
+ bool hasIncDecrementOperatorOverload() const;
+ bool hasBitwiseOperatorOverload() const;
+ bool hasComparisonOperatorOverload() const;
+ bool hasLogicalOperatorOverload() const;
+
+ const AbstractMetaFieldList &fields() const;
+ AbstractMetaFieldList &fields();
+ void setFields(const AbstractMetaFieldList &fields);
+ void addField(const AbstractMetaField &field);
+ bool hasStaticFields() const;
+
+ std::optional<AbstractMetaField> findField(QStringView name) const;
+
+ const AbstractMetaEnumList &enums() const;
+ AbstractMetaEnumList &enums();
+ void setEnums(const AbstractMetaEnumList &enums);
+ void addEnum(const AbstractMetaEnum &e);
+
+ std::optional<AbstractMetaEnum> findEnum(const QString &enumName) const;
+ std::optional<AbstractMetaEnumValue> findEnumValue(const QString &enumName) const;
+ void getEnumsToBeGenerated(AbstractMetaEnumList *enumList) const;
+ void getEnumsFromInvisibleNamespacesToBeGenerated(AbstractMetaEnumList *enumList) const;
+
+ void getFunctionsFromInvisibleNamespacesToBeGenerated(AbstractMetaFunctionCList *funcList) const;
+
+ QString fullName() const;
+
+ /**
+ * Retrieves the class name without any namespace/scope information.
+ * /return the class name without scope information
+ */
+ QString name() const;
+
+ const Documentation &documentation() const;
+ void setDocumentation(const Documentation& doc);
+
+ QString baseClassName() const;
+
+ AbstractMetaClassCPtr defaultSuperclass() const; // Attribute "default-superclass"
+ void setDefaultSuperclass(const AbstractMetaClassPtr &s);
+
+ AbstractMetaClassCPtr baseClass() const;
+ const AbstractMetaClassCList &baseClasses() const;
+ // base classes including defaultSuperclass
+ AbstractMetaClassCList typeSystemBaseClasses() const;
+ // Recursive list of all base classes including defaultSuperclass
+ AbstractMetaClassCList allTypeSystemAncestors() const;
+
+ void addBaseClass(const AbstractMetaClassCPtr &base_class);
+ void setBaseClass(const AbstractMetaClassCPtr &base_class);
+
+ /**
+ * \return the namespace from another package which this namespace extends.
+ */
+ AbstractMetaClassCPtr extendedNamespace() const;
+ void setExtendedNamespace(const AbstractMetaClassCPtr &e);
+
+ const AbstractMetaClassCList &innerClasses() const;
+ void addInnerClass(const AbstractMetaClassPtr &cl);
+ void setInnerClasses(const AbstractMetaClassCList &innerClasses);
+
+ QString package() const;
+
+ bool isNamespace() const;
+ bool isInvisibleNamespace() const;
+ bool isInlineNamespace() const;
+
+ bool isQtNamespace() const;
+
+ QString qualifiedCppName() const;
+
+ bool hasSignals() const;
+
+ /**
+ * Says if the class that declares or inherits a virtual function.
+ * \return true if the class implements or inherits any virtual methods
+ */
+ bool isPolymorphic() const;
+
+ /**
+ * Tells if this class has one or more fields (member variables) that are protected.
+ * \return true if the class has protected fields.
+ */
+ bool hasProtectedFields() const;
+
+
+ const TypeEntryCList &templateArguments() const;
+ void setTemplateArguments(const TypeEntryCList &);
+
+ // only valid during metabuilder's run
+ const QStringList &baseClassNames() const;
+ void setBaseClassNames(const QStringList &names);
+
+ ComplexTypeEntryCPtr typeEntry() const;
+ ComplexTypeEntryPtr typeEntry();
+ void setTypeEntry(const ComplexTypeEntryPtr &type);
+
+ /// Returns the global hash function as found by the code parser
+ QString hashFunction() const;
+ void setHashFunction(const QString &);
+
+ /// Returns whether the class has a qHash() overload. Currently unused,
+ /// specified in type system.
+ bool hasHashFunction() const;
+
+ const QList<QPropertySpec> &propertySpecs() const;
+ void addPropertySpec(const QPropertySpec &spec);
+ void setPropertyDocumentation(const QString &name, const Documentation &doc);
+
+ // Helpers to search whether a functions is a property setter/getter/reset
+ enum class PropertyFunction
+ {
+ Read,
+ Write,
+ Reset,
+ Notify
+ };
+ struct PropertyFunctionSearchResult
+ {
+ qsizetype index;
+ PropertyFunction function;
+ };
+
+ PropertyFunctionSearchResult searchPropertyFunction(const QString &name) const;
+
+ std::optional<QPropertySpec> propertySpecByName(const QString &name) const;
+
+ /// Returns a list of conversion operators for this class. The conversion
+ /// operators are defined in other classes of the same module.
+ const AbstractMetaFunctionCList &externalConversionOperators() const;
+ /// Adds a converter operator for this class.
+ void addExternalConversionOperator(const AbstractMetaFunctionCPtr &conversionOp);
+ /// Returns true if this class has any converter operators defined elsewhere.
+ bool hasExternalConversionOperators() const;
+
+ void sortFunctions();
+
+ AbstractMetaClassCPtr templateBaseClass() const;
+ void setTemplateBaseClass(const AbstractMetaClassCPtr &cls);
+
+ bool hasTemplateBaseClassInstantiations() const;
+ const AbstractMetaTypeList &templateBaseClassInstantiations() const;
+ void setTemplateBaseClassInstantiations(const AbstractMetaTypeList& instantiations);
+
+ void setTypeDef(bool typeDef);
+ bool isTypeDef() const;
+
+ bool isStream() const;
+ void setStream(bool stream);
+
+ bool hasToStringCapability() const;
+ void setToStringCapability(bool value, uint indirections = 0);
+
+ uint toStringCapabilityIndirections() const;
+
+ bool deleteInMainThread() const;
+
+ // Query functions for generators
+ bool isObjectType() const;
+ bool isCopyable() const;
+ bool isValueTypeWithCopyConstructorOnly() const;
+ void setValueTypeWithCopyConstructorOnly(bool v);
+ static bool determineValueTypeWithCopyConstructorOnly(const AbstractMetaClassCPtr &c,
+ bool avoidProtectedHack);
+
+ static AbstractMetaClassPtr findClass(const AbstractMetaClassList &classes,
+ QAnyStringView name);
+ static AbstractMetaClassCPtr findClass(const AbstractMetaClassCList &classes,
+ QAnyStringView name);
+ static AbstractMetaClassPtr findClass(const AbstractMetaClassList &classes,
+ const TypeEntryCPtr &typeEntry);
+ static AbstractMetaClassCPtr findClass(const AbstractMetaClassCList &classes,
+ const TypeEntryCPtr &typeEntry);
+ AbstractMetaClassCPtr findBaseClass(const QString &qualifiedName) const;
+
+ static std::optional<AbstractMetaEnumValue> findEnumValue(const AbstractMetaClassList &classes,
+ const QString &string);
+
+ SourceLocation sourceLocation() const;
+ void setSourceLocation(const SourceLocation &sourceLocation);
+
+ // For AbstractMetaBuilder
+ static void fixFunctions(const AbstractMetaClassPtr &klass);
+ bool needsInheritanceSetup() const;
+ void setInheritanceDone(bool b);
+ bool inheritanceDone() const;
+
+ template <class Function>
+ void invisibleNamespaceRecursion(Function f) const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ void format(QDebug &d) const;
+ void formatMembers(QDebug &d) const;
+ friend QDebug operator<<(QDebug d, const AbstractMetaClassCPtr &ac);
+ friend QDebug operator<<(QDebug d, const AbstractMetaClassPtr &ac);
+ friend QDebug operator<<(QDebug d, const AbstractMetaClass *ac);
+ friend void formatMetaClass(QDebug &, const AbstractMetaClass *);
+#endif
+
+ QScopedPointer<AbstractMetaClassPrivate> d;
+};
+
+inline bool AbstractMetaClass::isAbstract() const
+{
+ return attributes().testFlag(Abstract);
+}
+
+template <class Function>
+void AbstractMetaClass::invisibleNamespaceRecursion(Function f) const
+{
+ for (const auto &ic : innerClasses()) {
+ if (ic->isInvisibleNamespace()) {
+ f(ic);
+ ic->invisibleNamespaceRecursion(f);
+ }
+ }
+}
+
+bool inheritsFrom(const AbstractMetaClassCPtr &c, const AbstractMetaClassCPtr &other);
+bool inheritsFrom(const AbstractMetaClassCPtr &c, QAnyStringView name);
+inline bool isQObject(const AbstractMetaClassCPtr &c)
+{
+ return inheritsFrom(c, "QObject");
+}
+
+AbstractMetaClassCPtr findBaseClass(const AbstractMetaClassCPtr &c,
+ const QString &qualifiedName);
+
+/// Return type entry of the base class that declares the parent management
+TypeEntryCPtr parentManagementEntry(const AbstractMetaClassCPtr &klass);
+inline bool hasParentManagement(const AbstractMetaClassCPtr &c)
+{ return bool(parentManagementEntry(c)); }
+
+AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr metaClass);
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaClass::CppWrapper);
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaClass::Attributes);
+
+#endif // ABSTRACTMETALANG_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang_enums.h b/sources/shiboken6/ApiExtractor/abstractmetalang_enums.h
new file mode 100644
index 000000000..9047c6bcd
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetalang_enums.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETALANG_ENUMS_H
+#define ABSTRACTMETALANG_ENUMS_H
+
+#include <QtCore/QFlags>
+
+enum class FunctionQueryOption {
+ AnyConstructor = 0x0000001, // Any constructor (copy/move)
+ Constructors = 0x0000002, // Constructors except copy/move
+ CopyConstructor = 0x0000004, // Only copy constructors
+ //Destructors = 0x0000002, // Only destructors. Not included in class.
+ ClassImplements = 0x0000020, // Only functions implemented by the current class
+ StaticFunctions = 0x0000080, // Only static functions
+ Signals = 0x0000100, // Only signals
+ NormalFunctions = 0x0000200, // Only functions that aren't signals
+ Visible = 0x0000400, // Only public and protected functions
+ NonStaticFunctions = 0x0004000, // No static functions
+ Empty = 0x0008000, // Empty overrides of abstract functions
+ Invisible = 0x0010000, // Only private functions
+ VirtualInCppFunctions = 0x0020000, // Only functions that are virtual in C++
+ NotRemoved = 0x0400000, // Only functions that have not been removed
+ OperatorOverloads = 0x2000000, // Only functions that are operator overloads
+ GenerateExceptionHandling = 0x4000000,
+ GetAttroFunction = 0x8000000,
+ SetAttroFunction = 0x10000000
+};
+
+Q_DECLARE_FLAGS(FunctionQueryOptions, FunctionQueryOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(FunctionQueryOptions)
+
+enum class OperatorQueryOption {
+ ArithmeticOp = 0x01, // Arithmetic: +, -, *, /, %, +=, -=, *=, /=, %=, unary+, unary-
+ IncDecrementOp = 0x02, // ++, --
+ BitwiseOp = 0x04, // Bitwise: <<, <<=, >>, >>=, ~, &, &=, |, |=, ^, ^=
+ ComparisonOp = 0x08, // Comparison: <, <=, >, >=, !=, ==
+ // Comparing to instances of owner class: <, <=, >, >=, !=, ==
+ // (bool operator==(QByteArray,QByteArray) but not bool operator==(QByteArray,const char *)
+ SymmetricalComparisonOp = 0x10,
+ LogicalOp = 0x20, // Logical: !, &&, ||
+ ConversionOp = 0x40, // Conversion: operator [const] TYPE()
+ SubscriptionOp = 0x80, // Subscription: []
+ AssignmentOp = 0x100 // Assignment: =
+};
+
+Q_DECLARE_FLAGS(OperatorQueryOptions, OperatorQueryOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(OperatorQueryOptions)
+
+#endif // ABSTRACTMETALANG_ENUMS_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang_helpers.h b/sources/shiboken6/ApiExtractor/abstractmetalang_helpers.h
new file mode 100644
index 000000000..2a053ceaf
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetalang_helpers.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETALANG_HELPERS_H
+#define ABSTRACTMETALANG_HELPERS_H
+
+#include "abstractmetalang_typedefs.h"
+
+template <class MetaClass>
+std::shared_ptr<MetaClass> findByName(const QList<std::shared_ptr<MetaClass> > &haystack,
+ QStringView needle)
+{
+ for (const auto &c : haystack) {
+ if (c->name() == needle)
+ return c;
+ }
+ return {};
+}
+
+// Helper for recursing the base classes of an AbstractMetaClass.
+// Returns the class for which the predicate is true.
+template <class Predicate>
+AbstractMetaClassCPtr recurseClassHierarchy(const AbstractMetaClassCPtr &klass,
+ Predicate pred)
+{
+ if (pred(klass))
+ return klass;
+ for (auto base : klass->baseClasses()) {
+ if (auto r = recurseClassHierarchy(base, pred))
+ return r;
+ }
+ return {};
+}
+
+#endif // ABSTRACTMETALANG_HELPERS_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h b/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h
new file mode 100644
index 000000000..802f549cf
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETALANG_TYPEDEFS_H
+#define ABSTRACTMETALANG_TYPEDEFS_H
+
+#include <QtCore/QList>
+
+#include <memory>
+
+class AbstractMetaClass;
+class AbstractMetaField;
+class AbstractMetaArgument;
+class AbstractMetaEnum;
+class AbstractMetaEnumValue;
+class AbstractMetaFunction;
+class AbstractMetaType;
+struct UsingMember;
+
+using AbstractMetaFunctionPtr = std::shared_ptr<AbstractMetaFunction>;
+using AbstractMetaFunctionCPtr = std::shared_ptr<const AbstractMetaFunction>;
+using AbstractMetaClassPtr = std::shared_ptr<AbstractMetaClass>;
+using AbstractMetaClassCPtr = std::shared_ptr<const AbstractMetaClass>;
+
+using AbstractMetaArgumentList = QList<AbstractMetaArgument>;
+using AbstractMetaClassList = QList<AbstractMetaClassPtr>;
+using AbstractMetaClassCList = QList<AbstractMetaClassCPtr>;
+using AbstractMetaEnumList = QList<AbstractMetaEnum>;
+using AbstractMetaEnumValueList = QList<AbstractMetaEnumValue>;
+using AbstractMetaFieldList = QList<AbstractMetaField>;
+using AbstractMetaFunctionRawPtrList = QList<AbstractMetaFunction *>;
+using AbstractMetaFunctionCList = QList<AbstractMetaFunctionCPtr>;
+using AbstractMetaTypeList = QList<AbstractMetaType>;
+using UsingMembers = QList<UsingMember>;
+
+#endif // ABSTRACTMETALANG_TYPEDEFS_H
diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
new file mode 100644
index 000000000..3ec07509d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
@@ -0,0 +1,1120 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetatype.h"
+#include "abstractmetabuilder.h"
+#include "abstractmetalang.h"
+#include "messages.h"
+#include "typedatabase.h"
+#include "containertypeentry.h"
+#include "enumtypeentry.h"
+#include "flagstypeentry.h"
+
+#include "qtcompat.h"
+#include "typeinfo.h"
+
+#ifndef QT_NO_DEBUG_STREAM
+# include <QtCore/QDebug>
+#endif
+
+#include <QtCore/QHash>
+#include <QtCore/QSharedData>
+#include <QtCore/QStack>
+
+#include <memory>
+
+using namespace Qt::StringLiterals;
+
+using AbstractMetaTypeCPtr = std::shared_ptr<const AbstractMetaType>;
+
+const QSet<QString> &AbstractMetaType::cppFloatTypes()
+{
+ static const QSet<QString> result{u"double"_s, u"float"_s};
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppSignedCharTypes()
+{
+ static const QSet<QString> result{u"char"_s, u"signed char"_s};
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppUnsignedCharTypes()
+{
+ static const QSet<QString> result{u"unsigned char"_s};
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppCharTypes()
+{
+ static const QSet<QString> result = cppSignedCharTypes() | cppUnsignedCharTypes();
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppSignedIntTypes()
+{
+ static QSet<QString> result;
+ if (result.isEmpty()) {
+ result = {u"char"_s, u"signed char"_s, u"short"_s, u"short int"_s,
+ u"signed short"_s, u"signed short int"_s,
+ u"int"_s, u"signed int"_s,
+ u"long"_s, u"long int"_s,
+ u"signed long"_s, u"signed long int"_s,
+ u"long long"_s, u"long long int"_s,
+ u"signed long long int"_s,
+ u"ptrdiff_t"_s};
+ result |= cppSignedCharTypes();
+ }
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppUnsignedIntTypes()
+{
+ static QSet<QString> result;
+ if (result.isEmpty()) {
+ result = {u"unsigned short"_s, u"unsigned short int"_s,
+ u"unsigned"_s, u"unsigned int"_s,
+ u"unsigned long"_s, u"unsigned long int"_s,
+ u"unsigned long long"_s,
+ u"unsigned long long int"_s,
+ u"size_t"_s};
+ result |= cppUnsignedCharTypes();
+ }
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppIntegralTypes()
+{
+ static QSet<QString> result;
+ if (result.isEmpty()) {
+ result |= cppSignedIntTypes();
+ result |= cppUnsignedIntTypes();
+ result.insert(u"bool"_s);
+ }
+ return result;
+}
+
+const QSet<QString> &AbstractMetaType::cppPrimitiveTypes()
+{
+ static QSet<QString> result;
+ if (result.isEmpty()) {
+ result |= cppIntegralTypes();
+ result |= cppFloatTypes();
+ result.insert(u"wchar_t"_s);
+ }
+ return result;
+}
+
+class AbstractMetaTypeData : public QSharedData
+{
+public:
+ AbstractMetaTypeData(const TypeEntryCPtr &t);
+
+ int actualIndirections() const;
+ bool passByConstRef() const;
+ bool passByValue() const;
+ AbstractMetaType::TypeUsagePattern determineUsagePattern() const;
+ bool hasTemplateChildren() const;
+ QString formatSignature(bool minimal) const;
+ QString formatPythonSignature() const;
+ bool isEquivalent(const AbstractMetaTypeData &rhs) const;
+ bool equals(const AbstractMetaTypeData &rhs) const;
+ QStringList instantiationCppSignatures() const;
+
+ template <class Predicate>
+ bool generateOpaqueContainer(Predicate p) const;
+
+ TypeEntryCPtr m_typeEntry;
+ AbstractMetaTypeList m_instantiations;
+ mutable QString m_cachedCppSignature;
+ mutable QString m_cachedPythonSignature;
+ QString m_originalTypeDescription;
+
+ int m_arrayElementCount = -1;
+ AbstractMetaTypeCPtr m_arrayElementType;
+ AbstractMetaTypeCPtr m_originalTemplateType;
+ AbstractMetaTypeCPtr m_viewOn;
+ AbstractMetaType::Indirections m_indirections;
+
+ AbstractMetaType::TypeUsagePattern m_pattern = AbstractMetaType::VoidPattern;
+ uint m_constant : 1;
+ uint m_volatile : 1;
+ uint m_signaturesDirty : 1;
+ uint m_reserved : 29; // unused
+
+ ReferenceType m_referenceType = NoReference;
+ AbstractMetaTypeList m_children;
+};
+
+AbstractMetaTypeData::AbstractMetaTypeData(const TypeEntryCPtr &t) :
+ m_typeEntry(t),
+ m_constant(false),
+ m_volatile(false),
+ m_signaturesDirty(true),
+ m_reserved(0)
+{
+}
+
+QStringList AbstractMetaTypeData::instantiationCppSignatures() const
+{
+ QStringList result;
+ for (const auto &i : m_instantiations)
+ result.append(i.cppSignature());
+ return result;
+}
+
+AbstractMetaType::AbstractMetaType(const TypeEntryCPtr &t) :
+ d(new AbstractMetaTypeData(t))
+{
+ Q_ASSERT(t);
+}
+
+AbstractMetaType::AbstractMetaType()
+{
+ *this = AbstractMetaType::createVoid();
+}
+
+AbstractMetaType &AbstractMetaType::operator=(const AbstractMetaType &) = default;
+
+AbstractMetaType::AbstractMetaType(const AbstractMetaType &rhs) = default;
+
+AbstractMetaType::AbstractMetaType(AbstractMetaType &&) noexcept = default;
+
+AbstractMetaType &AbstractMetaType::operator=(AbstractMetaType &&) noexcept = default;
+
+AbstractMetaType::~AbstractMetaType() = default;
+
+QString AbstractMetaType::package() const
+{
+ return d->m_typeEntry->targetLangPackage();
+}
+
+QString AbstractMetaType::name() const
+{
+ return d->m_typeEntry->targetLangEntryName();
+}
+
+QString AbstractMetaType::fullName() const
+{
+ return d->m_typeEntry->qualifiedTargetLangName();
+}
+
+void AbstractMetaType::setTypeUsagePattern(AbstractMetaType::TypeUsagePattern pattern)
+{
+ if (d->m_pattern != pattern)
+ d->m_pattern = pattern;
+}
+
+AbstractMetaType::TypeUsagePattern AbstractMetaType::typeUsagePattern() const
+{
+ return d->m_pattern;
+}
+
+bool AbstractMetaType::hasInstantiations() const
+{
+ return !d->m_instantiations.isEmpty();
+}
+
+void AbstractMetaType::addInstantiation(const AbstractMetaType &inst)
+{
+ d->m_instantiations << inst;
+}
+
+void AbstractMetaType::setInstantiations(const AbstractMetaTypeList &insts)
+{
+ if (insts != d->m_instantiations)
+ d->m_instantiations = insts;
+}
+
+const AbstractMetaTypeList &AbstractMetaType::instantiations() const
+{
+ return d->m_instantiations;
+}
+
+QStringList AbstractMetaType::instantiationCppSignatures() const
+{
+ return d->instantiationCppSignatures();
+}
+
+// For applying the <array> function argument modification: change into a type
+// where "int *" becomes "int[]".
+bool AbstractMetaType::applyArrayModification(QString *errorMessage)
+{
+
+ if (d->m_pattern == AbstractMetaType::NativePointerAsArrayPattern) {
+ *errorMessage = u"<array> modification already applied."_s;
+ return false;
+ }
+ if (d->m_arrayElementType) {
+ QTextStream(errorMessage) << "The type \"" << cppSignature()
+ << "\" is an array of " << d->m_arrayElementType->name() << '.';
+ return false;
+ }
+ if (d->m_indirections.isEmpty()) {
+ QTextStream(errorMessage) << "The type \"" << cppSignature()
+ << "\" does not have indirections.";
+ return false;
+ }
+ // Element type to be used for ArrayHandle<>, strip constness.
+
+ auto elementType = new AbstractMetaType(*this);
+ auto indir = indirectionsV();
+ indir.pop_front();
+ elementType->setIndirectionsV(indir);
+ elementType->setConstant(false);
+ elementType->setVolatile(false);
+ elementType->decideUsagePattern();
+ d->m_arrayElementType.reset(elementType);
+ d->m_pattern = AbstractMetaType::NativePointerAsArrayPattern;
+ return true;
+}
+
+TypeEntryCPtr AbstractMetaType::typeEntry() const
+{
+ return d->m_typeEntry;
+}
+
+void AbstractMetaType::setTypeEntry(const TypeEntryCPtr &type)
+{
+ if (d->m_typeEntry != type)
+ d->m_typeEntry = type;
+}
+
+void AbstractMetaType::setOriginalTypeDescription(const QString &otd)
+{
+ if (d->m_originalTypeDescription != otd)
+ d->m_originalTypeDescription = otd;
+}
+
+QString AbstractMetaType::originalTypeDescription() const
+{
+ return d->m_originalTypeDescription;
+}
+
+void AbstractMetaType::setOriginalTemplateType(const AbstractMetaType &type)
+{
+ if (!d->m_originalTemplateType || *d->m_originalTemplateType != type)
+ d->m_originalTemplateType.reset(new AbstractMetaType(type));
+}
+
+const AbstractMetaType *AbstractMetaType::originalTemplateType() const
+{
+ return d->m_originalTemplateType.get();
+}
+
+AbstractMetaType AbstractMetaType::getSmartPointerInnerType() const
+{
+ Q_ASSERT(isSmartPointer());
+ Q_ASSERT(!d->m_instantiations.isEmpty());
+ AbstractMetaType innerType = d->m_instantiations.at(0);
+ return innerType;
+}
+
+QString AbstractMetaType::getSmartPointerInnerTypeName() const
+{
+ Q_ASSERT(isSmartPointer());
+ return getSmartPointerInnerType().name();
+}
+
+AbstractMetaTypeList AbstractMetaType::nestedArrayTypes() const
+{
+ AbstractMetaTypeList result;
+ switch (d->m_pattern) {
+ case ArrayPattern:
+ for (AbstractMetaType t = *this; t.typeUsagePattern() == ArrayPattern; ) {
+ const AbstractMetaType *elt = t.arrayElementType();
+ result.append(*elt);
+ t = *elt;
+ }
+ break;
+ case NativePointerAsArrayPattern:
+ result.append(*d->m_arrayElementType.get());
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+bool AbstractMetaTypeData::passByConstRef() const
+{
+ return m_constant && m_referenceType == LValueReference && m_indirections.isEmpty();
+}
+
+bool AbstractMetaType::passByConstRef() const
+{
+ return d->passByConstRef();
+}
+
+bool AbstractMetaTypeData::passByValue() const
+{
+ return m_referenceType == NoReference && m_indirections.isEmpty();
+}
+
+bool AbstractMetaType::passByValue() const
+{
+ return d->passByValue();
+}
+
+bool AbstractMetaType::useStdMove() const
+{
+ return (isUniquePointer() && d->passByValue())
+ || d->m_referenceType == RValueReference;
+}
+
+ReferenceType AbstractMetaType::referenceType() const
+{
+ return d->m_referenceType;
+}
+
+void AbstractMetaType::setReferenceType(ReferenceType ref)
+{
+ if (d->m_referenceType != ref) {
+ d->m_referenceType = ref;
+ d->m_signaturesDirty = true;
+ }
+}
+
+int AbstractMetaTypeData::actualIndirections() const
+{
+ return m_indirections.size() + (m_referenceType == LValueReference ? 1 : 0);
+}
+
+int AbstractMetaType::actualIndirections() const
+{
+ return d->actualIndirections();
+}
+
+AbstractMetaType::Indirections AbstractMetaType::indirectionsV() const
+{
+ return d->m_indirections;
+}
+
+void AbstractMetaType::setIndirectionsV(const AbstractMetaType::Indirections &i)
+{
+ if (d->m_indirections != i) {
+ d->m_indirections = i;
+ d->m_signaturesDirty = true;
+ }
+}
+
+void AbstractMetaType::clearIndirections()
+{
+ if (!d->m_indirections.isEmpty()) {
+ d->m_indirections.clear();
+ d->m_signaturesDirty = true;
+ }
+}
+
+int AbstractMetaType::indirections() const
+{
+ return d->m_indirections.size();
+}
+
+void AbstractMetaType::setIndirections(int indirections)
+{
+ const Indirections newValue(indirections, Indirection::Pointer);
+ if (d->m_indirections != newValue) {
+ d->m_indirections = newValue;
+ d->m_signaturesDirty = true;
+ }
+}
+
+void AbstractMetaType::addIndirection(Indirection i)
+{
+ d->m_indirections.append(i);
+}
+
+void AbstractMetaType::setArrayElementCount(int n)
+{
+ if (d->m_arrayElementCount != n) {
+ d->m_arrayElementCount = n;
+ d->m_signaturesDirty = true;
+ }
+}
+
+int AbstractMetaType::arrayElementCount() const
+{
+ return d->m_arrayElementCount;
+}
+
+const AbstractMetaType *AbstractMetaType::arrayElementType() const
+{
+ return d->m_arrayElementType.get();
+}
+
+void AbstractMetaType::setArrayElementType(const AbstractMetaType &t)
+{
+ if (!d->m_arrayElementType || *d->m_arrayElementType != t) {
+ d->m_arrayElementType.reset(new AbstractMetaType(t));
+ d->m_signaturesDirty = true;
+ }
+}
+
+AbstractMetaType AbstractMetaType::plainType() const
+{
+ AbstractMetaType result = *this;
+ result.clearIndirections();
+ result.setReferenceType(NoReference);
+ result.setConstant(false);
+ result.setVolatile(false);
+ result.decideUsagePattern();
+ return result;
+}
+
+QString AbstractMetaType::cppSignature() const
+{
+ const AbstractMetaTypeData *cd = d.constData();
+ if (cd->m_cachedCppSignature.isEmpty() || cd->m_signaturesDirty)
+ cd->m_cachedCppSignature = formatSignature(false);
+ return cd->m_cachedCppSignature;
+}
+
+QString AbstractMetaType::pythonSignature() const
+{
+ // PYSIDE-921: Handle container returntypes correctly.
+ // This is now a clean reimplementation.
+ const AbstractMetaTypeData *cd = d.constData();
+ if (cd->m_cachedPythonSignature.isEmpty() || cd->m_signaturesDirty)
+ cd->m_cachedPythonSignature = formatPythonSignature();
+ return cd->m_cachedPythonSignature;
+}
+
+AbstractMetaType::TypeUsagePattern AbstractMetaTypeData::determineUsagePattern() const
+{
+ if (m_typeEntry->isTemplateArgument())
+ return AbstractMetaType::TemplateArgument;
+
+ if (m_typeEntry->type() == TypeEntry::ConstantValueType)
+ return AbstractMetaType::NonTypeTemplateArgument;
+
+ if (m_typeEntry->isPrimitive() && (actualIndirections() == 0 || passByConstRef()))
+ return AbstractMetaType::PrimitivePattern;
+
+ if (m_typeEntry->isVoid()) {
+ return m_arrayElementCount < 0 && m_referenceType == NoReference
+ && m_indirections.isEmpty() && m_constant == 0 && m_volatile == 0
+ ? AbstractMetaType::VoidPattern : AbstractMetaType::NativePointerPattern;
+ }
+
+ if (m_typeEntry->isVarargs())
+ return AbstractMetaType::VarargsPattern;
+
+ if (m_typeEntry->isEnum() && (actualIndirections() == 0 || passByConstRef()))
+ return AbstractMetaType::EnumPattern;
+
+ if (m_typeEntry->isObject()) {
+ if (m_indirections.isEmpty() && m_referenceType == NoReference)
+ return AbstractMetaType::ValuePattern;
+ return AbstractMetaType::ObjectPattern;
+ }
+
+ if (m_typeEntry->isContainer() && m_indirections.isEmpty())
+ return AbstractMetaType::ContainerPattern;
+
+ if (m_typeEntry->isSmartPointer() && m_indirections.isEmpty())
+ return AbstractMetaType::SmartPointerPattern;
+
+ if (m_typeEntry->isFlags() && (actualIndirections() == 0 || passByConstRef()))
+ return AbstractMetaType::FlagsPattern;
+
+ if (m_typeEntry->isArray())
+ return AbstractMetaType::ArrayPattern;
+
+ if (m_typeEntry->isValue())
+ return m_indirections.size() == 1 ? AbstractMetaType::ValuePointerPattern : AbstractMetaType::ValuePattern;
+
+ return AbstractMetaType::NativePointerPattern;
+}
+
+AbstractMetaType::TypeUsagePattern AbstractMetaType::determineUsagePattern() const
+{
+ return d->determineUsagePattern();
+}
+
+void AbstractMetaType::decideUsagePattern()
+{
+ TypeUsagePattern pattern = determineUsagePattern();
+ if (d->m_typeEntry->isObject() && indirections() == 1
+ && d->m_referenceType == LValueReference && isConstant()) {
+ // const-references to pointers can be passed as pointers
+ setReferenceType(NoReference);
+ setConstant(false);
+ pattern = ObjectPattern;
+ }
+ setTypeUsagePattern(pattern);
+ Q_ASSERT(pattern != ContainerPattern || !d->m_instantiations.isEmpty());
+}
+
+bool AbstractMetaTypeData::hasTemplateChildren() const
+{
+ QStack<AbstractMetaType> children;
+ children << m_instantiations;
+
+ // Recursively iterate over the children / descendants of the type, to check if any of them
+ // corresponds to a template argument type.
+ while (!children.isEmpty()) {
+ AbstractMetaType child = children.pop();
+ if (child.typeEntry()->isTemplateArgument())
+ return true;
+ children << child.instantiations();
+ }
+ return false;
+}
+
+bool AbstractMetaType::hasTemplateChildren() const
+{
+ return d->hasTemplateChildren();
+}
+
+static inline QString formatArraySize(int e)
+{
+ QString result;
+ result += u'[';
+ if (e >= 0)
+ result += QString::number(e);
+ result += u']';
+ return result;
+}
+
+// Return the number of template parameters; remove the default
+// non template type parameter of std::span from the signature.
+static qsizetype stripDefaultTemplateArgs(const TypeEntryCPtr &te,
+ const AbstractMetaTypeList &instantiations)
+{
+ static const char16_t dynamicExtent64[] = u"18446744073709551615"; // size_t(-1)
+ static const char16_t dynamicExtent32[] = u"4294967295";
+
+ qsizetype result = instantiations.size();
+ if (result == 0 || !te->isContainer())
+ return result;
+ auto cte = std::static_pointer_cast<const ContainerTypeEntry>(te);
+ if (cte->containerKind() != ContainerTypeEntry::SpanContainer)
+ return result;
+ const auto lastTe = instantiations.constLast().typeEntry();
+ if (lastTe->type() == TypeEntry::ConstantValueType) {
+ const QString &name = lastTe->name();
+ if (name == dynamicExtent64 || name == dynamicExtent32)
+ --result;
+ }
+ return result;
+}
+
+QString AbstractMetaTypeData::formatSignature(bool minimal) const
+{
+ QString result;
+ if (m_constant)
+ result += u"const "_s;
+ if (m_volatile)
+ result += u"volatile "_s;
+ if (m_pattern == AbstractMetaType::ArrayPattern) {
+ // Build nested array dimensions a[2][3] in correct order
+ result += m_arrayElementType->minimalSignature();
+ const int arrayPos = result.indexOf(u'[');
+ if (arrayPos != -1)
+ result.insert(arrayPos, formatArraySize(m_arrayElementCount));
+ else
+ result.append(formatArraySize(m_arrayElementCount));
+ } else {
+ result += m_typeEntry->qualifiedCppName();
+ }
+ if (!m_instantiations.isEmpty()) {
+ result += u'<';
+ if (minimal)
+ result += u' ';
+ const auto size = stripDefaultTemplateArgs(m_typeEntry, m_instantiations);
+ for (qsizetype i = 0; i < size; ++i) {
+ if (i > 0)
+ result += u',';
+ result += m_instantiations.at(i).minimalSignature();
+ }
+ result += u'>';
+ }
+
+ if (!minimal && (!m_indirections.isEmpty() || m_referenceType != NoReference))
+ result += u' ';
+ for (Indirection i : m_indirections)
+ result += TypeInfo::indirectionKeyword(i);
+ switch (m_referenceType) {
+ case NoReference:
+ break;
+ case LValueReference:
+ result += u'&';
+ break;
+ case RValueReference:
+ result += u"&&"_s;
+ break;
+ }
+ return result;
+}
+
+QString AbstractMetaType::formatSignature(bool minimal) const
+{
+ return d->formatSignature(minimal);
+}
+
+QString AbstractMetaTypeData::formatPythonSignature() const
+{
+ /*
+ * This is a version of the above, more suitable for Python.
+ * We avoid extra keywords that are not needed in Python.
+ * We prepend the package name, unless it is a primitive type.
+ *
+ * Primitive types like 'int', 'char' etc.:
+ * When we have a primitive with an indirection, we use that '*'
+ * character for later postprocessing, since those indirections
+ * need to be modified into a result tuple.
+ * Smart pointer instantiations: Drop the package
+ */
+ QString result;
+ if (m_pattern == AbstractMetaType::NativePointerAsArrayPattern)
+ result += u"array "_s;
+ // We no longer use the "const" qualifier for heuristics. Instead,
+ // NativePointerAsArrayPattern indicates when we have <array> in XML.
+ // if (m_typeEntry->isPrimitive() && isConstant())
+ // result += QLatin1String("const ");
+ if (!m_typeEntry->isPrimitive() && !m_typeEntry->isSmartPointer()) {
+ const QString package = m_typeEntry->targetLangPackage();
+ if (!package.isEmpty())
+ result += package + u'.';
+ }
+ if (m_pattern == AbstractMetaType::ArrayPattern) {
+ // Build nested array dimensions a[2][3] in correct order
+ result += m_arrayElementType->formatPythonSignature();
+ const int arrayPos = result.indexOf(u'[');
+ if (arrayPos != -1)
+ result.insert(arrayPos, formatArraySize(m_arrayElementCount));
+ else
+ result.append(formatArraySize(m_arrayElementCount));
+ } else {
+ result += m_typeEntry->targetLangName();
+ }
+ if (!m_instantiations.isEmpty()) {
+ result += u'[';
+ for (qsizetype i = 0, size = m_instantiations.size(); i < size; ++i) {
+ if (i > 0)
+ result += u", "_s;
+ result += m_instantiations.at(i).formatPythonSignature();
+ }
+ result += u']';
+ }
+ if (m_typeEntry->isPrimitive())
+ for (Indirection i : m_indirections)
+ result += TypeInfo::indirectionKeyword(i);
+ // If it is a flags type, we replace it with the full name of the enum:
+ // "PySide6.QtCore.Qt.ItemFlag" instead of "PySide6.QtCore.QFlags<Qt.ItemFlag>"
+ if (m_typeEntry->isFlags()) {
+ const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(m_typeEntry);
+ result = fte->originator()->qualifiedTargetLangName();
+ }
+ result.replace(u"::"_s, u"."_s);
+ return result;
+}
+
+QString AbstractMetaType::formatPythonSignature() const
+{
+ return d->formatPythonSignature();
+}
+
+bool AbstractMetaType::isCppPrimitive() const
+{
+ return d->m_pattern == PrimitivePattern && ::isCppPrimitive(d->m_typeEntry);
+}
+
+bool AbstractMetaType::isConstant() const
+{
+ return d->m_constant;
+}
+
+void AbstractMetaType::setConstant(bool constant)
+{
+ if (d->m_constant != constant) {
+ d->m_constant = constant;
+ d->m_signaturesDirty = true;
+ }
+}
+
+bool AbstractMetaType::isVolatile() const
+{
+ return d->m_volatile;
+}
+
+void AbstractMetaType::setVolatile(bool v)
+{
+ if (d->m_volatile != v) {
+ d->m_volatile = v;
+ d->m_signaturesDirty = true;\
+ }
+}
+
+static bool equalsCPtr(const AbstractMetaTypeCPtr &t1, const AbstractMetaTypeCPtr &t2)
+{
+ if (bool(t1) != bool(t2))
+ return false;
+ return !t1 || *t1 == *t2;
+}
+
+bool AbstractMetaTypeData::isEquivalent(const AbstractMetaTypeData &rhs) const
+{
+ if (m_typeEntry != rhs.m_typeEntry
+ || m_indirections != rhs.m_indirections
+ || m_arrayElementCount != rhs.m_arrayElementCount) {
+ return false;
+ }
+
+ if (!equalsCPtr(m_arrayElementType, rhs.m_arrayElementType))
+ return false;
+
+ if (!equalsCPtr(m_viewOn, rhs.m_viewOn))
+ return false;
+
+ if (m_instantiations != rhs.m_instantiations)
+ return false;
+ return true;
+}
+
+bool AbstractMetaTypeData::equals(const AbstractMetaTypeData &rhs) const
+{
+ return m_constant == rhs.m_constant && m_volatile == rhs.m_volatile
+ && m_referenceType == rhs.m_referenceType && isEquivalent(rhs);
+}
+
+bool comparesEqual(const AbstractMetaType &lhs, const AbstractMetaType &rhs) noexcept
+{
+ return lhs.d->equals(*rhs.d);
+}
+
+bool AbstractMetaType::isEquivalent(const AbstractMetaType &rhs) const
+{
+ return d->isEquivalent(*rhs.d);
+}
+
+const AbstractMetaType *AbstractMetaType::viewOn() const
+{
+ return d->m_viewOn.get();
+}
+
+void AbstractMetaType::setViewOn(const AbstractMetaType &v)
+{
+ if (!d->m_viewOn || *d->m_viewOn != v)
+ d->m_viewOn.reset(new AbstractMetaType(v));
+}
+
+AbstractMetaType AbstractMetaType::createVoid()
+{
+ static QScopedPointer<AbstractMetaType> metaType;
+ if (metaType.isNull()) {
+ static TypeEntryCPtr voidTypeEntry = TypeDatabase::instance()->findType(u"void"_s);
+ Q_ASSERT(voidTypeEntry);
+ metaType.reset(new AbstractMetaType(voidTypeEntry));
+ metaType->decideUsagePattern();
+ }
+ return *metaType.data();
+}
+
+void AbstractMetaType::dereference(QString *type)
+{
+ type->prepend(u"(*"_s);
+ type->append(u')');
+}
+
+QString AbstractMetaType::dereferencePrefix(qsizetype n)
+{
+ const QChar c = n > 0 ? u'*' : u'&';
+ return QString(qAbs(n), c);
+}
+
+void AbstractMetaType::applyDereference(QString *type, qsizetype n)
+{
+ if (n == 0)
+ return;
+
+ type->prepend(dereferencePrefix(n));
+ type->prepend(u'(');
+ type->append(u')');
+}
+
+bool AbstractMetaType::stripDereference(QString *type)
+{
+ if (type->startsWith(u"(*") && type->endsWith(u')')) {
+ type->chop(1);
+ type->remove(0, 2);
+ *type = type->trimmed();
+ return true;
+ }
+ if (type->startsWith(u'*')) {
+ type->remove(0, 1);
+ *type = type->trimmed();
+ return true;
+ }
+ return false;
+}
+
+// Query functions for generators
+bool AbstractMetaType::isObjectType() const
+{
+ return d->m_typeEntry->isObject();
+}
+
+bool AbstractMetaType::isUniquePointer() const
+{
+ return isSmartPointer() && d->m_typeEntry->isUniquePointer();
+}
+
+bool AbstractMetaType::isPointer() const
+{
+ return !d->m_indirections.isEmpty()
+ || isNativePointer() || isValuePointer();
+}
+
+bool AbstractMetaType::isPointerToConst() const
+{
+ return d->m_constant && !d->m_indirections.isEmpty()
+ && d->m_indirections.constLast() != Indirection::ConstPointer;
+}
+
+bool AbstractMetaType::isCString() const
+{
+ return isNativePointer()
+ && d->m_indirections.size() == 1
+ && name() == u"char";
+}
+
+bool AbstractMetaType::isVoidPointer() const
+{
+ return isNativePointer()
+ && d->m_indirections.size() == 1
+ && name() == u"void";
+}
+
+bool AbstractMetaType::isUserPrimitive() const
+{
+ return d->m_indirections.isEmpty() && ::isUserPrimitive(d->m_typeEntry);
+}
+
+bool AbstractMetaType::isObjectTypeUsedAsValueType() const
+{
+ return d->m_typeEntry->isObject() && d->m_referenceType == NoReference
+ && d->m_indirections.isEmpty();
+}
+
+bool AbstractMetaType::isWrapperType() const
+{
+ return d->m_typeEntry->isWrapperType();
+}
+
+bool AbstractMetaType::isPointerToWrapperType() const
+{
+ return (isObjectType() && d->m_indirections.size() == 1) || isValuePointer();
+}
+
+bool AbstractMetaType::isWrapperPassedByReference() const
+{
+ return d->m_referenceType == LValueReference && isWrapperType()
+ && !isPointer();
+}
+
+bool AbstractMetaType::isCppIntegralPrimitive() const
+{
+ return ::isCppIntegralPrimitive(d->m_typeEntry);
+}
+
+bool AbstractMetaType::isExtendedCppPrimitive() const
+{
+ if (isCString() || isVoidPointer())
+ return true;
+ if (!d->m_indirections.isEmpty())
+ return false;
+ return ::isExtendedCppPrimitive(d->m_typeEntry);
+}
+
+bool AbstractMetaType::isValueTypeWithCopyConstructorOnly() const
+{
+ bool result = false;
+ if (d->m_typeEntry->isComplex()) {
+ const auto cte = std::static_pointer_cast<const ComplexTypeEntry>(d->m_typeEntry);
+ result = cte->isValueTypeWithCopyConstructorOnly();
+ }
+ return result;
+}
+
+bool AbstractMetaType::valueTypeWithCopyConstructorOnlyPassed() const
+{
+ return (passByValue() || passByConstRef())
+ && isValueTypeWithCopyConstructorOnly();
+}
+
+using AbstractMetaTypeCache = QHash<QString, AbstractMetaType>;
+
+Q_GLOBAL_STATIC(AbstractMetaTypeCache, metaTypeFromStringCache)
+
+std::optional<AbstractMetaType>
+AbstractMetaType::fromString(const QString &typeSignatureIn, QString *errorMessage)
+{
+ auto &cache = *metaTypeFromStringCache();
+ auto it = cache.find(typeSignatureIn);
+ if (it != cache.end())
+ return it.value();
+
+ QString typeSignature = typeSignatureIn.trimmed();
+ if (typeSignature.startsWith(u"::"))
+ typeSignature.remove(0, 2);
+
+ it = cache.find(typeSignature);
+ if (it == cache.end()) {
+ auto metaType =
+ AbstractMetaBuilder::translateType(typeSignature, nullptr, {}, errorMessage);
+ if (Q_UNLIKELY(!metaType.has_value())) {
+ if (errorMessage)
+ errorMessage->prepend(msgCannotBuildMetaType(typeSignature));
+ return {};
+ }
+ if (typeSignature != typeSignatureIn)
+ cache.insert(typeSignatureIn, metaType.value());
+ it = cache.insert(typeSignature, metaType.value());
+ }
+ return it.value();
+}
+
+AbstractMetaType AbstractMetaType::fromTypeEntry(const TypeEntryCPtr &typeEntry)
+{
+ QString typeName = typeEntry->qualifiedCppName();
+ if (typeName.startsWith(u"::"))
+ typeName.remove(0, 2);
+ auto &cache = *metaTypeFromStringCache();
+ auto it = cache.find(typeName);
+ if (it != cache.end())
+ return it.value();
+ AbstractMetaType metaType = AbstractMetaType(typeEntry).plainType();
+ cache.insert(typeName, metaType);
+ return metaType;
+}
+
+AbstractMetaType AbstractMetaType::fromAbstractMetaClass(const AbstractMetaClassCPtr &metaClass)
+{
+ return fromTypeEntry(metaClass->typeEntry());
+}
+
+template <class Predicate> // Predicate(containerTypeEntry, signature)
+bool AbstractMetaTypeData::generateOpaqueContainer(Predicate pred) const
+{
+ // Allow for passing containers by pointer as well.
+ if (!m_typeEntry->isContainer())
+ return false;
+ if (m_indirections.size() > 1)
+ return false;
+ auto containerTypeEntry = std::static_pointer_cast<const ContainerTypeEntry>(m_typeEntry);
+ auto kind = containerTypeEntry->containerKind();
+ if (kind != ContainerTypeEntry::ListContainer && kind != ContainerTypeEntry::SpanContainer)
+ return false;
+
+ const auto &firstInstantiation = m_instantiations.constFirst();
+ if (firstInstantiation.referenceType() != NoReference)
+ return false;
+ switch (firstInstantiation.typeEntry()->type()) {
+ case TypeEntry::PrimitiveType:
+ case TypeEntry::FlagsType:
+ case TypeEntry::EnumType:
+ case TypeEntry::BasicValueType:
+ case TypeEntry::ObjectType:
+ case TypeEntry::CustomType:
+ break;
+ default:
+ return false;
+ }
+
+ return pred(containerTypeEntry, instantiationCppSignatures());
+}
+
+// Simple predicate for checking whether an opaque container should be generated
+static bool opaqueContainerPredicate(const ContainerTypeEntryCPtr &t,
+ const QStringList &instantiations)
+{
+ return t->generateOpaqueContainer(instantiations);
+}
+
+bool AbstractMetaType::generateOpaqueContainer() const
+{
+ return d->generateOpaqueContainer(opaqueContainerPredicate);
+}
+
+// Helper for determining whether a function should return an opaque container,
+// that is, the function return type is modified accordingly
+// (cf AbstractMetaFunction::generateOpaqueContainerReturn())
+bool AbstractMetaType::generateOpaqueContainerForGetter(const QString &modifiedType) const
+{
+ auto predicate = [&modifiedType](const ContainerTypeEntryCPtr &t,
+ const QStringList &instantiations) {
+ return t->opaqueContainerName(instantiations) == modifiedType;
+ };
+ return d->generateOpaqueContainer(predicate);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void AbstractMetaType::formatDebug(QDebug &debug) const
+{
+ debug << '"' << name() << '"';
+ if (debug.verbosity() > 2 && !isVoid()) {
+ auto te = typeEntry();
+ debug << ", typeEntry=";
+ if (debug.verbosity() > 3)
+ debug << te;
+ else
+ debug << "(\"" << te->qualifiedCppName() << "\", " << te->type() << ')';
+ debug << ", signature=\"" << cppSignature() << "\", pattern="
+ << typeUsagePattern();
+ const auto indirections = indirectionsV();
+ if (!indirections.isEmpty()) {
+ debug << ", indirections=";
+ for (auto i : indirections)
+ debug << ' ' << TypeInfo::indirectionKeyword(i);
+ }
+ if (referenceType() != NoReference)
+ debug << ", reftype=" << referenceType();
+ if (isConstant())
+ debug << ", [const]";
+ if (isVolatile())
+ debug << ", [volatile]";
+ if (isArray()) {
+ debug << ", array of \"" << arrayElementType()->cppSignature()
+ << "\", arrayElementCount=" << arrayElementCount();
+ }
+ const auto &instantiations = this->instantiations();
+ if (const auto instantiationsSize = instantiations.size()) {
+ debug << ", instantiations[" << instantiationsSize << "]=<";
+ for (qsizetype i = 0; i < instantiationsSize; ++i) {
+ if (i)
+ debug << ", ";
+ instantiations.at(i).formatDebug(debug);
+ }
+ }
+ debug << '>';
+ if (viewOn())
+ debug << ", views " << viewOn()->name();
+ }
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaType &at)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaType(";
+ at.formatDebug(d);
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaType *at)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ if (at)
+ d << *at;
+ else
+ d << "AbstractMetaType(0)";
+ return d;
+}
+
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.h b/sources/shiboken6/ApiExtractor/abstractmetatype.h
new file mode 100644
index 000000000..8a1ecdf20
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetatype.h
@@ -0,0 +1,276 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ABSTRACTMETATYPE_H
+#define ABSTRACTMETATYPE_H
+
+#include "abstractmetalang_enums.h"
+#include "abstractmetalang_typedefs.h"
+#include "parser/codemodel_enums.h"
+#include "typedatabase_typedefs.h"
+
+#include <QtCore/QtCompare>
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/QHashFunctions>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QList>
+#include <QtCore/QSet>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class AbstractMetaTypeData;
+class TypeEntry;
+
+class AbstractMetaType
+{
+ Q_GADGET
+public:
+ using Indirections = QList<Indirection>;
+
+ enum TypeUsagePattern {
+ PrimitivePattern,
+ FlagsPattern,
+ EnumPattern,
+ ValuePattern,
+ ObjectPattern,
+ ValuePointerPattern,
+ NativePointerPattern,
+ NativePointerAsArrayPattern, // "int*" as "int[]"
+ ContainerPattern,
+ SmartPointerPattern,
+ VarargsPattern,
+ ArrayPattern,
+ VoidPattern, // Plain "void", no "void *" or similar.
+ TemplateArgument, // 'T' in std::array<T,2>
+ NonTypeTemplateArgument // '2' in in std::array<T,2>
+ };
+ Q_ENUM(TypeUsagePattern)
+
+ AbstractMetaType();
+ explicit AbstractMetaType(const TypeEntryCPtr &t);
+ AbstractMetaType(const AbstractMetaType &);
+ AbstractMetaType &operator=(const AbstractMetaType &);
+ AbstractMetaType(AbstractMetaType &&) noexcept;
+ AbstractMetaType &operator=(AbstractMetaType &&) noexcept;
+ ~AbstractMetaType();
+
+ QString package() const;
+ QString name() const;
+ QString fullName() const;
+
+ void setTypeUsagePattern(TypeUsagePattern pattern);
+ TypeUsagePattern typeUsagePattern() const;
+
+ // true when use pattern is container
+ bool hasInstantiations() const;
+
+ const AbstractMetaTypeList &instantiations() const;
+ void addInstantiation(const AbstractMetaType &inst);
+ void setInstantiations(const AbstractMetaTypeList &insts);
+ QStringList instantiationCppSignatures() const;
+
+ QString minimalSignature() const { return formatSignature(true); }
+
+ // returns true if the typs is used as a non complex primitive, no & or *'s
+ bool isPrimitive() const { return typeUsagePattern() == PrimitivePattern; }
+
+ bool isCppPrimitive() const;
+
+ // returns true if the type is used as an enum
+ bool isEnum() const { return typeUsagePattern() == EnumPattern; }
+
+ // returns true if the type is used as an object, e.g. Xxx *
+ bool isObject() const { return typeUsagePattern() == ObjectPattern; }
+
+ // returns true if the type is indicated an object by the TypeEntry
+ bool isObjectType() const;
+
+ // returns true if the type is used as an array, e.g. Xxx[42]
+ bool isArray() const { return typeUsagePattern() == ArrayPattern; }
+
+ // returns true if the type is used as a value type (X or const X &)
+ bool isValue() const { return typeUsagePattern() == ValuePattern; }
+
+ bool isValuePointer() const { return typeUsagePattern() == ValuePointerPattern; }
+
+ // returns true for more complex types...
+ bool isNativePointer() const { return typeUsagePattern() == NativePointerPattern; }
+
+ // return true if the type was originally a varargs
+ bool isVarargs() const { return typeUsagePattern() == VarargsPattern; }
+
+ // returns true if the type was used as a container
+ bool isContainer() const { return typeUsagePattern() == ContainerPattern; }
+
+ // returns true if the type was used as a smart pointer
+ bool isSmartPointer() const { return typeUsagePattern() == SmartPointerPattern; }
+ bool isUniquePointer() const;
+
+ // returns true if the type was used as a flag
+ bool isFlags() const { return typeUsagePattern() == FlagsPattern; }
+
+ bool isVoid() const { return typeUsagePattern() == VoidPattern; }
+
+ bool isConstant() const;
+ void setConstant(bool constant);
+
+ bool isVolatile() const;
+ void setVolatile(bool v);
+
+ bool passByConstRef() const;
+ bool passByValue() const;
+ bool useStdMove() const;
+
+ ReferenceType referenceType() const;
+ void setReferenceType(ReferenceType ref);
+
+ int actualIndirections() const;
+
+ Indirections indirectionsV() const;
+ void setIndirectionsV(const Indirections &i);
+ void clearIndirections();
+
+ // "Legacy"?
+ int indirections() const;
+ void setIndirections(int indirections);
+ void addIndirection(Indirection i = Indirection::Pointer);
+
+ void setArrayElementCount(int n);
+ int arrayElementCount() const;
+
+ const AbstractMetaType *arrayElementType() const;
+ void setArrayElementType(const AbstractMetaType &t);
+
+ AbstractMetaTypeList nestedArrayTypes() const;
+
+ /// Strip const/indirections/reference from the type
+ AbstractMetaType plainType() const;
+
+ QString cppSignature() const;
+
+ QString pythonSignature() const;
+
+ bool applyArrayModification(QString *errorMessage);
+
+ TypeEntryCPtr typeEntry() const;
+ void setTypeEntry(const TypeEntryCPtr &type);
+
+ void setOriginalTypeDescription(const QString &otd);
+ QString originalTypeDescription() const;
+
+ void setOriginalTemplateType(const AbstractMetaType &type);
+ const AbstractMetaType *originalTemplateType() const;
+
+ AbstractMetaType getSmartPointerInnerType() const;
+
+ QString getSmartPointerInnerTypeName() const;
+
+ /// Decides and sets the proper usage patter for the current meta type.
+ void decideUsagePattern();
+
+ bool hasTemplateChildren() const;
+
+ /// Is equivalent from the POV of argument passing (differ by const ref)
+ bool isEquivalent(const AbstractMetaType &rhs) const;
+
+ // View on: Type to use for function argument conversion, fex
+ // std::string_view -> std::string for foo(std::string_view);
+ // cf TypeEntry::viewOn()
+ const AbstractMetaType *viewOn() const;
+ void setViewOn(const AbstractMetaType &v);
+
+ static AbstractMetaType createVoid();
+
+ /// Builds an AbstractMetaType object from a QString.
+ /// Returns nullopt if no type could be built from the string.
+ /// \param typeSignature The string describing the type to be built.
+ /// \return A new AbstractMetaType object or nullopt in case of failure.
+ static std::optional<AbstractMetaType>
+ fromString(const QString &typeSignatureIn, QString *errorMessage = nullptr);
+ /// Creates an AbstractMetaType object from a TypeEntry.
+ static AbstractMetaType fromTypeEntry(const TypeEntryCPtr &typeEntry);
+ /// Creates an AbstractMetaType object from an AbstractMetaClass.
+ static AbstractMetaType fromAbstractMetaClass(const AbstractMetaClassCPtr &metaClass);
+
+ static void dereference(QString *type); // "foo" -> "(*foo)"
+ /// Apply the result of shouldDereferenceArgument()
+ static QString dereferencePrefix(qsizetype n); // Return the prefix **/& as as required
+ static void applyDereference(QString *type, qsizetype n);
+ static bool stripDereference(QString *type); // "(*foo)" -> "foo"
+
+ // Query functions for generators
+ /// Check if type is a pointer.
+ bool isPointer() const;
+ /// Helper for field setters: Check for "const QWidget *" (settable field),
+ /// but not "int *const" (read-only field).
+ bool isPointerToConst() const;
+ /// Returns true if the type is a C string (const char *).
+ bool isCString() const;
+ /// Returns true if the type is a void pointer.
+ bool isVoidPointer() const;
+ /// Returns true if the type is a primitive but not a C++ primitive.
+ bool isUserPrimitive() const;
+ /// Returns true if it is an Object Type used as a value.
+ bool isObjectTypeUsedAsValueType() const;
+ /// Returns true if the type passed has a Python wrapper for it.
+ /// Although namespace has a Python wrapper, it's not considered a type.
+ bool isWrapperType() const;
+ /// Checks if the type is an Object/QObject or pointer to Value Type.
+ /// In other words, tells if the type is "T*" and T has a Python wrapper.
+ bool isPointerToWrapperType() const;
+ /// Wrapper type passed by reference
+ bool isWrapperPassedByReference() const;
+ /// Returns true if the type is a C++ integral primitive,
+ /// i.e. bool, char, int, long, and their unsigned counterparts.
+ bool isCppIntegralPrimitive() const;
+ /// Returns true if the type is an extended C++ primitive, a void*,
+ /// a const char*, or a std::string (cf isCppPrimitive()).
+ bool isExtendedCppPrimitive() const;
+ /// Returns whether the underlying type is a value type with copy constructor only
+ bool isValueTypeWithCopyConstructorOnly() const;
+ /// Returns whether the type (function argument) is a value type with
+ /// copy constructor only is passed as value or const-ref and thus
+ /// no default value can be constructed.
+ bool valueTypeWithCopyConstructorOnlyPassed() const;
+ /// Returns whether to generate an opaque container for the type
+ bool generateOpaqueContainer() const;
+ /// Returns whether to generate an opaque container for a getter
+ bool generateOpaqueContainerForGetter(const QString &modifiedType) const;
+
+ /// Types for which libshiboken has built-in primitive converters
+ static const QSet<QString> &cppFloatTypes();
+ static const QSet<QString> &cppSignedCharTypes();
+ static const QSet<QString> &cppUnsignedCharTypes();
+ static const QSet<QString> &cppCharTypes();
+ static const QSet<QString> &cppSignedIntTypes();
+ static const QSet<QString> &cppUnsignedIntTypes();
+ static const QSet<QString> &cppIntegralTypes();
+ static const QSet<QString> &cppPrimitiveTypes();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &debug) const;
+#endif
+
+private:
+ friend size_t qHash(const AbstractMetaType &t, size_t seed = 0) noexcept
+ { return qHash(t.typeEntry().get(), seed); }
+ friend bool comparesEqual(const AbstractMetaType &lhs,
+ const AbstractMetaType &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(AbstractMetaType)
+
+ friend class AbstractMetaTypeData;
+ QSharedDataPointer<AbstractMetaTypeData> d;
+
+ TypeUsagePattern determineUsagePattern() const;
+ QString formatSignature(bool minimal) const;
+ QString formatPythonSignature() const;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const AbstractMetaType &at);
+QDebug operator<<(QDebug d, const AbstractMetaType *at);
+#endif
+
+#endif // ABSTRACTMETALANG_H
diff --git a/sources/shiboken6/ApiExtractor/addedfunction.cpp b/sources/shiboken6/ApiExtractor/addedfunction.cpp
new file mode 100644
index 000000000..9d95b734c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/addedfunction.cpp
@@ -0,0 +1,216 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "addedfunction.h"
+#include "addedfunction_p.h"
+#include "typeparser.h"
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+constexpr auto callOperator = "operator()"_L1;
+
+// Helpers to split a parameter list of <add-function>, <declare-function>
+// (@ denoting names), like
+// "void foo(QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...)"
+namespace AddedFunctionParser {
+
+QDebug operator<<(QDebug d, const Argument &a)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "Argument(type=\"" << a.type << '"';
+ if (!a.name.isEmpty())
+ d << ", name=\"" << a.name << '"';
+ if (!a.defaultValue.isEmpty())
+ d << ", defaultValue=\"" << a.defaultValue << '"';
+ d << ')';
+ return d;
+}
+
+// Helper for finding the end of a function parameter, observing
+// nested template parameters or lists.
+static qsizetype parameterTokenEnd(qsizetype startPos, QStringView paramString)
+{
+ const auto end = paramString.size();
+ int nestingLevel = 0;
+ for (qsizetype p = startPos; p < end; ++p) {
+ switch (paramString.at(p).toLatin1()) {
+ case ',':
+ if (nestingLevel == 0)
+ return p;
+ break;
+ case '<': // templates
+ case '{': // initializer lists of default values
+ case '(': // initialization, function pointers
+ case '[': // array dimensions
+ ++nestingLevel;
+ break;
+ case '>':
+ case '}':
+ case ')':
+ case ']':
+ --nestingLevel;
+ break;
+ }
+ }
+ return end;
+}
+
+// Split a function parameter list into string tokens containing one
+// parameters (including default value, etc).
+static QList<QStringView> splitParameterTokens(QStringView paramString)
+{
+ QList<QStringView> result;
+ qsizetype startPos = 0;
+ for ( ; startPos < paramString.size(); ) {
+ const auto end = parameterTokenEnd(startPos, paramString);
+ result.append(paramString.mid(startPos, end - startPos).trimmed());
+ startPos = end + 1;
+ }
+ return result;
+}
+
+// Split a function parameter list
+Arguments splitParameters(QStringView paramString, QString *errorMessage)
+{
+ Arguments result;
+ const QList<QStringView> tokens = splitParameterTokens(paramString);
+
+ for (const auto &t : tokens) {
+ Argument argument;
+ // Check defaultValue, "int @b@=5"
+ const auto equalPos = t.lastIndexOf(u'=');
+ if (equalPos != -1) {
+ const int defaultValuePos = equalPos + 1;
+ argument.defaultValue =
+ t.mid(defaultValuePos, t.size() - defaultValuePos).trimmed().toString();
+ }
+ QString typeString = (equalPos != -1 ? t.left(equalPos) : t).trimmed().toString();
+ // Check @name@
+ const auto atPos = typeString.indexOf(u'@');
+ if (atPos != -1) {
+ const int namePos = atPos + 1;
+ const int nameEndPos = typeString.indexOf(u'@', namePos);
+ if (nameEndPos == -1) {
+ if (errorMessage != nullptr) {
+ *errorMessage = u"Mismatched @ in \""_s
+ + paramString.toString() + u'"';
+ }
+ return {};
+ }
+ argument.name = typeString.mid(namePos, nameEndPos - namePos).trimmed();
+ typeString.remove(atPos, nameEndPos - atPos + 1);
+ }
+ argument.type = typeString.trimmed();
+ result.append(argument);
+ }
+
+ return result;
+}
+
+} // namespace AddedFunctionParser
+
+AddedFunction::AddedFunction(const QString &name, const QList<Argument> &arguments,
+ const TypeInfo &returnType) :
+ m_name(name),
+ m_arguments(arguments),
+ m_returnType(returnType)
+{
+}
+
+AddedFunction::AddedFunctionPtr
+ AddedFunction::createAddedFunction(const QString &signatureIn, const QString &returnTypeIn,
+ QString *errorMessage)
+
+{
+ errorMessage->clear();
+
+ QList<Argument> arguments;
+ const TypeInfo returnType = returnTypeIn.isEmpty()
+ ? TypeInfo::voidType()
+ : TypeParser::parse(returnTypeIn, errorMessage);
+ if (!errorMessage->isEmpty())
+ return {};
+
+ QStringView signature = QStringView{signatureIn}.trimmed();
+
+ // Skip past "operator()(...)"
+ const auto parenSearchStartPos = signature.startsWith(callOperator)
+ ? callOperator.size() : 0;
+ const auto openParenPos = signature.indexOf(u'(', parenSearchStartPos);
+ if (openParenPos < 0) {
+ return AddedFunctionPtr(new AddedFunction(signature.toString(),
+ arguments, returnType));
+ }
+
+ const QString name = signature.left(openParenPos).trimmed().toString();
+ const auto closingParenPos = signature.lastIndexOf(u')');
+ if (closingParenPos < 0) {
+ *errorMessage = u"Missing closing parenthesis"_s;
+ return {};
+ }
+
+ // Check for "foo() const"
+ bool isConst = false;
+ const auto signatureLength = signature.length();
+ const auto qualifierLength = signatureLength - closingParenPos - 1;
+ if (qualifierLength >= 5
+ && signature.right(qualifierLength).contains(u"const")) {
+ isConst = true;
+ }
+
+ const auto paramString = signature.mid(openParenPos + 1, closingParenPos - openParenPos - 1);
+ const auto params = AddedFunctionParser::splitParameters(paramString, errorMessage);
+ if (params.isEmpty() && !errorMessage->isEmpty())
+ return {};
+ for (const auto &p : params) {
+ TypeInfo type = p.type == u"..."
+ ? TypeInfo::varArgsType() : TypeParser::parse(p.type, errorMessage);
+ if (!errorMessage->isEmpty()) {
+ errorMessage->prepend(u"Unable to parse added function "_s + signatureIn
+ + u": "_s);
+ return {};
+ }
+ arguments.append({type, p.name, p.defaultValue});
+ }
+
+ auto result = std::make_shared<AddedFunction>(name, arguments, returnType);
+ result->setConstant(isConst);
+ return result;
+}
+
+QDebug operator<<(QDebug d, const AddedFunction::Argument &a)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "Argument(";
+ d << a.typeInfo;
+ if (!a.name.isEmpty())
+ d << ' ' << a.name;
+ if (!a.defaultValue.isEmpty())
+ d << " = " << a.defaultValue;
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AddedFunction &af)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AddedFunction(";
+ if (af.access() == AddedFunction::Protected)
+ d << "protected";
+ if (af.isStatic())
+ d << " static";
+ d << af.returnType() << ' ' << af.name() << '(' << af.arguments() << ')';
+ if (af.isConstant())
+ d << " const";
+ if (af.isDeclaration())
+ d << " [declaration]";
+ return d;
+}
diff --git a/sources/shiboken6/ApiExtractor/addedfunction.h b/sources/shiboken6/ApiExtractor/addedfunction.h
new file mode 100644
index 000000000..b8d189b7a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/addedfunction.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ADDEDFUNCTION_H
+#define ADDEDFUNCTION_H
+
+#include "modifications.h"
+#include "parser/typeinfo.h"
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+/// \internal
+/// Struct used to store information about functions added by the typesystem.
+/// This info will be used later to create a fake AbstractMetaFunction which
+/// will be inserted into the right AbstractMetaClass.
+struct AddedFunction
+{
+ using AddedFunctionPtr = std::shared_ptr<AddedFunction>;
+
+ /// Function access types.
+ enum Access {
+ Protected = 0x1,
+ Public = 0x2
+ };
+
+ struct Argument
+ {
+ TypeInfo typeInfo;
+ QString name;
+ QString defaultValue;
+ };
+
+ /// Creates a new AddedFunction with a signature and a return type.
+ explicit AddedFunction(const QString &name, const QList<Argument> &arguments,
+ const TypeInfo &returnType);
+
+ static AddedFunctionPtr createAddedFunction(const QString &signatureIn,
+ const QString &returnTypeIn,
+ QString *errorMessage);
+
+ AddedFunction() = default;
+
+ /// Returns the function name.
+ QString name() const { return m_name; }
+
+ /// Set the function access type.
+ void setAccess(Access access) { m_access = access; }
+
+ /// Returns the function access type.
+ Access access() const { return m_access; }
+
+ /// Returns the function return type.
+ const TypeInfo &returnType() const { return m_returnType; }
+
+ /// Returns a list of argument type infos.
+ const QList<Argument> &arguments() const { return m_arguments; }
+
+ /// Returns true if this is a constant method.
+ bool isConstant() const { return m_isConst; }
+ void setConstant(bool c) { m_isConst = c; };
+
+ /// Set this method static.
+ void setStatic(bool value) { m_isStatic = value; }
+
+ /// Set this method as a classmethod.
+ void setClassMethod(bool value) { m_isClassMethod = value; }
+
+ /// Returns true if this is a static method.
+ bool isStatic() const { return m_isStatic; }
+
+ /// Returns true if this is a class method.
+ bool isClassMethod() const { return m_isClassMethod; }
+
+ bool isDeclaration() const { return m_isDeclaration; } // <declare-function>
+ void setDeclaration(bool value) { m_isDeclaration = value; }
+
+ bool isPythonOverride() const { return m_isPythonOverride; }
+ void setPythonOverride(bool o) { m_isPythonOverride = o; }
+
+ const FunctionModificationList &modifications() const { return m_modifications; }
+ FunctionModificationList &modifications() { return m_modifications; }
+
+ const DocModificationList &docModifications() const { return m_docModifications; }
+ DocModificationList &docModifications() { return m_docModifications; }
+ void addDocModification(const DocModification &m) { m_docModifications.append(m); }
+
+ QString targetLangPackage() const { return m_targetLangPackage; }
+ void setTargetLangPackage(const QString &p) { m_targetLangPackage = p; }
+
+private:
+ QString m_name;
+ QList<Argument> m_arguments;
+ TypeInfo m_returnType;
+ FunctionModificationList m_modifications;
+ DocModificationList m_docModifications;
+ QString m_targetLangPackage;
+ Access m_access = Public;
+ bool m_isConst = false;
+ bool m_isClassMethod = false;
+ bool m_isStatic = false;
+ bool m_isDeclaration = false;
+ bool m_isPythonOverride = false;
+};
+
+QDebug operator<<(QDebug d, const AddedFunction::Argument &a);
+QDebug operator<<(QDebug d, const AddedFunction &af);
+
+#endif // ADDEDFUNCTION_H
diff --git a/sources/shiboken6/ApiExtractor/addedfunction_p.h b/sources/shiboken6/ApiExtractor/addedfunction_p.h
new file mode 100644
index 000000000..40b69a5df
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/addedfunction_p.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ADDEDFUNCTION_P_H
+#define ADDEDFUNCTION_P_H
+
+#include <QtCore/QtCompare>
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtCore/QStringView>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+QT_END_NAMESPACE
+
+// Helpers to split a parameter list of <add-function>, <declare-function>
+// in a separate header for testing purposes
+
+namespace AddedFunctionParser {
+
+struct Argument
+{
+ QString type;
+ QString name;
+ QString defaultValue;
+
+ friend bool comparesEqual(const Argument &lhs, const Argument &rhs) noexcept
+ {
+ return lhs.type == rhs.type && lhs.name == rhs.name
+ && lhs.defaultValue == rhs.defaultValue;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(Argument)
+};
+
+using Arguments = QList<Argument>;
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const Argument &a);
+#endif
+
+Arguments splitParameters(QStringView paramString, QString *errorMessage = nullptr);
+
+} // namespace AddedFunctionParser
+
+#endif // MODIFICATIONS_P_H
diff --git a/sources/shiboken6/ApiExtractor/anystringview_helpers.cpp b/sources/shiboken6/ApiExtractor/anystringview_helpers.cpp
new file mode 100644
index 000000000..35d2d535a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/anystringview_helpers.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "anystringview_helpers.h"
+
+#include <QtCore/QString> // Must go before QAnyStringView for operator<<(QTextStream,QASV)!
+#include <QtCore/QAnyStringView>
+#include <QtCore/QDebug>
+#include <QtCore/QTextStream>
+
+#include <cstring>
+
+QTextStream &operator<<(QTextStream &str, QAnyStringView asv)
+{
+ asv.visit([&str](auto s) { str << s; });
+ return str;
+}
+
+static bool asv_containsImpl(QLatin1StringView v, char c)
+{
+ return v.contains(uint16_t(c));
+}
+
+static bool asv_containsImpl(QUtf8StringView v, char c)
+{
+ return std::strchr(v.data(), c) != nullptr;
+}
+
+static bool asv_containsImpl(QStringView v, char c)
+{
+ return v.contains(uint16_t(c));
+}
+
+bool asv_contains(QAnyStringView asv, char needle)
+{
+ return asv.visit([needle](auto s) { return asv_containsImpl(s, needle); });
+}
+
+static bool asv_containsImpl(QLatin1StringView v, const char *c)
+{
+ return v.contains(QLatin1StringView(c));
+}
+static bool asv_containsImpl(QUtf8StringView v, const char *c)
+{
+ return std::strstr(v.data(), c) != nullptr;
+}
+
+static bool asv_containsImpl(QStringView v, const char *c)
+{
+ return v.contains(QLatin1StringView(c));
+}
+
+bool asv_contains(QAnyStringView asv, const char *needle)
+{
+ return asv.visit([needle](auto s) { return asv_containsImpl(s, needle); });
+}
diff --git a/sources/shiboken6/ApiExtractor/anystringview_helpers.h b/sources/shiboken6/ApiExtractor/anystringview_helpers.h
new file mode 100644
index 000000000..e1e6ab7f0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/anystringview_helpers.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ANYSTRINGVIEW_STREAM_H
+#define ANYSTRINGVIEW_STREAM_H
+
+#include <QtCore/QtClassHelperMacros>
+
+QT_FORWARD_DECLARE_CLASS(QAnyStringView)
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+QTextStream &operator<<(QTextStream &str, QAnyStringView asv);
+
+bool asv_contains(QAnyStringView asv, char needle);
+bool asv_contains(QAnyStringView asv, const char *needle);
+
+#endif // ANYSTRINGVIEW_STREAM_H
diff --git a/sources/shiboken6/ApiExtractor/apiextractor.cpp b/sources/shiboken6/ApiExtractor/apiextractor.cpp
new file mode 100644
index 000000000..786cd0783
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/apiextractor.cpp
@@ -0,0 +1,849 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "apiextractor.h"
+#include "apiextractorresult.h"
+#include "abstractmetaargument.h"
+#include "abstractmetabuilder.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafield.h"
+#include "abstractmetafunction.h"
+#include "abstractmetalang.h"
+#include "codesnip.h"
+#include "exception.h"
+#include "messages.h"
+#include "modifications.h"
+#include "optionsparser.h"
+#include "reporthandler.h"
+#include "typedatabase.h"
+#include "customconversion.h"
+#include "containertypeentry.h"
+#include "primitivetypeentry.h"
+#include "smartpointertypeentry.h"
+#include "typedefentry.h"
+#include "namespacetypeentry.h"
+#include "typesystemtypeentry.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtCore/QTemporaryFile>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+
+using namespace Qt::StringLiterals;
+
+struct InstantiationCollectContext
+{
+ AbstractMetaTypeList instantiatedContainers;
+ InstantiatedSmartPointers instantiatedSmartPointers;
+ QStringList instantiatedContainersNames;
+};
+
+struct ApiExtractorOptions
+{
+ QString m_typeSystemFileName;
+ QFileInfoList m_cppFileNames;
+ HeaderPaths m_includePaths;
+ QStringList m_clangOptions;
+ QString m_logDirectory;
+ LanguageLevel m_languageLevel = LanguageLevel::Default;
+ bool m_skipDeprecated = false;
+};
+
+static inline QString languageLevelDescription()
+{
+ return u"C++ Language level (c++11..c++17, default="_s
+ + QLatin1StringView(clang::languageLevelOption(clang::emulatedCompilerLanguageLevel()))
+ + u')';
+}
+
+QList<OptionDescription> ApiExtractor::options()
+{
+ return {
+ {u"use-global-header"_s,
+ u"Use the global headers in generated code."_s},
+ {u"clang-option"_s,
+ u"Option to be passed to clang"_s},
+ {u"clang-options"_s,
+ u"A comma-separated list of options to be passed to clang"_s},
+ {u"skip-deprecated"_s,
+ u"Skip deprecated functions"_s},
+ {u"-F<path>"_s, {} },
+ {u"framework-include-paths="_s + OptionsParser::pathSyntax(),
+ u"Framework include paths used by the C++ parser"_s},
+ {u"-isystem<path>"_s, {} },
+ {u"system-include-paths="_s + OptionsParser::pathSyntax(),
+ u"System include paths used by the C++ parser"_s},
+ {u"language-level=, -std=<level>"_s,
+ languageLevelDescription()},
+ };
+}
+
+class ApiExtractorOptionsParser : public OptionsParser
+{
+public:
+ explicit ApiExtractorOptionsParser(ApiExtractorOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value,
+ OptionSource source) override;
+
+private:
+ void parseIncludePathOption(const QString &value, HeaderType headerType);
+ void parseIncludePathOption(const QStringList &values, HeaderType headerType);
+ void setLanguageLevel(const QString &value);
+
+ ApiExtractorOptions *m_options;
+};
+
+void ApiExtractorOptionsParser::parseIncludePathOption(const QString &value,
+ HeaderType headerType)
+{
+ if (value.isEmpty())
+ throw Exception(u"Empty value passed to include path option"_s);
+ const auto path = QFile::encodeName(QDir::cleanPath(value));
+ m_options->m_includePaths.append(HeaderPath{path, headerType});
+}
+
+void ApiExtractorOptionsParser::parseIncludePathOption(const QStringList &values,
+ HeaderType headerType)
+{
+ for (const auto &value : values)
+ parseIncludePathOption(value, headerType);
+}
+
+void ApiExtractorOptionsParser::setLanguageLevel(const QString &value)
+{
+ const QByteArray languageLevelBA = value.toLatin1();
+ const LanguageLevel level = clang::languageLevelFromOption(languageLevelBA.constData());
+ if (level == LanguageLevel::Default)
+ throw Exception(msgInvalidLanguageLevel(value));
+ m_options->m_languageLevel = level;
+}
+
+bool ApiExtractorOptionsParser::handleBoolOption(const QString &key, OptionSource source)
+{
+ static const auto isystemOption = "isystem"_L1;
+
+ switch (source) {
+ case OptionSource::CommandLine:
+ case OptionSource::ProjectFile:
+ if (key == u"use-global-header") {
+ AbstractMetaBuilder::setUseGlobalHeader(true);
+ return true;
+ }
+ if (key == u"skip-deprecated") {
+ m_options->m_skipDeprecated = true;
+ return true;
+ }
+ break;
+
+ case OptionSource::CommandLineSingleDash:
+ if (key.startsWith(u'I')) { // Shorthand path arguments -I/usr/include...
+ parseIncludePathOption(key.sliced(1), HeaderType::Standard);
+ return true;
+ }
+ if (key.startsWith(u'F')) {
+ parseIncludePathOption(key.sliced(1), HeaderType::Framework);
+ return true;
+ }
+ if (key.startsWith(isystemOption)) {
+ parseIncludePathOption(key.sliced(isystemOption.size()), HeaderType::System);
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool ApiExtractorOptionsParser::handleOption(const QString &key, const QString &value,
+ OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash) {
+ if (key == u"std") {
+ setLanguageLevel(value);
+ return true;
+ }
+ return false;
+ }
+
+ if (key == u"clang-option") {
+ m_options->m_clangOptions.append(value);
+ return true;
+ }
+ if (key == u"clang-options") {
+ m_options->m_clangOptions.append(value.split(u',', Qt::SkipEmptyParts));
+ return true;
+ }
+ if (key == u"include-paths") {
+ parseIncludePathOption(value.split(QDir::listSeparator(), Qt::SkipEmptyParts),
+ HeaderType::Standard);
+ return true;
+ }
+ if (key == u"framework-include-paths") {
+ parseIncludePathOption(value.split(QDir::listSeparator(), Qt::SkipEmptyParts),
+ HeaderType::Framework);
+ return true;
+ }
+ if (key == u"system-include-paths") {
+ parseIncludePathOption(value.split(QDir::listSeparator(), Qt::SkipEmptyParts),
+ HeaderType::System);
+ return true;
+ }
+ if (key == u"language-level") {
+ setLanguageLevel(value);
+ return true;
+ }
+
+ if (source == OptionSource::ProjectFile) {
+ if (key == u"include-path") {
+ parseIncludePathOption(value, HeaderType::Standard);
+ return true;
+ }
+ if (key == u"framework-include-path") {
+ parseIncludePathOption(value, HeaderType::Framework);
+ return true;
+ }
+ if (key == u"system-include-path") {
+ parseIncludePathOption(value, HeaderType::System);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::shared_ptr<OptionsParser> ApiExtractor::createOptionsParser()
+{
+ return std::make_shared<ApiExtractorOptionsParser>(d);
+}
+
+struct ApiExtractorPrivate : public ApiExtractorOptions
+{
+ bool runHelper(ApiExtractorFlags flags);
+
+ static QString getSimplifiedContainerTypeName(const AbstractMetaType &type);
+ void addInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context,
+ const AbstractMetaType &type,
+ const QString &contextName);
+ void collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context,
+ const AbstractMetaFunctionCPtr &func);
+ void collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context,
+ const AbstractMetaClassCPtr &metaClass);
+ void collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context);
+ void collectInstantiatedOpqaqueContainers(InstantiationCollectContext &context);
+ void collectContainerTypesFromSnippets(InstantiationCollectContext &context);
+ void collectContainerTypesFromConverterMacros(InstantiationCollectContext &context,
+ const QString &code,
+ bool toPythonMacro);
+ void addInstantiatedSmartPointer(InstantiationCollectContext &context,
+ const AbstractMetaType &type);
+
+ AbstractMetaBuilder *m_builder = nullptr;
+};
+
+ApiExtractor::ApiExtractor() :
+ d(new ApiExtractorPrivate)
+{
+}
+
+ApiExtractor::~ApiExtractor()
+{
+ delete d->m_builder;
+ delete d;
+}
+
+HeaderPaths ApiExtractor::includePaths() const
+{
+ return d->m_includePaths;
+}
+
+void ApiExtractor::setLogDirectory(const QString& logDir)
+{
+ d->m_logDirectory = logDir;
+}
+
+void ApiExtractor::setCppFileNames(const QFileInfoList &cppFileName)
+{
+ d->m_cppFileNames = cppFileName;
+}
+
+QFileInfoList ApiExtractor::cppFileNames() const
+{
+ return d->m_cppFileNames;
+}
+
+void ApiExtractor::setTypeSystem(const QString& typeSystemFileName)
+{
+ d->m_typeSystemFileName = typeSystemFileName;
+}
+
+QString ApiExtractor::typeSystem() const
+{
+ return d->m_typeSystemFileName;
+}
+
+const AbstractMetaEnumList &ApiExtractor::globalEnums() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->globalEnums();
+}
+
+const AbstractMetaFunctionCList &ApiExtractor::globalFunctions() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->globalFunctions();
+}
+
+const AbstractMetaClassList &ApiExtractor::classes() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->classes();
+}
+
+const AbstractMetaClassList &ApiExtractor::smartPointers() const
+{
+ Q_ASSERT(d->m_builder);
+ return d->m_builder->smartPointers();
+}
+
+// Add defines required for parsing Qt code headers
+static void addPySideExtensions(QByteArrayList *a)
+{
+ // Make "signals:", "slots:" visible as access specifiers
+ a->append(QByteArrayLiteral("-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))"));
+
+ // Q_PROPERTY is defined as class annotation which does not work since a
+ // sequence of properties will to expand to a sequence of annotations
+ // annotating nothing, causing clang to complain. Instead, define it away in a
+ // static assert with the stringified argument in a ','-operator (cf qdoc).
+ a->append(QByteArrayLiteral("-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__),#type);"));
+
+ // With Qt6, qsimd.h became public header and was included in <QtCore>. That
+ // introduced a conflict with libclang headers on macOS. To be able to include
+ // <QtCore>, we prevent its inclusion by adding its include guard.
+ a->append(QByteArrayLiteral("-DQSIMD_H"));
+}
+
+bool ApiExtractorPrivate::runHelper(ApiExtractorFlags flags)
+{
+ if (m_builder)
+ return false;
+
+ if (!TypeDatabase::instance()->parseFile(m_typeSystemFileName)) {
+ std::cerr << "Cannot parse file: " << qPrintable(m_typeSystemFileName);
+ return false;
+ }
+
+ const QString pattern = QDir::tempPath() + u'/'
+ + m_cppFileNames.constFirst().baseName() + "_XXXXXX.hpp"_L1;
+ QTemporaryFile ppFile(pattern);
+ bool autoRemove = !qEnvironmentVariableIsSet("KEEP_TEMP_FILES");
+ // make sure that a tempfile can be written
+ if (!ppFile.open()) {
+ std::cerr << "could not create tempfile " << qPrintable(pattern)
+ << ": " << qPrintable(ppFile.errorString()) << '\n';
+ return false;
+ }
+ for (const auto &cppFileName : std::as_const(m_cppFileNames)) {
+ ppFile.write("#include \"");
+ ppFile.write(cppFileName.absoluteFilePath().toLocal8Bit());
+ ppFile.write("\"\n");
+ }
+ const QString preprocessedCppFileName = ppFile.fileName();
+ ppFile.close();
+ m_builder = new AbstractMetaBuilder;
+ m_builder->setLogDirectory(m_logDirectory);
+ m_builder->setGlobalHeaders(m_cppFileNames);
+ m_builder->setSkipDeprecated(m_skipDeprecated);
+ m_builder->setHeaderPaths(m_includePaths);
+ m_builder->setApiExtractorFlags(flags);
+
+ QByteArrayList arguments;
+ const auto clangOptionsSize = m_clangOptions.size();
+ arguments.reserve(m_includePaths.size() + clangOptionsSize + 1);
+
+ bool addCompilerSupportArguments = true;
+ if (clangOptionsSize > 0) {
+ qsizetype i = 0;
+ if (m_clangOptions.at(i) == u"-") {
+ ++i;
+ addCompilerSupportArguments = false; // No built-in options
+ }
+ for (; i < clangOptionsSize; ++i)
+ arguments.append(m_clangOptions.at(i).toUtf8());
+ }
+
+ for (const HeaderPath &headerPath : std::as_const(m_includePaths))
+ arguments.append(HeaderPath::includeOption(headerPath));
+ if (flags.testFlag(ApiExtractorFlag::UsePySideExtensions))
+ addPySideExtensions(&arguments);
+ arguments.append(QFile::encodeName(preprocessedCppFileName));
+
+ if (ReportHandler::isDebug(ReportHandler::SparseDebug)) {
+ qCInfo(lcShiboken).noquote().nospace()
+ << "clang language level: " << int(m_languageLevel)
+ << "\nclang arguments: " << arguments;
+ }
+
+ const bool result = m_builder->build(arguments, flags, addCompilerSupportArguments,
+ m_languageLevel);
+ if (!result)
+ autoRemove = false;
+ if (!autoRemove) {
+ ppFile.setAutoRemove(false);
+ std::cerr << "Keeping temporary file: " << qPrintable(QDir::toNativeSeparators(preprocessedCppFileName)) << '\n';
+ }
+ return result;
+}
+
+static inline void classListToCList(const AbstractMetaClassList &list, AbstractMetaClassCList *target)
+{
+ target->reserve(list.size());
+ std::copy(list.cbegin(), list.cend(), std::back_inserter(*target));
+}
+
+std::optional<ApiExtractorResult> ApiExtractor::run(ApiExtractorFlags flags)
+{
+ if (!d->runHelper(flags))
+ return {};
+ InstantiationCollectContext collectContext;
+ d->collectInstantiatedContainersAndSmartPointers(collectContext);
+
+ ApiExtractorResult result;
+ classListToCList(d->m_builder->takeClasses(), &result.m_metaClasses);
+ classListToCList(d->m_builder->takeSmartPointers(), &result.m_smartPointers);
+ result.m_globalFunctions = d->m_builder->globalFunctions();
+ result.m_globalEnums = d->m_builder->globalEnums();
+ result.m_enums = d->m_builder->typeEntryToEnumsHash();
+ result.m_flags = flags;
+ result.m_typedefTargetToName = d->m_builder->typedefTargetToName();
+ qSwap(result.m_instantiatedContainers, collectContext.instantiatedContainers);
+ qSwap(result.m_instantiatedSmartPointers, collectContext.instantiatedSmartPointers);
+ return result;
+}
+
+LanguageLevel ApiExtractor::languageLevel() const
+{
+ return d->m_languageLevel;
+}
+
+QStringList ApiExtractor::clangOptions() const
+{
+ return d->m_clangOptions;
+}
+
+AbstractMetaFunctionPtr
+ ApiExtractor::inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes)
+{
+ return AbstractMetaBuilder::inheritTemplateFunction(function, templateTypes);
+}
+
+AbstractMetaFunctionPtr
+ ApiExtractor::inheritTemplateMember(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaClassPtr &subclass)
+{
+ return AbstractMetaBuilder::inheritTemplateMember(function, templateTypes,
+ templateClass, subclass);
+}
+
+AbstractMetaClassPtr ApiExtractor::inheritTemplateClass(const ComplexTypeEntryPtr &te,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes)
+{
+ return AbstractMetaBuilder::inheritTemplateClass(te, templateClass,
+ templateTypes);
+}
+
+QString ApiExtractorPrivate::getSimplifiedContainerTypeName(const AbstractMetaType &type)
+{
+ const QString signature = type.cppSignature();
+ if (!type.typeEntry()->isContainer() && !type.typeEntry()->isSmartPointer())
+ return signature;
+ QString typeName = signature;
+ if (type.isConstant())
+ typeName.remove(0, sizeof("const ") / sizeof(char) - 1);
+ switch (type.referenceType()) {
+ case NoReference:
+ break;
+ case LValueReference:
+ typeName.chop(1);
+ break;
+ case RValueReference:
+ typeName.chop(2);
+ break;
+ }
+ while (typeName.endsWith(u'*') || typeName.endsWith(u' '))
+ typeName.chop(1);
+ return typeName;
+}
+
+// Strip a "const QSharedPtr<const Foo> &" or similar to "QSharedPtr<Foo>" (PYSIDE-1016/454)
+AbstractMetaType canonicalSmartPtrInstantiation(const AbstractMetaType &type)
+{
+ const AbstractMetaTypeList &instantiations = type.instantiations();
+ Q_ASSERT(instantiations.size() == 1);
+ const bool needsFix = type.isConstant() || type.referenceType() != NoReference;
+ const bool pointeeNeedsFix = instantiations.constFirst().isConstant();
+ if (!needsFix && !pointeeNeedsFix)
+ return type;
+ auto fixedType = type;
+ fixedType.setReferenceType(NoReference);
+ fixedType.setConstant(false);
+ if (pointeeNeedsFix) {
+ auto fixedPointeeType = instantiations.constFirst();
+ fixedPointeeType.setConstant(false);
+ fixedType.setInstantiations(AbstractMetaTypeList(1, fixedPointeeType));
+ }
+ return fixedType;
+}
+
+static inline TypeEntryCPtr pointeeTypeEntry(const AbstractMetaType &smartPtrType)
+{
+ return smartPtrType.instantiations().constFirst().typeEntry();
+}
+
+static AbstractMetaType simplifiedType(AbstractMetaType type)
+{
+ type.setIndirections(0);
+ type.setConstant(false);
+ type.setReferenceType(NoReference);
+ type.decideUsagePattern();
+ return type;
+}
+
+void
+ApiExtractorPrivate::addInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context,
+ const AbstractMetaType &type,
+ const QString &contextName)
+{
+ for (const auto &t : type.instantiations())
+ addInstantiatedContainersAndSmartPointers(context, t, contextName);
+ const auto typeEntry = type.typeEntry();
+ const bool isContainer = typeEntry->isContainer();
+ if (!isContainer
+ && !(typeEntry->isSmartPointer() && typeEntry->generateCode())) {
+ return;
+ }
+ if (type.hasTemplateChildren()) {
+ const auto piece = isContainer ? "container"_L1 : "smart pointer"_L1;
+ QString warning =
+ QString::fromLatin1("Skipping instantiation of %1 '%2' because it has template"
+ " arguments.").arg(piece, type.originalTypeDescription());
+ if (!contextName.isEmpty())
+ warning.append(" Calling context: "_L1 + contextName);
+
+ qCWarning(lcShiboken).noquote().nospace() << warning;
+ return;
+
+ }
+ if (isContainer) {
+ const QString typeName = getSimplifiedContainerTypeName(type);
+ if (!context.instantiatedContainersNames.contains(typeName)) {
+ context.instantiatedContainersNames.append(typeName);
+ context.instantiatedContainers.append(simplifiedType(type));
+ }
+ return;
+ }
+
+ // Is smart pointer. Check if the (const?) pointee is already known for the given
+ // smart pointer type entry.
+ auto pt = pointeeTypeEntry(type);
+ const bool present =
+ std::any_of(context.instantiatedSmartPointers.cbegin(),
+ context.instantiatedSmartPointers.cend(),
+ [typeEntry, pt] (const InstantiatedSmartPointer &smp) {
+ return smp.type.typeEntry() == typeEntry
+ && pointeeTypeEntry(smp.type) == pt;
+ });
+ if (!present)
+ addInstantiatedSmartPointer(context, type);
+}
+
+// Create a modification that invalidates the pointee argument of a smart
+// pointer constructor or reset().
+static FunctionModification invalidateArgMod(const AbstractMetaFunctionCPtr &f, int index = 1)
+{
+ ArgumentModification argMod;
+ argMod.setTargetOwnerShip(TypeSystem::CppOwnership);
+ argMod.setIndex(index);
+ FunctionModification funcMod;
+ funcMod.setSignature(f->minimalSignature());
+ funcMod.setArgument_mods({argMod});
+ return funcMod;
+}
+
+static void addOwnerModification(const AbstractMetaFunctionCList &functions,
+ const ComplexTypeEntryPtr &typeEntry)
+{
+ for (const auto &f : functions) {
+ if (!f->arguments().isEmpty()
+ && f->arguments().constFirst().type().indirections() > 0) {
+ std::const_pointer_cast<AbstractMetaFunction>(f)->clearModificationsCache();
+ typeEntry->addFunctionModification(invalidateArgMod(f));
+ }
+ }
+}
+
+void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContext &context,
+ const AbstractMetaType &type)
+{
+ InstantiatedSmartPointer smp;
+ smp.type = canonicalSmartPtrInstantiation(type);
+ smp.smartPointer = AbstractMetaClass::findClass(m_builder->smartPointers(),
+ type.typeEntry());
+ Q_ASSERT(smp.smartPointer);
+
+ const auto &instantiatedType = type.instantiations().constFirst();
+ const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(smp.smartPointer->typeEntry());
+ QString name = ste->getTargetName(smp.type);
+ auto parentTypeEntry = ste->parent();
+
+ // FIXME PYSIDE 7: Make global scope the default behavior?
+ // Note: Also requires changing SmartPointerTypeEntry::getTargetName()
+ // to not strip namespaces from unnamed instances.
+ const bool globalScope = name.startsWith("::"_L1);
+ if (globalScope) {
+ name.remove(0, 2);
+ parentTypeEntry = typeSystemTypeEntry(ste);
+ }
+
+ auto colonPos = name.lastIndexOf(u"::");
+ const bool withinNameSpace = colonPos != -1;
+ if (withinNameSpace) { // user defined
+ const QString nameSpace = name.left(colonPos);
+ name.remove(0, colonPos + 2);
+ const auto nameSpaces = TypeDatabase::instance()->findNamespaceTypes(nameSpace);
+ if (nameSpaces.isEmpty())
+ throw Exception(msgNamespaceNotFound(name));
+ parentTypeEntry = nameSpaces.constFirst();
+ }
+
+ TypedefEntryPtr typedefEntry(new TypedefEntry(name, ste->name(), ste->version(),
+ parentTypeEntry));
+ typedefEntry->setTargetLangPackage(ste->targetLangPackage());
+ auto instantiationEntry = TypeDatabase::initializeTypeDefEntry(typedefEntry, ste);
+ instantiationEntry->setParent(parentTypeEntry);
+
+ smp.specialized = ApiExtractor::inheritTemplateClass(instantiationEntry, smp.smartPointer,
+ {instantiatedType});
+ Q_ASSERT(smp.specialized);
+ if (parentTypeEntry->type() != TypeEntry::TypeSystemType) { // move class to desired namespace
+ const auto enclClass = AbstractMetaClass::findClass(m_builder->classes(), parentTypeEntry);
+ Q_ASSERT(enclClass);
+ auto specialized = std::const_pointer_cast<AbstractMetaClass>(smp.specialized);
+ specialized->setEnclosingClass(enclClass);
+ enclClass->addInnerClass(specialized);
+ }
+
+ if (instantiationEntry->isComplex()) {
+ addOwnerModification(smp.specialized->queryFunctions(FunctionQueryOption::Constructors),
+ instantiationEntry);
+ if (!ste->resetMethod().isEmpty()) {
+ addOwnerModification(smp.specialized->findFunctions(ste->resetMethod()),
+ instantiationEntry);
+ }
+ }
+
+ context.instantiatedSmartPointers.append(smp);
+}
+
+void
+ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context,
+ const AbstractMetaFunctionCPtr &func)
+{
+ addInstantiatedContainersAndSmartPointers(context, func->type(), func->signature());
+ for (const AbstractMetaArgument &arg : func->arguments()) {
+ const auto argType = arg.type();
+ const auto type = argType.viewOn() != nullptr ? *argType.viewOn() : argType;
+ addInstantiatedContainersAndSmartPointers(context, type, func->signature());
+ }
+}
+
+void
+ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ if (!metaClass->typeEntry()->generateCode())
+ return;
+ for (const auto &func : metaClass->functions())
+ collectInstantiatedContainersAndSmartPointers(context, func);
+ for (const auto &func : metaClass->userAddedPythonOverrides())
+ collectInstantiatedContainersAndSmartPointers(context, func);
+ for (const AbstractMetaField &field : metaClass->fields())
+ addInstantiatedContainersAndSmartPointers(context, field.type(), field.name());
+
+ // The list of inner classes might be extended when smart pointer
+ // instantiations are specified to be in namespaces.
+ const auto &innerClasses = metaClass->innerClasses();
+ for (auto i = innerClasses.size() - 1; i >= 0; --i) {
+ const auto innerClass = innerClasses.at(i);
+ if (!innerClass->typeEntry()->isSmartPointer())
+ collectInstantiatedContainersAndSmartPointers(context, innerClass);
+ }
+}
+
+void
+ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(InstantiationCollectContext &context)
+{
+ collectInstantiatedOpqaqueContainers(context);
+ for (const auto &func : m_builder->globalFunctions())
+ collectInstantiatedContainersAndSmartPointers(context, func);
+ for (const auto &metaClass : m_builder->classes())
+ collectInstantiatedContainersAndSmartPointers(context, metaClass);
+ collectContainerTypesFromSnippets(context);
+}
+
+// Whether to generate an opaque container: If the instantiation type is in
+// the current package or, for primitive types, if the container is in the
+// current package.
+static bool generateOpaqueContainer(const AbstractMetaType &type,
+ const TypeSystemTypeEntryCPtr &moduleEntry)
+{
+ auto te = type.instantiations().constFirst().typeEntry();
+ auto typeModuleEntry = typeSystemTypeEntry(te);
+ return typeModuleEntry == moduleEntry
+ || (te->isPrimitive() && typeSystemTypeEntry(type.typeEntry()) == moduleEntry);
+}
+
+void ApiExtractorPrivate::collectInstantiatedOpqaqueContainers(InstantiationCollectContext &context)
+{
+ // Add all instantiations of opaque containers for types from the current
+ // module.
+ auto *td = TypeDatabase::instance();
+ const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ const auto &containers = td->containerTypes();
+ for (const auto &container : containers) {
+ for (const auto &oc : container->opaqueContainers()) {
+ QString errorMessage;
+ const QString typeName = container->qualifiedCppName() + oc.templateParameters();
+ auto typeOpt = AbstractMetaType::fromString(typeName, &errorMessage);
+ if (typeOpt.has_value()
+ && generateOpaqueContainer(typeOpt.value(), moduleEntry)) {
+ addInstantiatedContainersAndSmartPointers(context, typeOpt.value(),
+ u"opaque containers"_s);
+ }
+ }
+ }
+}
+
+static void getCode(QStringList &code, const CodeSnipList &codeSnips)
+{
+ for (const CodeSnip &snip : std::as_const(codeSnips))
+ code.append(snip.code());
+}
+
+static void getCode(QStringList &code, const TypeEntryCPtr &type)
+{
+ if (type->isComplex())
+ getCode(code, std::static_pointer_cast<const ComplexTypeEntry>(type)->codeSnips());
+ else if (type->isTypeSystem())
+ getCode(code, std::static_pointer_cast<const TypeSystemTypeEntry>(type)->codeSnips());
+
+ auto customConversion = CustomConversion::getCustomConversion(type);
+ if (!customConversion)
+ return;
+
+ if (!customConversion->nativeToTargetConversion().isEmpty())
+ code.append(customConversion->nativeToTargetConversion());
+
+ const auto &toCppConversions = customConversion->targetToNativeConversions();
+ if (toCppConversions.isEmpty())
+ return;
+
+ for (const auto &toNative : std::as_const(toCppConversions))
+ code.append(toNative.conversion());
+}
+
+void ApiExtractorPrivate::collectContainerTypesFromSnippets(InstantiationCollectContext &context)
+{
+ QStringList snips;
+ auto *td = TypeDatabase::instance();
+ const PrimitiveTypeEntryCList &primitiveTypeList = td->primitiveTypes();
+ for (const auto &type : primitiveTypeList)
+ getCode(snips, type);
+ const ContainerTypeEntryCList &containerTypeList = td->containerTypes();
+ for (const auto &type : containerTypeList)
+ getCode(snips, type);
+ for (const auto &metaClass : m_builder->classes())
+ getCode(snips, metaClass->typeEntry());
+
+ const auto moduleEntry = td->defaultTypeSystemType();
+ Q_ASSERT(moduleEntry);
+ getCode(snips, moduleEntry);
+
+ for (const auto &func : m_builder->globalFunctions())
+ getCode(snips, func->injectedCodeSnips());
+
+ for (const QString &code : std::as_const(snips)) {
+ collectContainerTypesFromConverterMacros(context, code, true);
+ collectContainerTypesFromConverterMacros(context, code, false);
+ }
+}
+
+void
+ApiExtractorPrivate::collectContainerTypesFromConverterMacros(InstantiationCollectContext &context,
+ const QString &code,
+ bool toPythonMacro)
+{
+ QString convMacro = toPythonMacro ? u"%CONVERTTOPYTHON["_s : u"%CONVERTTOCPP["_s;
+ const qsizetype offset = toPythonMacro ? sizeof("%CONVERTTOPYTHON") : sizeof("%CONVERTTOCPP");
+ qsizetype start = 0;
+ QString errorMessage;
+ while ((start = code.indexOf(convMacro, start)) != -1) {
+ int end = code.indexOf(u']', start);
+ start += offset;
+ if (code.at(start) != u'%') {
+ QString typeString = code.mid(start, end - start);
+ auto type = AbstractMetaType::fromString(typeString, &errorMessage);
+ if (type.has_value()) {
+ const QString &d = type->originalTypeDescription();
+ addInstantiatedContainersAndSmartPointers(context, type.value(), d);
+ } else {
+ QString m;
+ QTextStream(&m) << __FUNCTION__ << ": Cannot translate type \""
+ << typeString << "\": " << errorMessage;
+ throw Exception(m);
+ }
+ }
+ start = end;
+ }
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+template <class Container>
+static void debugFormatSequence(QDebug &d, const char *key, const Container& c)
+{
+ if (c.isEmpty())
+ return;
+ const auto begin = c.begin();
+ d << "\n " << key << '[' << c.size() << "]=(";
+ for (auto it = begin, end = c.end(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << *it;
+ }
+ d << ')';
+}
+
+QDebug operator<<(QDebug d, const ApiExtractor &ae)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ if (ReportHandler::debugLevel() >= ReportHandler::FullDebug)
+ d.setVerbosity(3); // Trigger verbose output of AbstractMetaClass
+ d << "ApiExtractor(typeSystem=\"" << ae.typeSystem() << "\", cppFileNames=\""
+ << ae.cppFileNames() << ", ";
+ ae.d->m_builder->formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/apiextractor.h b/sources/shiboken6/ApiExtractor/apiextractor.h
new file mode 100644
index 000000000..7bff5c252
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/apiextractor.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef APIEXTRACTOR_H
+#define APIEXTRACTOR_H
+
+#include "abstractmetalang_typedefs.h"
+#include "apiextractorflags.h"
+#include "header_paths.h"
+#include "clangparser/compilersupport.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QFileInfoList>
+#include <QtCore/QStringList>
+
+#include <optional>
+
+class ApiExtractorResult;
+class AbstractMetaClass;
+class AbstractMetaEnum;
+class AbstractMetaFunction;
+class ComplexTypeEntry;
+struct OptionDescription;
+class OptionsParser;
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QIODevice;
+QT_END_NAMESPACE
+
+struct ApiExtractorPrivate;
+
+class ApiExtractor
+{
+public:
+ Q_DISABLE_COPY_MOVE(ApiExtractor)
+
+ ApiExtractor();
+ ~ApiExtractor();
+
+ static QList<OptionDescription> options();
+ std::shared_ptr<OptionsParser> createOptionsParser();
+
+ void setTypeSystem(const QString& typeSystemFileName);
+ QString typeSystem() const;
+ void setCppFileNames(const QFileInfoList &cppFileNames);
+ QFileInfoList cppFileNames() const;
+ HeaderPaths includePaths() const;
+ void setLogDirectory(const QString& logDir);
+ LanguageLevel languageLevel() const;
+ QStringList clangOptions() const;
+
+ const AbstractMetaEnumList &globalEnums() const;
+ const AbstractMetaFunctionCList &globalFunctions() const;
+ const AbstractMetaClassList &classes() const;
+ const AbstractMetaClassList &smartPointers() const;
+
+ std::optional<ApiExtractorResult> run(ApiExtractorFlags flags);
+
+ /// Forwards to AbstractMetaBuilder::inheritTemplateFunction()
+ static AbstractMetaFunctionPtr
+ inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes);
+
+ /// Forwards to AbstractMetaBuilder::inheritTemplateMember()
+ static AbstractMetaFunctionPtr
+ inheritTemplateMember(const AbstractMetaFunctionCPtr &function,
+ const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaClassPtr &subclass);
+
+ /// Forwards to AbstractMetaBuilder::inheritTemplateClass()
+ static AbstractMetaClassPtr
+ inheritTemplateClass(const ComplexTypeEntryPtr &te,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes);
+
+private:
+ ApiExtractorPrivate *d;
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend QDebug operator<<(QDebug d, const ApiExtractor &ae);
+#endif
+};
+
+#endif // APIEXTRACTOR_H
+
diff --git a/sources/shiboken6/ApiExtractor/apiextractorflags.h b/sources/shiboken6/ApiExtractor/apiextractorflags.h
new file mode 100644
index 000000000..6f69b8b77
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/apiextractorflags.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef APIEXTRACTORFLAGS_H
+#define APIEXTRACTORFLAGS_H
+
+#include <QtCore/QFlags>
+
+enum class ApiExtractorFlag
+{
+ UsePySideExtensions = 0x1,
+ AvoidProtectedHack = 0x2
+};
+
+Q_DECLARE_FLAGS(ApiExtractorFlags, ApiExtractorFlag)
+Q_DECLARE_OPERATORS_FOR_FLAGS(ApiExtractorFlags)
+
+#endif // APIEXTRACTORFLAGS_H
diff --git a/sources/shiboken6/ApiExtractor/apiextractorresult.cpp b/sources/shiboken6/ApiExtractor/apiextractorresult.cpp
new file mode 100644
index 000000000..b53ffa9b6
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/apiextractorresult.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "apiextractorresult.h"
+#include "abstractmetalang.h"
+#include "abstractmetaenum.h"
+
+#include "enumtypeentry.h"
+#include "flagstypeentry.h"
+
+ApiExtractorResult::ApiExtractorResult() = default;
+
+ApiExtractorResult::ApiExtractorResult(const ApiExtractorResult &) = default;
+
+ApiExtractorResult &ApiExtractorResult::operator=(const ApiExtractorResult &) = default;
+
+ApiExtractorResult::ApiExtractorResult(ApiExtractorResult &&) noexcept = default;
+
+ApiExtractorResult &ApiExtractorResult::operator=(ApiExtractorResult &&) noexcept = default;
+
+ApiExtractorResult::~ApiExtractorResult() = default;
+
+const AbstractMetaEnumList &ApiExtractorResult::globalEnums() const
+{
+ return m_globalEnums;
+}
+
+const AbstractMetaFunctionCList &ApiExtractorResult::globalFunctions() const
+{
+ return m_globalFunctions;
+}
+
+const AbstractMetaClassCList &ApiExtractorResult::classes() const
+{
+ return m_metaClasses;
+}
+
+const AbstractMetaClassCList &ApiExtractorResult::smartPointers() const
+{
+ return m_smartPointers;
+}
+
+const AbstractMetaTypeList &ApiExtractorResult::instantiatedContainers() const
+{
+ return m_instantiatedContainers;
+}
+
+const InstantiatedSmartPointers &ApiExtractorResult::instantiatedSmartPointers() const
+{
+ return m_instantiatedSmartPointers;
+}
+
+std::optional<InstantiatedSmartPointer>
+ ApiExtractorResult::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const
+{
+ for (const auto &smp : m_instantiatedSmartPointers) {
+ const auto &i = smp.type;
+ if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee)
+ return smp;
+ }
+ return std::nullopt;
+}
+
+const QMultiHash<QString, QString> &ApiExtractorResult::typedefTargetToName() const
+{
+ return m_typedefTargetToName;
+}
+
+ApiExtractorFlags ApiExtractorResult::flags() const
+{
+ return m_flags;
+}
+
+void ApiExtractorResult::setFlags(ApiExtractorFlags f)
+{
+ m_flags = f;
+}
+
+std::optional<AbstractMetaEnum>
+ ApiExtractorResult::findAbstractMetaEnum(TypeEntryCPtr typeEntry) const
+{
+ if (typeEntry && typeEntry->isFlags())
+ typeEntry = std::static_pointer_cast<const FlagsTypeEntry>(typeEntry)->originator();
+ const auto it = m_enums.constFind(typeEntry);
+ if (it == m_enums.constEnd())
+ return {};
+ return it.value();
+}
+
+AbstractMetaFunctionCList ApiExtractorResult::implicitConversions(const TypeEntryCPtr &type) const
+{
+ if (type->isValue()) {
+ if (auto metaClass = AbstractMetaClass::findClass(m_metaClasses, type))
+ return metaClass->implicitConversions();
+ }
+ return {};
+}
+
+AbstractMetaFunctionCList ApiExtractorResult::implicitConversions(const AbstractMetaType &metaType) const
+{
+ return implicitConversions(metaType.typeEntry());
+}
diff --git a/sources/shiboken6/ApiExtractor/apiextractorresult.h b/sources/shiboken6/ApiExtractor/apiextractorresult.h
new file mode 100644
index 000000000..b2aae88ed
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/apiextractorresult.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef APIEXTRACTORRESULT_H
+#define APIEXTRACTORRESULT_H
+
+#include "apiextractorflags.h"
+#include "abstractmetatype.h"
+#include "abstractmetalang_typedefs.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QMultiHash>
+
+#include <optional>
+
+class ApiExtractorResultData;
+
+struct InstantiatedSmartPointer
+{
+ AbstractMetaClassCPtr smartPointer; // Template class
+ AbstractMetaClassCPtr specialized; // Specialized for type
+ AbstractMetaType type;
+};
+
+using InstantiatedSmartPointers = QList<InstantiatedSmartPointer>;
+
+/// Result of an ApiExtractor run.
+class ApiExtractorResult
+{
+public:
+ ApiExtractorResult();
+ ApiExtractorResult(const ApiExtractorResult &);
+ ApiExtractorResult &operator=(const ApiExtractorResult &);
+ ApiExtractorResult(ApiExtractorResult &&) noexcept;
+ ApiExtractorResult &operator=(ApiExtractorResult &&) noexcept;
+ ~ApiExtractorResult();
+
+ const AbstractMetaEnumList &globalEnums() const;
+ const AbstractMetaFunctionCList &globalFunctions() const;
+ const AbstractMetaClassCList &classes() const;
+ const AbstractMetaClassCList &smartPointers() const;
+
+ const AbstractMetaTypeList &instantiatedContainers() const;
+ const InstantiatedSmartPointers &instantiatedSmartPointers() const;
+ std::optional<InstantiatedSmartPointer>
+ findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const;
+
+ const QMultiHash<QString, QString> &typedefTargetToName() const;
+
+ // Query functions for the generators
+ std::optional<AbstractMetaEnum>
+ findAbstractMetaEnum(TypeEntryCPtr typeEntry) const;
+
+ /// Retrieves a list of constructors used in implicit conversions
+ /// available on the given type. The TypeEntry must be a value-type
+ /// or else it will return an empty list.
+ /// \param type a TypeEntry that is expected to be a value-type
+ /// \return a list of constructors that could be used as implicit converters
+ AbstractMetaFunctionCList implicitConversions(const TypeEntryCPtr &type) const;
+ AbstractMetaFunctionCList implicitConversions(const AbstractMetaType &metaType) const;
+
+ ApiExtractorFlags flags() const;
+ void setFlags(ApiExtractorFlags f);
+
+private:
+ AbstractMetaClassCList m_metaClasses;
+ AbstractMetaClassCList m_smartPointers;
+ AbstractMetaFunctionCList m_globalFunctions;
+ AbstractMetaEnumList m_globalEnums;
+ AbstractMetaTypeList m_instantiatedContainers;
+ InstantiatedSmartPointers m_instantiatedSmartPointers;
+ QHash<TypeEntryCPtr, AbstractMetaEnum> m_enums;
+ QMultiHash<QString, QString> m_typedefTargetToName;
+ ApiExtractorFlags m_flags;
+
+ friend class ApiExtractor;
+};
+
+#endif // APIEXTRACTORRESULT_H
diff --git a/sources/shiboken6/ApiExtractor/arraytypeentry.h b/sources/shiboken6/ApiExtractor/arraytypeentry.h
new file mode 100644
index 000000000..5b9bb191e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/arraytypeentry.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ARRAYTYPEENTRY_H
+#define ARRAYTYPEENTRY_H
+
+#include "typesystem.h"
+
+class ArrayTypeEntryPrivate;
+
+class ArrayTypeEntry : public TypeEntry
+{
+public:
+ explicit ArrayTypeEntry(const TypeEntryCPtr &nested_type, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ void setNestedTypeEntry(const TypeEntryPtr &nested);
+ TypeEntryCPtr nestedTypeEntry() const;
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit ArrayTypeEntry(ArrayTypeEntryPrivate *d);
+
+ QString buildTargetLangName() const override;
+};
+
+#endif // ARRAYTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
new file mode 100644
index 000000000..31e7efb05
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
@@ -0,0 +1,1296 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "clangbuilder.h"
+#include "compilersupport.h"
+#include "clangutils.h"
+#include "clangdebugutils.h"
+
+#include <codemodel.h>
+#include <reporthandler.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStack>
+#include <QtCore/QList>
+
+#include <cstring>
+#include <ctype.h>
+
+using namespace Qt::StringLiterals;
+
+namespace clang {
+
+static inline bool isClassCursor(const CXCursor &c)
+{
+ return c.kind == CXCursor_ClassDecl || c.kind == CXCursor_StructDecl
+ || c.kind == CXCursor_ClassTemplate
+ || c.kind == CXCursor_ClassTemplatePartialSpecialization;
+}
+
+static inline bool isClassOrNamespaceCursor(const CXCursor &c)
+{
+ return c.kind == CXCursor_Namespace || isClassCursor(c);
+}
+
+static inline bool withinClassDeclaration(const CXCursor &cursor)
+{
+ return isClassCursor(clang_getCursorLexicalParent(cursor));
+}
+
+static QString fixTypeName(QString t)
+{
+ // Fix "Foo &" -> "Foo&", similarly "Bar **" -> "Bar**"
+ auto pos = t.size() - 1;
+ for (; pos >= 0 && (t.at(pos) == u'&' || t.at(pos) == u'*'); --pos) {}
+ if (pos > 0 && t.at(pos) == u' ')
+ t.remove(pos, 1);
+ return t;
+}
+
+// Insert template parameter to class name: "Foo<>" -> "Foo<T1>" -> "Foo<T1,T2>"
+// This needs to be done immediately when template parameters are encountered since
+// the class name "Foo<T1,T2>" is the scope for nested items.
+static bool insertTemplateParameterIntoClassName(const QString &parmName, QString *name)
+{
+ if (Q_UNLIKELY(!name->endsWith(u'>')))
+ return false;
+ const bool needsComma = name->at(name->size() - 2) != u'<';
+ const auto insertionPos = name->size() - 1;
+ name->insert(insertionPos, parmName);
+ if (needsComma)
+ name->insert(insertionPos, u',');
+ return true;
+}
+
+static inline bool insertTemplateParameterIntoClassName(const QString &parmName,
+ const ClassModelItem &item)
+{
+ QString name = item->name();
+ const bool result = insertTemplateParameterIntoClassName(parmName, &name);
+ item->setName(name);
+ return result;
+}
+
+static inline Access accessPolicy(CX_CXXAccessSpecifier access)
+{
+ Access result = Access::Public;
+ switch (access) {
+ case CX_CXXProtected:
+ result = Access::Protected;
+ break;
+ case CX_CXXPrivate:
+ result = Access::Private;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+static bool isSigned(CXTypeKind kind)
+{
+ switch (kind) {
+ case CXType_UChar:
+ case CXType_Char16:
+ case CXType_Char32:
+ case CXType_UShort:
+ case CXType_UInt:
+ case CXType_ULong:
+ case CXType_ULongLong:
+ case CXType_UInt128:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+class BuilderPrivate {
+public:
+ Q_DISABLE_COPY_MOVE(BuilderPrivate)
+
+ enum class SpecialSystemHeader {
+ None,
+ Types,
+ OpenGL,
+ WhiteListed,
+ WhiteListedPath
+ };
+
+ using CursorClassHash = QHash<CXCursor, ClassModelItem>;
+ using TypeInfoHash = QHash<CXType, TypeInfo>;
+
+ explicit BuilderPrivate(BaseVisitor *bv) : m_baseVisitor(bv), m_model(new CodeModel)
+ {
+ m_scopeStack.push(NamespaceModelItem(new _FileModelItem(m_model)));
+ }
+ ~BuilderPrivate()
+ {
+ delete m_model;
+ }
+
+ // Determine scope from top item. Note that the scope list does not necessarily
+ // match the scope stack in case of forward-declared inner classes whose definition
+ // appears in the translation unit while the scope is the outer class.
+ void updateScope()
+ {
+ if (m_scopeStack.size() <= 1)
+ m_scope.clear();
+ else
+ m_scope = m_scopeStack.back()->scope() << m_scopeStack.back()->name();
+ }
+
+ void pushScope(const ScopeModelItem &i)
+ {
+ m_scopeStack.push(i);
+ updateScope();
+ }
+
+ void popScope()
+ {
+ m_scopeStack.back()->purgeClassDeclarations();
+ m_scopeStack.pop();
+ updateScope();
+ }
+
+ bool addClass(const CXCursor &cursor, CodeModel::ClassType t);
+ FunctionModelItem createFunction(const CXCursor &cursor,
+ CodeModel::FunctionType t = CodeModel::Normal,
+ bool isTemplateCode = false);
+ FunctionModelItem createMemberFunction(const CXCursor &cursor,
+ bool isTemplateCode = false);
+ void qualifyConstructor(const CXCursor &cursor);
+ TypeInfo createTypeInfoUncached(const CXType &type,
+ bool *cacheable = nullptr) const;
+ TypeInfo createTypeInfo(const CXType &type) const;
+ TypeInfo createTypeInfo(const CXCursor &cursor) const
+ { return createTypeInfo(clang_getCursorType(cursor)); }
+ void addTemplateInstantiations(const CXType &type,
+ QString *typeName,
+ TypeInfo *t) const;
+ bool addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const;
+
+ void addTypeDef(const CXCursor &cursor, const CXType &cxType);
+ ClassModelItem currentTemplateClass() const;
+ void startTemplateTypeAlias(const CXCursor &cursor);
+ void endTemplateTypeAlias(const CXCursor &typeAliasCursor);
+
+ TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const;
+ TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const;
+ void addField(const CXCursor &cursor);
+
+ static QString cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor);
+ std::pair<QString, ClassModelItem> getBaseClass(CXType type) const;
+ void addBaseClass(const CXCursor &cursor);
+
+ SpecialSystemHeader specialSystemHeader(const QString &fileName) const;
+ bool visitHeader(const QString &fileName) const;
+ static const char *specialSystemHeaderReason(SpecialSystemHeader sh);
+
+ void setFileName(const CXCursor &cursor, _CodeModelItem *item);
+
+ BaseVisitor *m_baseVisitor;
+ CodeModel *m_model;
+
+ QStack<ScopeModelItem> m_scopeStack;
+ QStringList m_scope;
+ // Store all classes by cursor so that base classes can be found and inner
+ // classes can be correctly parented in case of forward-declared inner classes
+ // (QMetaObject::Connection)
+ CursorClassHash m_cursorClassHash;
+
+ mutable TypeInfoHash m_typeInfoHash; // Cache type information
+ mutable QHash<QString, TemplateTypeAliasModelItem> m_templateTypeAliases;
+
+ ClassModelItem m_currentClass;
+ EnumModelItem m_currentEnum;
+ FunctionModelItem m_currentFunction;
+ ArgumentModelItem m_currentArgument;
+ VariableModelItem m_currentField;
+ TemplateTypeAliasModelItem m_currentTemplateTypeAlias;
+ QStringList m_forceProcessSystemIncludes; // files, like "memory"
+ QStringList m_forceProcessSystemIncludePaths; // paths, like "/usr/include/Qt/"
+ QString m_usingTypeRef; // Base classes in "using Base::member;"
+ bool m_withinUsingDeclaration = false;
+
+ int m_anonymousEnumCount = 0;
+ CodeModel::FunctionType m_currentFunctionType = CodeModel::Normal;
+ bool m_withinFriendDecl = false;
+ mutable QHash<QString, SpecialSystemHeader> m_systemHeaders;
+};
+
+bool BuilderPrivate::addClass(const CXCursor &cursor, CodeModel::ClassType t)
+{
+ QString className = getCursorSpelling(cursor);
+ m_currentClass.reset(new _ClassModelItem(m_model, className));
+ setFileName(cursor, m_currentClass.get());
+ m_currentClass->setClassType(t);
+ // Some inner class? Note that it does not need to be (lexically) contained in a
+ // class since it is possible to forward declare an inner class:
+ // class QMetaObject { class Connection; }
+ // class QMetaObject::Connection {}
+ const CXCursor semPar = clang_getCursorSemanticParent(cursor);
+ if (isClassCursor(semPar)) {
+ const CursorClassHash::const_iterator it = m_cursorClassHash.constFind(semPar);
+ if (it == m_cursorClassHash.constEnd()) {
+ QString message;
+ QTextStream(&message) << "Unable to find containing class \""
+ << getCursorSpelling(semPar) << "\" of inner class \""
+ << className << "\".";
+ // PYSIDE-1501: Has been observed to fail for inner class of
+ // template with separated implementation where a forward
+ // declaration of the outer template is reported (Boost).
+ const auto severity = semPar.kind == CXCursor_ClassTemplate
+ ? CXDiagnostic_Warning : CXDiagnostic_Error;
+ const Diagnostic d(message, cursor, severity);
+ qWarning() << d;
+ m_baseVisitor->appendDiagnostic(d);
+ return false;
+ }
+ const ClassModelItem &containingClass = it.value();
+ containingClass->addClass(m_currentClass);
+ m_currentClass->setScope(containingClass->scope() << containingClass->name());
+ } else {
+ m_currentClass->setScope(m_scope);
+ m_scopeStack.back()->addClass(m_currentClass);
+ }
+ pushScope(m_currentClass);
+ m_cursorClassHash.insert(cursor, m_currentClass);
+ return true;
+}
+
+static QString msgCannotDetermineException(const std::string_view &snippetV)
+{
+ const auto newLine = snippetV.find('\n'); // Multiline noexcept specifications have been found in Qt
+ const bool truncate = newLine != std::string::npos;
+ const qsizetype length = qsizetype(truncate ? newLine : snippetV.size());
+ QString snippet = QString::fromUtf8(snippetV.data(), length);
+ if (truncate)
+ snippet += "..."_L1;
+
+ return u"Cannot determine exception specification: \""_s + snippet + u'"';
+}
+
+// Return whether noexcept(<value>) throws. noexcept() takes a constexpr value.
+// Try to determine the simple cases (true|false) via code snippet.
+static ExceptionSpecification computedExceptionSpecificationFromClang(BaseVisitor *bv,
+ const CXCursor &cursor,
+ bool isTemplateCode)
+{
+ const std::string_view snippet = bv->getCodeSnippet(cursor);
+ if (snippet.empty())
+ return ExceptionSpecification::Unknown; // Macro expansion, cannot tell
+ if (snippet.find("noexcept(false)") != std::string::npos)
+ return ExceptionSpecification::Throws;
+ if (snippet.find("noexcept(true)") != std::string::npos)
+ return ExceptionSpecification::NoExcept;
+ // Warn about it unless it is some form of template code where it is common
+ // to have complicated code, which is of no concern to shiboken, like:
+ // "QList::emplace(T) noexcept(is_pod<T>)".
+ if (!isTemplateCode && ReportHandler::isDebug(ReportHandler::FullDebug)) {
+ const Diagnostic d(msgCannotDetermineException(snippet), cursor, CXDiagnostic_Warning);
+ qWarning() << d;
+ bv->appendDiagnostic(d);
+ }
+ return ExceptionSpecification::Unknown;
+}
+
+static ExceptionSpecification exceptionSpecificationFromClang(BaseVisitor *bv,
+ const CXCursor &cursor,
+ bool isTemplateCode)
+{
+ const auto ce = clang_getCursorExceptionSpecificationType(cursor);
+ switch (ce) {
+ case CXCursor_ExceptionSpecificationKind_ComputedNoexcept:
+ return computedExceptionSpecificationFromClang(bv, cursor, isTemplateCode);
+ case CXCursor_ExceptionSpecificationKind_BasicNoexcept:
+ case CXCursor_ExceptionSpecificationKind_DynamicNone: // throw()
+ case CXCursor_ExceptionSpecificationKind_NoThrow:
+ return ExceptionSpecification::NoExcept;
+ case CXCursor_ExceptionSpecificationKind_Dynamic: // throw(t1..)
+ case CXCursor_ExceptionSpecificationKind_MSAny: // throw(...)
+ return ExceptionSpecification::Throws;
+ default:
+ // CXCursor_ExceptionSpecificationKind_None,
+ // CXCursor_ExceptionSpecificationKind_Unevaluated,
+ // CXCursor_ExceptionSpecificationKind_Uninstantiated
+ break;
+ }
+ return ExceptionSpecification::Unknown;
+}
+
+FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor,
+ CodeModel::FunctionType t,
+ bool isTemplateCode)
+{
+ QString name = getCursorSpelling(cursor);
+ // Apply type fixes to "operator X &" -> "operator X&"
+ if (name.startsWith(u"operator "))
+ name = fixTypeName(name);
+ auto result = std::make_shared<_FunctionModelItem>(m_model, name);
+ setFileName(cursor, result.get());
+ const auto type = clang_getCursorResultType(cursor);
+ result->setType(createTypeInfo(type));
+ result->setScopeResolution(hasScopeResolution(type));
+ result->setFunctionType(t);
+ result->setScope(m_scope);
+ result->setStatic(clang_Cursor_getStorageClass(cursor) == CX_SC_Static);
+ result->setExceptionSpecification(exceptionSpecificationFromClang(m_baseVisitor, cursor, isTemplateCode));
+ switch (clang_getCursorAvailability(cursor)) {
+ case CXAvailability_Available:
+ break;
+ case CXAvailability_Deprecated:
+ result->setAttribute(FunctionAttribute::Deprecated);
+ break;
+ case CXAvailability_NotAvailable: // "Foo(const Foo&) = delete;"
+ result->setDeleted(true);
+ break;
+ case CXAvailability_NotAccessible:
+ break;
+ }
+ return result;
+}
+
+static inline CodeModel::FunctionType functionTypeFromCursor(const CXCursor &cursor)
+{
+ CodeModel::FunctionType result = CodeModel::Normal;
+ switch (cursor.kind) {
+ case CXCursor_Constructor:
+ if (clang_CXXConstructor_isCopyConstructor(cursor) != 0)
+ result = CodeModel::CopyConstructor;
+ else if (clang_CXXConstructor_isMoveConstructor(cursor) != 0)
+ result = CodeModel::MoveConstructor;
+ else
+ result = CodeModel::Constructor;
+ break;
+ case CXCursor_Destructor:
+ result = CodeModel::Destructor;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+FunctionModelItem BuilderPrivate::createMemberFunction(const CXCursor &cursor,
+ bool isTemplateCode)
+{
+ const CodeModel::FunctionType functionType =
+ m_currentFunctionType == CodeModel::Signal || m_currentFunctionType == CodeModel::Slot
+ ? m_currentFunctionType // by annotation
+ : functionTypeFromCursor(cursor);
+ isTemplateCode |= m_currentClass->name().endsWith(u'>');
+ auto result = createFunction(cursor, functionType, isTemplateCode);
+ result->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor)));
+ result->setConstant(clang_CXXMethod_isConst(cursor) != 0);
+ result->setAttribute(FunctionAttribute::Static, clang_CXXMethod_isStatic(cursor) != 0);
+ result->setAttribute(FunctionAttribute::Virtual, clang_CXXMethod_isVirtual(cursor) != 0);
+ result->setAttribute(FunctionAttribute::Abstract, clang_CXXMethod_isPureVirtual(cursor) != 0);
+ return result;
+}
+
+// For CXCursor_Constructor, on endToken().
+void BuilderPrivate::qualifyConstructor(const CXCursor &cursor)
+{
+ // Clang does not tell us whether a constructor is explicit, preventing it
+ // from being used for implicit conversions. Try to guess whether a
+ // constructor is explicit in the C++99 sense (1 parameter) by checking for
+ // isConvertingConstructor() == 0. Fixme: The notion of "isConvertingConstructor"
+ // should be used in the code model instead of "explicit"
+ if (clang_CXXConstructor_isDefaultConstructor(cursor) == 0
+ && m_currentFunction->arguments().size() == 1
+ && clang_CXXConstructor_isCopyConstructor(cursor) == 0
+ && clang_CXXConstructor_isMoveConstructor(cursor) == 0) {
+ m_currentFunction->setAttribute(FunctionAttribute::Explicit,
+ clang_CXXConstructor_isConvertingConstructor(cursor) == 0);
+ }
+}
+
+TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCursor &cursor) const
+{
+ return std::make_shared<_TemplateParameterModelItem>(m_model, getCursorSpelling(cursor));
+}
+
+TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const
+{
+ TemplateParameterModelItem result = createTemplateParameter(cursor);
+ result->setType(createTypeInfo(clang_getCursorType(cursor)));
+ return result;
+}
+
+// CXCursor_VarDecl, CXCursor_FieldDecl cursors
+void BuilderPrivate::addField(const CXCursor &cursor)
+{
+ auto field = std::make_shared<_VariableModelItem>(m_model, getCursorSpelling(cursor));
+ field->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor)));
+ field->setScope(m_scope);
+ field->setType(createTypeInfo(cursor));
+ field->setMutable(clang_CXXField_isMutable(cursor) != 0);
+ setFileName(cursor, field.get());
+ m_currentField = field;
+ m_scopeStack.back()->addVariable(field);
+}
+
+// Create qualified name "std::list<std::string>" -> ("std", "list<std::string>")
+static QStringList qualifiedName(const QString &t)
+{
+ QStringList result;
+ int end = t.indexOf(u'<');
+ if (end == -1)
+ end = t.indexOf(u'(');
+ if (end == -1)
+ end = t.size();
+ int lastPos = 0;
+ while (true) {
+ const int nextPos = t.indexOf(u"::"_s, lastPos);
+ if (nextPos < 0 || nextPos >= end)
+ break;
+ result.append(t.mid(lastPos, nextPos - lastPos));
+ lastPos = nextPos + 2;
+ }
+ result.append(t.right(t.size() - lastPos));
+ return result;
+}
+
+static bool isArrayType(CXTypeKind k)
+{
+ return k == CXType_ConstantArray || k == CXType_IncompleteArray
+ || k == CXType_VariableArray || k == CXType_DependentSizedArray;
+}
+
+static bool isPointerType(CXTypeKind k)
+{
+ return k == CXType_Pointer || k == CXType_LValueReference || k == CXType_RValueReference;
+}
+
+bool BuilderPrivate::addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const
+{
+ // Template arguments
+ switch (type.kind) {
+ case CXType_Elaborated:
+ case CXType_Record:
+ case CXType_Unexposed:
+ if (const int numTemplateArguments = qMax(0, clang_Type_getNumTemplateArguments(type))) {
+ for (unsigned tpl = 0; tpl < unsigned(numTemplateArguments); ++tpl) {
+ const CXType argType = clang_Type_getTemplateArgumentAsType(type, tpl);
+ // CXType_Invalid is returned when hitting on a specialization
+ // of a non-type template (template <int v>).
+ if (argType.kind == CXType_Invalid)
+ return false;
+ t->addInstantiation(createTypeInfoUncached(argType));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+static void dummyTemplateArgumentHandler(int, QStringView) {}
+
+void BuilderPrivate::addTemplateInstantiations(const CXType &type,
+ QString *typeName,
+ TypeInfo *t) const
+{
+ // In most cases, for templates like "Vector<A>", Clang will give us the
+ // arguments by recursing down the type. However this will fail for example
+ // within template classes (for functions like the copy constructor):
+ // template <class T>
+ // class Vector {
+ // Vector(const Vector&);
+ // };
+ // In that case, have TypeInfo parse the list from the spelling.
+ // Finally, remove the list "<>" from the type name.
+ const bool parsed = addTemplateInstantiationsRecursion(type, t)
+ && !t->instantiations().isEmpty();
+ if (!parsed)
+ t->setInstantiations({});
+ const auto pos = parsed
+ ? parseTemplateArgumentList(*typeName, dummyTemplateArgumentHandler)
+ : t->parseTemplateArgumentList(*typeName);
+ if (pos.first != -1 && pos.second != -1 && pos.second > pos.first)
+ typeName->remove(pos.first, pos.second - pos.first);
+}
+
+TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type,
+ bool *cacheable) const
+{
+ if (type.kind == CXType_Pointer) { // Check for function pointers, first.
+ const CXType pointeeType = clang_getPointeeType(type);
+ const int argCount = clang_getNumArgTypes(pointeeType);
+ if (argCount >= 0) {
+ TypeInfo result = createTypeInfoUncached(clang_getResultType(pointeeType),
+ cacheable);
+ result.setFunctionPointer(true);
+ for (int a = 0; a < argCount; ++a)
+ result.addArgument(createTypeInfoUncached(clang_getArgType(pointeeType, unsigned(a)),
+ cacheable));
+ return result;
+ }
+ }
+
+ TypeInfo typeInfo;
+
+ CXType nestedType = type;
+ for (; isArrayType(nestedType.kind); nestedType = clang_getArrayElementType(nestedType)) {
+ const long long size = clang_getArraySize(nestedType);
+ typeInfo.addArrayElement(size >= 0 ? QString::number(size) : QString());
+ }
+
+ TypeInfo::Indirections indirections;
+ for (; isPointerType(nestedType.kind); nestedType = clang_getPointeeType(nestedType)) {
+ switch (nestedType.kind) {
+ case CXType_Pointer:
+ indirections.prepend(clang_isConstQualifiedType(nestedType) != 0
+ ? Indirection::ConstPointer : Indirection::Pointer);
+ break;
+ case CXType_LValueReference:
+ typeInfo.setReferenceType(LValueReference);
+ break;
+ case CXType_RValueReference:
+ typeInfo.setReferenceType(RValueReference);
+ break;
+ default:
+ break;
+ }
+ }
+ typeInfo.setIndirectionsV(indirections);
+
+ typeInfo.setConstant(clang_isConstQualifiedType(nestedType) != 0);
+ typeInfo.setVolatile(clang_isVolatileQualifiedType(nestedType) != 0);
+
+ QString typeName = getResolvedTypeName(nestedType);
+ while (TypeInfo::stripLeadingConst(&typeName)
+ || TypeInfo::stripLeadingVolatile(&typeName)) {
+ }
+
+ // For typedefs within templates or nested classes within templates (iterators):
+ // "template <class T> class QList { using Value=T; .."
+ // the typedef source is named "type-parameter-0-0". Convert it back to the
+ // template parameter name. The CXTypes are the same for all templates and
+ // must not be cached.
+ if (m_currentClass && typeName.startsWith(u"type-parameter-0-")) {
+ if (cacheable != nullptr)
+ *cacheable = false;
+ bool ok;
+ const int n = QStringView{typeName}.mid(17).toInt(&ok);
+ if (ok) {
+ auto currentTemplate = currentTemplateClass();
+ if (currentTemplate && n < currentTemplate->templateParameters().size())
+ typeName = currentTemplate->templateParameters().at(n)->name();
+ }
+ }
+
+ // Obtain template instantiations if the name has '<' (thus excluding
+ // typedefs like "std::string".
+ if (typeName.contains(u'<'))
+ addTemplateInstantiations(nestedType, &typeName, &typeInfo);
+
+ typeInfo.setQualifiedName(qualifiedName(typeName));
+ // 3320:CINDEX_LINKAGE int clang_getNumArgTypes(CXType T); function ptr types?
+ typeInfo.simplifyStdType();
+ return typeInfo;
+}
+
+TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const
+{
+ const auto it = m_typeInfoHash.constFind(type);
+ if (it != m_typeInfoHash.constEnd())
+ return it.value();
+ bool cacheable = true;
+ TypeInfo result = createTypeInfoUncached(type, &cacheable);
+ if (cacheable)
+ m_typeInfoHash.insert(type, result);
+ return result;
+}
+
+void BuilderPrivate::addTypeDef(const CXCursor &cursor, const CXType &cxType)
+{
+ const QString target = getCursorSpelling(cursor);
+ auto item = std::make_shared<_TypeDefModelItem>(m_model, target);
+ setFileName(cursor, item.get());
+ item->setType(createTypeInfo(cxType));
+ item->setScope(m_scope);
+ m_scopeStack.back()->addTypeDef(item);
+}
+
+ClassModelItem BuilderPrivate::currentTemplateClass() const
+{
+ for (auto i = m_scopeStack.size() - 1; i >= 0; --i) {
+ auto klass = std::dynamic_pointer_cast<_ClassModelItem>(m_scopeStack.at(i));
+ if (klass && klass->isTemplate())
+ return klass;
+ }
+ return {};
+}
+
+void BuilderPrivate::startTemplateTypeAlias(const CXCursor &cursor)
+{
+ const QString target = getCursorSpelling(cursor);
+ m_currentTemplateTypeAlias.reset(new _TemplateTypeAliasModelItem(m_model, target));
+ setFileName(cursor, m_currentTemplateTypeAlias.get());
+ m_currentTemplateTypeAlias->setScope(m_scope);
+}
+
+void BuilderPrivate::endTemplateTypeAlias(const CXCursor &typeAliasCursor)
+{
+ CXType type = clang_getTypedefDeclUnderlyingType(typeAliasCursor);
+ // Usually "<elaborated>std::list<T>" or "<unexposed>Container1<T>",
+ // as obtained with parser of PYSIDE-323
+ if (type.kind == CXType_Unexposed || type.kind == CXType_Elaborated) {
+ m_currentTemplateTypeAlias->setType(createTypeInfo(type));
+ m_scopeStack.back()->addTemplateTypeAlias(m_currentTemplateTypeAlias);
+ }
+ m_currentTemplateTypeAlias.reset();
+}
+
+// extract an expression from the cursor via source
+// CXCursor_EnumConstantDecl, ParmDecl (a = Flag1 | Flag2)
+QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor)
+{
+ const std::string_view snippet = bv->getCodeSnippet(cursor);
+ auto equalSign = snippet.find('=');
+ if (equalSign == std::string::npos)
+ return QString();
+ ++equalSign;
+ QString result = QString::fromLocal8Bit(snippet.data() + equalSign,
+ qsizetype(snippet.size() - equalSign));
+ // Fix a default expression as read from code. Simplify white space
+ result.remove(u'\r');
+ return result.contains(u'"') ? result.trimmed() : result.simplified();
+}
+
+// Resolve a type (loop over aliases/typedefs), for example for base classes
+// Note: TypeAliasTemplateDecl ("using QVector<T>=QList<T>") is automatically
+// resolved by clang_getTypeDeclaration(), but it stops at
+// TypeAliasDecl / TypedefDecl.
+
+struct TypeDeclaration
+{
+ CXType type;
+ CXCursor declaration;
+};
+
+static inline bool isTypeAliasDecl(const CXCursor &cursor)
+{
+ const auto kind = clang_getCursorKind(cursor);
+ return kind == CXCursor_TypeAliasDecl || kind == CXCursor_TypedefDecl;
+}
+
+static TypeDeclaration resolveBaseClassType(CXType type)
+{
+ CXCursor decl = clang_getTypeDeclaration(type);
+ auto resolvedType = clang_getCursorType(decl);
+ if (resolvedType.kind != CXType_Invalid && resolvedType.kind != type.kind)
+ type = resolvedType;
+ while (isTypeAliasDecl(decl)) {
+ type = clang_getTypedefDeclUnderlyingType(decl);
+ decl = clang_getTypeDeclaration(type);
+ }
+ return {type, decl};
+}
+
+// Note: Return the baseclass for cursors like CXCursor_CXXBaseSpecifier,
+// where the cursor spelling has "struct baseClass".
+std::pair<QString, ClassModelItem> BuilderPrivate::getBaseClass(CXType type) const
+{
+ const auto decl = resolveBaseClassType(type);
+ // Note: spelling has "struct baseClass", use type
+ QString baseClassName = getTypeName(decl.type);
+ if (baseClassName.startsWith(u"std::")) // Simplify "std::" types
+ baseClassName = createTypeInfo(decl.type).toString();
+
+ auto it = m_cursorClassHash.constFind(decl.declaration);
+ // Not found: Set unqualified name. This happens in cases like
+ // "class X : public std::list<...>", "template<class T> class Foo : public T"
+ // and standard types like true_type, false_type.
+ if (it == m_cursorClassHash.constEnd())
+ return {baseClassName, {}};
+
+ // Completely qualify the class name by looking it up and taking its scope
+ // plus the actual baseClass stripped off any scopes. Consider:
+ // namespace std {
+ // template <class T> class vector {};
+ // namespace n {
+ // class Foo : public vector<int> {};
+ // }
+ // }
+ // should have "std::vector<int>" as base class (whereas the type of the base class is
+ // "std::vector<T>").
+ const QStringList &baseScope = it.value()->scope();
+ if (!baseScope.isEmpty()) {
+ const int lastSep = baseClassName.lastIndexOf(u"::"_s);
+ if (lastSep >= 0)
+ baseClassName.remove(0, lastSep + u"::"_s.size());
+ baseClassName.prepend(u"::"_s);
+ baseClassName.prepend(baseScope.join(u"::"_s));
+ }
+ return {baseClassName, it.value()};
+}
+
+// Add a base class to the current class from CXCursor_CXXBaseSpecifier
+void BuilderPrivate::addBaseClass(const CXCursor &cursor)
+{
+ Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier);
+ const auto access = accessPolicy(clang_getCXXAccessSpecifier(cursor));
+ const auto baseClass = getBaseClass(clang_getCursorType(cursor));
+ m_currentClass->addBaseClass({baseClass.first, baseClass.second, access});
+}
+
+void BuilderPrivate::setFileName(const CXCursor &cursor, _CodeModelItem *item)
+{
+ const SourceRange range = getCursorRange(cursor);
+ QString file = m_baseVisitor->getFileName(range.first.file);
+ if (!file.isEmpty()) { // Has been observed to be 0 for invalid locations
+ item->setFileName(QDir::cleanPath(file));
+ item->setStartPosition(int(range.first.line), int(range.first.column));
+ item->setEndPosition(int(range.second.line), int(range.second.column));
+ }
+}
+
+Builder::Builder()
+{
+ d = new BuilderPrivate(this);
+}
+
+Builder::~Builder()
+{
+ delete d;
+}
+
+static QString baseName(QString path)
+{
+ qsizetype lastSlash = path.lastIndexOf(u'/');
+#ifdef Q_OS_WIN
+ if (lastSlash < 0)
+ lastSlash = path.lastIndexOf(u'\\');
+#endif
+ if (lastSlash > 0)
+ path.remove(0, lastSlash + 1);
+ return path;
+}
+
+const char * BuilderPrivate::specialSystemHeaderReason(BuilderPrivate::SpecialSystemHeader sh)
+{
+ static const QHash<SpecialSystemHeader, const char *> mapping {
+ {SpecialSystemHeader::OpenGL, "OpenGL"},
+ {SpecialSystemHeader::Types, "types"},
+ {SpecialSystemHeader::WhiteListed, "white listed"},
+ {SpecialSystemHeader::WhiteListedPath, "white listed path"}
+ };
+ return mapping.value(sh, "");
+}
+
+bool BuilderPrivate::visitHeader(const QString &fileName) const
+{
+ auto it = m_systemHeaders.find(fileName);
+ if (it == m_systemHeaders.end()) {
+ it = m_systemHeaders.insert(fileName, specialSystemHeader(fileName));
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug)) {
+ const QString &name = QDir::toNativeSeparators(fileName);
+ if (it.value() == SpecialSystemHeader::None) {
+ qCInfo(lcShiboken, "Skipping system header %s", qPrintable(name));
+ } else {
+ qCInfo(lcShiboken, "Parsing system header %s (%s)",
+ qPrintable(name), specialSystemHeaderReason(it.value()));
+ }
+ }
+ }
+ return it.value() != SpecialSystemHeader::None;
+}
+
+BuilderPrivate::SpecialSystemHeader
+ BuilderPrivate::specialSystemHeader(const QString &fileName) const
+{
+ // Resolve OpenGL typedefs although the header is considered a system header.
+ const QString baseName = clang::baseName(fileName);
+ if (baseName == u"gl.h"
+ || baseName == u"gl2.h"
+ || baseName == u"gl3.h"
+ || baseName == u"gl31.h"
+ || baseName == u"gl32.h"
+ || baseName == u"stdint.h" // Windows: int32_t, uint32_t
+ || baseName == u"stddef.h") { // size_t`
+ return SpecialSystemHeader::OpenGL;
+ }
+
+ switch (clang::platform()) {
+ case Platform::Unix:
+ if (fileName == u"/usr/include/stdlib.h"
+ || baseName == u"types.h"
+ || baseName == u"stdint-intn.h" // int32_t
+ || baseName == u"stdint-uintn.h") { // uint32_t
+ return SpecialSystemHeader::Types;
+ }
+ break;
+ case Platform::macOS:
+ // Parse the following system headers to get the correct typdefs for types like
+ // int32_t, which are used in the macOS implementation of OpenGL framework.
+ // They are installed under /Applications/Xcode.app/Contents/Developer/Platforms...
+ if (baseName == u"gltypes.h"
+ || fileName.contains(u"/usr/include/_types")
+ || fileName.contains(u"/usr/include/sys/_types")) {
+ return SpecialSystemHeader::Types;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // When building against system Qt (as it happens with yocto / Boot2Qt), the Qt headers are
+ // considered system headers by clang_Location_isInSystemHeader, and shiboken will not
+ // process them. We need to explicitly process them by checking against the list of
+ // include paths that were passed to shiboken's --force-process-system-include-paths option
+ // or specified via the <system-include> xml tag.
+ if (m_forceProcessSystemIncludes.contains(baseName))
+ return SpecialSystemHeader::WhiteListed;
+
+ if (std::any_of(m_forceProcessSystemIncludePaths.cbegin(),
+ m_forceProcessSystemIncludePaths.cend(),
+ [fileName](const QString &p) { return fileName.startsWith(p); })) {
+ return SpecialSystemHeader::WhiteListedPath;
+ }
+
+ return SpecialSystemHeader::None;
+}
+
+bool Builder::visitLocation(const QString &fileName, LocationType locationType) const
+{
+ return locationType != LocationType::System || d->visitHeader(fileName);
+}
+
+void Builder::setForceProcessSystemIncludes(const QStringList &systemIncludes)
+{
+ for (const auto &i : systemIncludes) {
+ QFileInfo fi(i);
+ if (fi.exists() && fi.isDir())
+ d->m_forceProcessSystemIncludePaths.append(i);
+ else
+ d->m_forceProcessSystemIncludes.append(i);
+ }
+}
+
+FileModelItem Builder::dom() const
+{
+ Q_ASSERT(!d->m_scopeStack.isEmpty());
+ auto rootScope = d->m_scopeStack.constFirst();
+ rootScope->purgeClassDeclarations();
+ return std::dynamic_pointer_cast<_FileModelItem>(rootScope);
+}
+
+static QString msgOutOfOrder(const CXCursor &cursor, const char *expectedScope)
+{
+ return getCursorKindName(cursor.kind) + u' '
+ + getCursorSpelling(cursor) + u" encountered outside "_s
+ + QLatin1StringView(expectedScope) + u'.';
+}
+
+static CodeModel::ClassType codeModelClassTypeFromCursor(CXCursorKind kind)
+{
+ CodeModel::ClassType result = CodeModel::Class;
+ if (kind == CXCursor_UnionDecl)
+ result = CodeModel::Union;
+ else if (kind == CXCursor_StructDecl)
+ result = CodeModel::Struct;
+ return result;
+}
+
+static NamespaceType namespaceType(const CXCursor &cursor)
+{
+ if (clang_Cursor_isAnonymous(cursor))
+ return NamespaceType::Anonymous;
+#if CINDEX_VERSION_MAJOR > 0 || CINDEX_VERSION_MINOR >= 59
+ if (clang_Cursor_isInlineNamespace(cursor))
+ return NamespaceType::Inline;
+#endif
+ return NamespaceType::Default;
+}
+
+static QString enumType(const CXCursor &cursor)
+{
+ QString name = getCursorSpelling(cursor); // "enum Foo { v1, v2 };"
+ if (name.contains(u"unnamed enum")) // Clang 16.0
+ return {};
+ if (name.isEmpty()) {
+ // PYSIDE-1228: For "typedef enum { v1, v2 } Foo;", type will return
+ // "Foo" as expected. Care must be taken to exclude real anonymous enums.
+ name = getTypeName(clang_getCursorType(cursor));
+ if (name.contains(u"(unnamed") // Clang 12.0.1
+ || name.contains(u"(anonymous")) { // earlier
+ name.clear();
+ }
+ }
+ return name;
+}
+
+BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
+{
+ switch (cursor.kind) {
+ case CXCursor_CXXAccessSpecifier:
+ d->m_currentFunctionType = CodeModel::Normal;
+ break;
+ case CXCursor_AnnotateAttr: {
+ const QString annotation = getCursorSpelling(cursor);
+ if (annotation == u"qt_slot")
+ d->m_currentFunctionType = CodeModel::Slot;
+ else if (annotation == u"qt_signal")
+ d->m_currentFunctionType = CodeModel::Signal;
+ else
+ d->m_currentFunctionType = CodeModel::Normal;
+ }
+ break;
+ case CXCursor_CXXBaseSpecifier:
+ if (!d->m_currentClass) {
+ const Diagnostic d(msgOutOfOrder(cursor, "class"), cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ d->addBaseClass(cursor);
+ break;
+ case CXCursor_ClassDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_StructDecl:
+ if (d->m_withinFriendDecl || clang_isCursorDefinition(cursor) == 0
+ || !d->addClass(cursor, codeModelClassTypeFromCursor(cursor.kind))) {
+ return Skip;
+ }
+ break;
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ if (d->m_withinFriendDecl || clang_isCursorDefinition(cursor) == 0
+ || !d->addClass(cursor, CodeModel::Class)) {
+ return Skip;
+ }
+ d->m_currentClass->setName(d->m_currentClass->name() + "<>"_L1);
+ d->m_scope.back() += "<>"_L1;
+ break;
+ case CXCursor_EnumDecl: {
+ QString name = enumType(cursor);
+ EnumKind kind = CEnum;
+ if (name.isEmpty()) {
+ kind = AnonymousEnum;
+ name = "enum_"_L1 + QString::number(++d->m_anonymousEnumCount);
+#if !CLANG_NO_ENUMDECL_ISSCOPED
+ } else if (clang_EnumDecl_isScoped(cursor) != 0) {
+#else
+ } else if (clang_EnumDecl_isScoped4(this, cursor) != 0) {
+#endif
+ kind = EnumClass;
+ }
+ d->m_currentEnum.reset(new _EnumModelItem(d->m_model, name));
+ d->setFileName(cursor, d->m_currentEnum.get());
+ d->m_currentEnum->setScope(d->m_scope);
+ d->m_currentEnum->setEnumKind(kind);
+ if (clang_getCursorAvailability(cursor) == CXAvailability_Deprecated)
+ d->m_currentEnum->setDeprecated(true);
+ const auto enumType = fullyResolveType(clang_getEnumDeclIntegerType(cursor));
+ d->m_currentEnum->setSigned(isSigned(enumType.kind));
+ d->m_currentEnum->setUnderlyingType(getTypeName(enumType));
+ if (std::dynamic_pointer_cast<_ClassModelItem>(d->m_scopeStack.back()))
+ d->m_currentEnum->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor)));
+ }
+ break;
+ case CXCursor_EnumConstantDecl: {
+ const QString name = getCursorSpelling(cursor);
+ if (!d->m_currentEnum) {
+ const Diagnostic d(msgOutOfOrder(cursor, "enum"), cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ EnumValue enumValue;
+ if (d->m_currentEnum->isSigned())
+ enumValue.setValue(clang_getEnumConstantDeclValue(cursor));
+ else
+ enumValue.setUnsignedValue(clang_getEnumConstantDeclUnsignedValue(cursor));
+ auto enumConstant = std::make_shared<_EnumeratorModelItem>(d->m_model, name);
+ enumConstant->setStringValue(d->cursorValueExpression(this, cursor));
+ enumConstant->setValue(enumValue);
+ if (clang_getCursorAvailability(cursor) == CXAvailability_Deprecated)
+ enumConstant->setDeprecated(true);
+ d->m_currentEnum->addEnumerator(enumConstant);
+ }
+ break;
+ case CXCursor_VarDecl:
+ // static class members are seen as CXCursor_VarDecl
+ if (isClassOrNamespaceCursor(clang_getCursorSemanticParent(cursor))) {
+ d->addField(cursor);
+ d->m_currentField->setStatic(true);
+ }
+ break;
+ case CXCursor_FieldDecl:
+ d->addField(cursor);
+ break;
+ case CXCursor_FriendDecl:
+ d->m_withinFriendDecl = true;
+ break;
+ case CXCursor_CompoundStmt: // Function bodies
+ return Skip;
+ 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 (d->m_withinFriendDecl || !withinClassDeclaration(cursor))
+ return Skip;
+ d->m_currentFunction = d->createMemberFunction(cursor, false);
+ d->m_scopeStack.back()->addFunction(d->m_currentFunction);
+ break;
+ // Not fully supported, currently, seen as normal function
+ // Note: May appear inside class (member template) or outside (free template).
+ case CXCursor_FunctionTemplate: {
+ const CXCursor semParent = clang_getCursorSemanticParent(cursor);
+ if (isClassCursor(semParent)) {
+ if (semParent == clang_getCursorLexicalParent(cursor)) {
+ d->m_currentFunction = d->createMemberFunction(cursor, true);
+ d->m_scopeStack.back()->addFunction(d->m_currentFunction);
+ break;
+ }
+ return Skip; // inline member functions outside class
+ }
+ }
+ d->m_currentFunction = d->createFunction(cursor, CodeModel::Normal, true);
+ d->setFileName(cursor, d->m_currentFunction.get());
+ d->m_scopeStack.back()->addFunction(d->m_currentFunction);
+ break;
+ case CXCursor_FunctionDecl:
+ // 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) {
+ auto 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_currentFunction->setHiddenFriend(d->m_withinFriendDecl);
+ d->m_scopeStack.at(scope)->addFunction(d->m_currentFunction);
+ }
+ break;
+ case CXCursor_Namespace: {
+ const auto type = namespaceType(cursor);
+ if (type == NamespaceType::Anonymous)
+ return Skip;
+ const QString name = getCursorSpelling(cursor);
+ const auto parentNamespaceItem = std::dynamic_pointer_cast<_NamespaceModelItem>(d->m_scopeStack.back());
+ if (!parentNamespaceItem) {
+ const QString message = msgOutOfOrder(cursor, "namespace")
+ + u" (current scope: "_s + d->m_scopeStack.back()->name() + u')';
+ const Diagnostic d(message, cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ // Treat namespaces separately to allow for extending namespaces
+ // in subsequent modules.
+ NamespaceModelItem namespaceItem = parentNamespaceItem->findNamespace(name);
+ namespaceItem.reset(new _NamespaceModelItem(d->m_model, name));
+ d->setFileName(cursor, namespaceItem.get());
+ namespaceItem->setScope(d->m_scope);
+ namespaceItem->setType(type);
+ parentNamespaceItem->addNamespace(namespaceItem);
+ d->pushScope(namespaceItem);
+ }
+ break;
+ case CXCursor_ParmDecl:
+ // Skip in case of nested CXCursor_ParmDecls in case one parameter is a function pointer
+ // and function pointer typedefs.
+ if (!d->m_currentArgument && d->m_currentFunction) {
+ const QString name = getCursorSpelling(cursor);
+ d->m_currentArgument.reset(new _ArgumentModelItem(d->m_model, name));
+ const auto type = clang_getCursorType(cursor);
+ d->m_currentArgument->setScopeResolution(hasScopeResolution(type));
+ d->m_currentArgument->setType(d->createTypeInfo(type));
+ d->m_currentFunction->addArgument(d->m_currentArgument);
+ QString defaultValueExpression = d->cursorValueExpression(this, cursor);
+ if (!defaultValueExpression.isEmpty()) {
+ d->m_currentArgument->setDefaultValueExpression(defaultValueExpression);
+ d->m_currentArgument->setDefaultValue(true);
+ }
+ } else {
+ return Skip;
+ }
+ break;
+ case CXCursor_TemplateTypeParameter:
+ case CXCursor_NonTypeTemplateParameter: {
+ const TemplateParameterModelItem tItem = cursor.kind == CXCursor_TemplateTemplateParameter
+ ? d->createTemplateParameter(cursor) : d->createNonTypeTemplateParameter(cursor);
+ // Apply to function/member template?
+ if (d->m_currentFunction) {
+ d->m_currentFunction->setTemplateParameters(d->m_currentFunction->templateParameters() << tItem);
+ } else if (d->m_currentTemplateTypeAlias) {
+ d->m_currentTemplateTypeAlias->addTemplateParameter(tItem);
+ } else if (d->m_currentClass) { // Apply to class
+ const QString &tplParmName = tItem->name();
+ if (Q_UNLIKELY(!insertTemplateParameterIntoClassName(tplParmName, d->m_currentClass)
+ || !insertTemplateParameterIntoClassName(tplParmName, &d->m_scope.back()))) {
+ const QString message = "Error inserting template parameter \""_L1 + tplParmName
+ + "\" into "_L1 + d->m_currentClass->name();
+ const Diagnostic d(message, cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ d->m_currentClass->setTemplateParameters(d->m_currentClass->templateParameters() << tItem);
+ }
+ }
+ break;
+ case CXCursor_TypeAliasTemplateDecl:
+ d->startTemplateTypeAlias(cursor);
+ break;
+ case CXCursor_TypeAliasDecl: // May contain nested CXCursor_TemplateTypeParameter
+ if (!d->m_currentTemplateTypeAlias) {
+ const CXType type = clang_getCanonicalType(clang_getCursorType(cursor));
+ if (type.kind > CXType_Unexposed)
+ d->addTypeDef(cursor, type);
+ return Skip;
+ } else {
+ d->endTemplateTypeAlias(cursor);
+ }
+ break;
+ case CXCursor_TypedefDecl: {
+ auto underlyingType = clang_getTypedefDeclUnderlyingType(cursor);
+ d->addTypeDef(cursor, underlyingType);
+ // For "typedef enum/struct {} Foo;", skip the enum/struct
+ // definition nested into the typedef (PYSIDE-1228).
+ if (underlyingType.kind == CXType_Elaborated)
+ return Skip;
+ }
+ break;
+ // Using declarations look as follows:
+ // 1) Normal, non-template case ("using QObject::parent"): UsingDeclaration, TypeRef
+ // 2) Simple template case ("using QList::append()"): UsingDeclaration, TypeRef "QList<T>"
+ // 3) Template case with parameters ("using QList<T>::append()"):
+ // UsingDeclaration, TemplateRef "QList", TypeRef "T"
+ case CXCursor_TemplateRef:
+ if (d->m_withinUsingDeclaration && d->m_usingTypeRef.isEmpty())
+ d->m_usingTypeRef = getCursorSpelling(cursor);
+ break;
+ case CXCursor_TypeRef:
+ if (d->m_withinUsingDeclaration && d->m_usingTypeRef.isEmpty())
+ d->m_usingTypeRef = d->getBaseClass(clang_getCursorType(cursor)).first;
+ break;
+ case CXCursor_CXXFinalAttr:
+ if (d->m_currentFunction)
+ d->m_currentFunction->setAttribute(FunctionAttribute::Final);
+ else if (d->m_currentClass)
+ d->m_currentClass->setFinal(true);
+ break;
+ case CXCursor_CXXOverrideAttr:
+ if (d->m_currentFunction)
+ d->m_currentFunction->setAttribute(FunctionAttribute::Override);
+ break;
+ case CXCursor_StaticAssert:
+ // Check for Q_PROPERTY() (see PySide6/global.h.in for an explanation
+ // how it is defined, and qdoc).
+ if (clang_isDeclaration(cursor.kind) && d->m_currentClass) {
+ auto snippet = getCodeSnippet(cursor);
+ const auto length = snippet.size();
+ if (length > 12 && *snippet.rbegin() == ')'
+ && snippet.compare(0, 11, "Q_PROPERTY(") == 0) {
+ const QString qProperty = QString::fromUtf8(snippet.data() + 11, length - 12);
+ d->m_currentClass->addPropertyDeclaration(qProperty);
+ }
+ }
+ break;
+ // UsingDeclaration: consists of a TypeRef (base) and OverloadedDeclRef (member name)
+ case CXCursor_UsingDeclaration:
+ if (d->m_currentClass)
+ d->m_withinUsingDeclaration = true;
+ break;
+ case CXCursor_OverloadedDeclRef:
+ if (d->m_withinUsingDeclaration && !d->m_usingTypeRef.isEmpty()) {
+ QString member = getCursorSpelling(cursor);
+ if (member == d->m_currentClass->name())
+ member = d->m_usingTypeRef; // Overloaded member is Constructor, use base
+ const auto ap = accessPolicy(clang_getCXXAccessSpecifier(cursor));
+ d->m_currentClass->addUsingMember(d->m_usingTypeRef, member, ap);
+ }
+ break;
+ default:
+ break;
+ }
+ return BaseVisitor::Recurse;
+}
+
+bool Builder::endToken(const CXCursor &cursor)
+{
+ switch (cursor.kind) {
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassDecl:
+ case CXCursor_StructDecl:
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ d->popScope();
+ // Continue in outer class after leaving inner class?
+ if (auto lastClass = std::dynamic_pointer_cast<_ClassModelItem>(d->m_scopeStack.back()))
+ d->m_currentClass = lastClass;
+ else
+ d->m_currentClass.reset();
+ d->m_currentFunctionType = CodeModel::Normal;
+ break;
+ case CXCursor_EnumDecl:
+ if (d->m_currentEnum)
+ d->m_scopeStack.back()->addEnum(d->m_currentEnum);
+ d->m_currentEnum.reset();
+ break;
+ case CXCursor_FriendDecl:
+ d->m_withinFriendDecl = false;
+ break;
+ case CXCursor_VarDecl:
+ case CXCursor_FieldDecl:
+ d->m_currentField.reset();
+ break;
+ case CXCursor_Constructor:
+ d->qualifyConstructor(cursor);
+ if (d->m_currentFunction) {
+ d->m_currentFunction->_determineType();
+ d->m_currentFunction.reset();
+ }
+ break;
+ case CXCursor_Destructor:
+ case CXCursor_CXXMethod:
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ if (d->m_currentFunction) {
+ d->m_currentFunction->_determineType();
+ d->m_currentFunction.reset();
+ }
+ break;
+ case CXCursor_ConversionFunction:
+ if (d->m_currentFunction) {
+ d->m_currentFunction->setFunctionType(CodeModel::ConversionOperator);
+ d->m_currentFunction.reset();
+ }
+ break;
+ case CXCursor_Namespace:
+ d->popScope();
+ break;
+ case CXCursor_ParmDecl:
+ d->m_currentArgument.reset();
+ break;
+ case CXCursor_TypeAliasTemplateDecl:
+ d->m_currentTemplateTypeAlias.reset();
+ break;
+ case CXCursor_UsingDeclaration:
+ d->m_withinUsingDeclaration = false;
+ d->m_usingTypeRef.clear();
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+} // namespace clang
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.h b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.h
new file mode 100644
index 000000000..b2ec6d304
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CLANGBUILDER_H
+#define CLANGBUILDER_H
+
+#include "clangparser.h"
+
+#include <codemodel_fwd.h>
+
+namespace clang {
+
+class BuilderPrivate;
+
+class Builder : public BaseVisitor {
+public:
+ Q_DISABLE_COPY_MOVE(Builder)
+
+ Builder();
+ ~Builder();
+
+ void setForceProcessSystemIncludes(const QStringList &systemIncludes);
+
+ bool visitLocation(const QString &fileName, LocationType locationType) const override;
+
+ StartTokenResult startToken(const CXCursor &cursor) override;
+ bool endToken(const CXCursor &cursor) override;
+
+ FileModelItem dom() const;
+
+private:
+ BuilderPrivate *d;
+};
+
+} // namespace clang
+
+#endif // CLANGBUILDER_H
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.cpp
new file mode 100644
index 000000000..3c002da9c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.cpp
@@ -0,0 +1,175 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "clangdebugutils.h"
+#include "clangutils.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+#ifndef QT_NO_DEBUG_STREAM
+
+#ifdef Q_OS_WIN
+const char pathSep = '\\';
+#else
+const char pathSep = '/';
+#endif
+
+static const char *baseName(const char *fileName)
+{
+ const char *b = std::strrchr(fileName, pathSep);
+ return b ? b + 1 : fileName;
+}
+
+QDebug operator<<(QDebug s, const CXString &cs)
+{
+ s << clang_getCString(cs);
+ return s;
+}
+
+QDebug operator<<(QDebug s, CXCursorKind cursorKind) // Enum
+{
+ const CXString kindName = clang_getCursorKindSpelling(cursorKind);
+ s << kindName;
+ clang_disposeString(kindName);
+ return s;
+}
+
+static const char *accessSpecsStrings[]
+{
+ // CX_CXXInvalidAccessSpecifier, CX_CXXPublic, CX_CXXProtected, CX_CXXPrivate
+ "invalid", "public", "protected", "private"
+};
+
+QDebug operator<<(QDebug s, CX_CXXAccessSpecifier ac)
+{
+ s << accessSpecsStrings[ac];
+ return s;
+}
+
+struct formatCXTypeName
+{
+ explicit formatCXTypeName(const CXType &type) : m_type(type) {}
+
+ const CXType &m_type;
+};
+
+QDebug operator<<(QDebug debug, const formatCXTypeName &ft)
+{
+ CXString typeSpelling = clang_getTypeSpelling(ft.m_type);
+ debug << typeSpelling;
+ clang_disposeString(typeSpelling);
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const CXType &type)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug.noquote();
+ debug << "CXType(";
+ if (type.kind == CXType_Invalid) {
+ debug << "invalid)";
+ return debug;
+ }
+
+ debug << type.kind;
+ switch (type.kind) {
+ case CXType_Unexposed:
+ debug << " [unexposed]";
+ break;
+ case CXType_Elaborated:
+ debug << " [elaborated]";
+ break;
+ default:
+ break;
+ }
+ debug << ", " << formatCXTypeName(type) << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const CXCursor &cursor)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug.noquote();
+ const CXCursorKind kind = clang_getCursorKind(cursor);
+ debug << "CXCursor(";
+ if (kind >= CXCursor_FirstInvalid && kind <= CXCursor_LastInvalid) {
+ debug << "invalid)";
+ return debug;
+ }
+
+ const QString cursorSpelling = clang::getCursorSpelling(cursor);
+ debug << '"' << cursorSpelling << '"';
+ CXString cursorDisplay = clang_getCursorDisplayName(cursor);
+ if (const char *dpy = clang_getCString(cursorDisplay)) {
+ const QString display = QString::fromUtf8(dpy);
+ if (display != cursorSpelling)
+ debug << ", display=\"" << dpy << '"';
+ }
+ clang_disposeString(cursorDisplay);
+
+ debug << ", kind=" << kind;
+
+ const CXType type = clang_getCursorType(cursor);
+ switch (kind) {
+ case CXCursor_CXXAccessSpecifier:
+ debug << ", " << clang_getCXXAccessSpecifier(cursor);
+ break;
+ case CXCursor_CXXBaseSpecifier:
+ debug << ", inherits=\"" << clang::getCursorSpelling(clang_getTypeDeclaration(type)) << '"';
+ break;
+ case CXCursor_CXXMethod:
+ case CXCursor_FunctionDecl:
+ case CXCursor_ConversionFunction:
+ debug << ", result type=\""
+ << formatCXTypeName(clang_getCursorResultType(cursor)) << '"';
+ break;
+ case CXCursor_TypedefDecl:
+ debug << ", underlyingType=\""
+ << formatCXTypeName(clang_getTypedefDeclUnderlyingType(cursor)) << '"';
+ break;
+ default:
+ break;
+ }
+
+ debug << ", type=\"" << formatCXTypeName(type) << '"';
+ if (clang_Cursor_hasAttrs(cursor))
+ debug << ", [attrs]";
+
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug s, const CXSourceLocation &location)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ CXFile file; // void *
+ unsigned line;
+ unsigned column;
+ unsigned offset;
+ clang_getExpansionLocation(location, &file, &line, &column, &offset);
+ const CXString cxFileName = clang_getFileName(file);
+ // Has been observed to be 0 for invalid locations
+ if (const char *cFileName = clang_getCString(cxFileName))
+ s << baseName(cFileName) << ':';
+ s << line << ':' << column;
+ clang_disposeString(cxFileName);
+ return s;
+}
+
+QDebug operator<<(QDebug s, const std::string_view &v)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ s.noquote();
+ s << '"';
+ for (auto c : v)
+ s << c;
+ s << '"';
+ return s;
+}
+
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.h b/sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.h
new file mode 100644
index 000000000..7aac8a575
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CLANGDEBUGUTILS_H
+#define CLANGDEBUGUTILS_H
+
+#include <QtCore/qtclasshelpermacros.h>
+
+#include <clang-c/Index.h>
+
+#include <string_view>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const CXString &cs);
+QDebug operator<<(QDebug s, CXCursorKind cursorKind);
+QDebug operator<<(QDebug s, CX_CXXAccessSpecifier ac);
+QDebug operator<<(QDebug s, const CXType &t);
+QDebug operator<<(QDebug s, const CXCursor &cursor);
+QDebug operator<<(QDebug s, const CXSourceLocation &location);
+QDebug operator<<(QDebug s, const std::string_view &v); // for code snippets
+#endif // !QT_NO_DEBUG_STREAM
+
+#endif // CLANGDEBUGUTILS_H
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
new file mode 100644
index 000000000..6c0cf3ae2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
@@ -0,0 +1,323 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "clangparser.h"
+#include "clangutils.h"
+#include "clangdebugutils.h"
+#include "compilersupport.h"
+
+#include <QtCore/QByteArrayList>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QScopedArrayPointer>
+#include <QtCore/QString>
+
+using namespace Qt::StringLiterals;
+
+namespace clang {
+
+QString SourceFileCache::getFileName(CXFile file)
+{
+ auto it = m_fileNameCache.find(file);
+ if (it == m_fileNameCache.end())
+ it = m_fileNameCache.insert(file, clang::getFileName(file));
+ return it.value();
+}
+
+std::string_view SourceFileCache::getCodeSnippet(const CXCursor &cursor,
+ QString *errorMessage)
+{
+ static const char empty[] = "";
+
+ if (errorMessage)
+ errorMessage->clear();
+
+ const SourceRange range = getCursorRange(cursor);
+ // Quick check for equal locations: Frequently happens if the code is
+ // the result of a macro expansion
+ if (range.first == range.second)
+ return std::string_view(empty, 0);
+
+ if (range.first.file != range.second.file) {
+ if (errorMessage)
+ *errorMessage = "Range spans several files"_L1;
+ return std::string_view(empty, 0);
+ }
+
+ auto it = m_fileBufferCache.find(range.first.file);
+ if (it == m_fileBufferCache.end()) {
+ const QString fileName = getFileName(range.first.file);
+ if (fileName.isEmpty()) {
+ if (errorMessage)
+ *errorMessage = "Range has no file"_L1;
+ return std::string_view(empty, 0);
+ }
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ if (errorMessage) {
+ QTextStream str(errorMessage);
+ str << "Cannot open \"" << QDir::toNativeSeparators(fileName)
+ << "\": " << file.errorString();
+ }
+ return std::string_view(empty, 0);
+ }
+ it = m_fileBufferCache.insert(range.first.file, file.readAll());
+ }
+
+ const unsigned pos = range.first.offset;
+ const unsigned end = range.second.offset;
+ Q_ASSERT(end > pos);
+ const QByteArray &contents = it.value();
+ if (end >= unsigned(contents.size())) {
+ if (errorMessage) {
+ QTextStream str(errorMessage);
+ str << "Range end " << end << " is above size of \""
+ << QDir::toNativeSeparators(getFileName(range.first.file))
+ << "\" (" << contents.size() << ')';
+ }
+ return std::string_view(empty, 0);
+ }
+
+ return std::string_view(contents.constData() + pos, end - pos);
+}
+
+BaseVisitor::BaseVisitor() = default;
+BaseVisitor::~BaseVisitor() = default;
+
+bool BaseVisitor::visitLocation(const QString &, LocationType locationType) const
+{
+ return locationType != LocationType::System;
+}
+
+BaseVisitor::StartTokenResult BaseVisitor::cbHandleStartToken(const CXCursor &cursor)
+{
+ switch (cursor.kind) {
+ default:
+ break;
+ }
+
+ return startToken(cursor);
+}
+
+bool BaseVisitor::cbHandleEndToken(const CXCursor &cursor, StartTokenResult startResult)
+{
+ const bool result = startResult != Recurse || endToken(cursor);
+ switch (cursor.kind) {
+ default:
+ break;
+ }
+
+ return result;
+}
+
+std::string_view BaseVisitor::getCodeSnippet(const CXCursor &cursor)
+{
+ QString errorMessage;
+ const std::string_view result = m_fileCache.getCodeSnippet(cursor, &errorMessage);
+ if (result.empty() && !errorMessage.isEmpty()) {
+ QString message;
+ QTextStream str(&message);
+ str << "Unable to retrieve code snippet \"" << getCursorSpelling(cursor)
+ << "\": " << errorMessage;
+ appendDiagnostic(Diagnostic(message, cursor, CXDiagnostic_Error));
+ }
+ return result;
+}
+
+bool BaseVisitor::_handleVisitLocation(const CXSourceLocation &location)
+{
+ CXFile cxFile; // void *
+ unsigned line;
+ unsigned column;
+ unsigned offset;
+ clang_getExpansionLocation(location, &cxFile, &line, &column, &offset);
+
+ if (cxFile == m_currentCxFile) // Same file?
+ return m_visitCurrent;
+
+ const QString fileName = getFileName(cxFile);
+
+ LocationType locationType = LocationType::Unknown;
+ if (!fileName.isEmpty()) {
+ if (clang_Location_isFromMainFile(location) != 0)
+ locationType = LocationType::Main;
+ else if (clang_Location_isInSystemHeader(location) != 0)
+ locationType = LocationType::System;
+ else
+ locationType = LocationType::Other;
+ }
+
+ m_currentCxFile = cxFile;
+ m_visitCurrent = visitLocation(fileName, locationType);
+ return m_visitCurrent;
+}
+
+QString BaseVisitor::getCodeSnippetString(const CXCursor &cursor)
+{
+ const std::string_view result = getCodeSnippet(cursor);
+ return result.empty()
+ ? QString()
+ : QString::fromUtf8(result.data(), qsizetype(result.size()));
+}
+
+static CXChildVisitResult
+ visitorCallback(CXCursor cursor, CXCursor /* parent */, CXClientData clientData)
+{
+ auto *bv = reinterpret_cast<BaseVisitor *>(clientData);
+
+ const CXSourceLocation location = clang_getCursorLocation(cursor);
+ if (!bv->_handleVisitLocation(location))
+ return CXChildVisit_Continue;
+
+ const BaseVisitor::StartTokenResult startResult = bv->cbHandleStartToken(cursor);
+ switch (startResult) {
+ case clang::BaseVisitor::Error:
+ return CXChildVisit_Break;
+ case clang::BaseVisitor::Skip:
+ break;
+ case clang::BaseVisitor::Recurse:
+ clang_visitChildren(cursor, visitorCallback, clientData);
+ break;
+ }
+
+ if (!bv->cbHandleEndToken(cursor, startResult))
+ return CXChildVisit_Break;
+
+ return CXChildVisit_Continue;
+}
+
+BaseVisitor::Diagnostics BaseVisitor::diagnostics() const
+{
+ return m_diagnostics;
+}
+
+void BaseVisitor::setDiagnostics(const Diagnostics &d)
+{
+ m_diagnostics = d;
+}
+
+void BaseVisitor::appendDiagnostic(const Diagnostic &d)
+{
+ m_diagnostics.append(d);
+}
+
+static inline const char **byteArrayListToFlatArgV(const QByteArrayList &bl)
+{
+ const char **result = new const char *[bl.size() + 1];
+ result[bl.size()] = nullptr;
+ std::transform(bl.cbegin(), bl.cend(), result,
+ [] (const QByteArray &a) { return a.constData(); });
+ return result;
+}
+
+static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsigned flags)
+{
+ QByteArray result = "clang_parseTranslationUnit2(0x";
+ result += QByteArray::number(flags, 16);
+ const auto count = clangArgs.size();
+ result += ", cmd[" + QByteArray::number(count) + "]=";
+ for (qsizetype i = 0; i < count; ++i) {
+ const QByteArray &arg = clangArgs.at(i);
+ if (i)
+ result += ' ';
+ const bool quote = arg.contains(' ') || arg.contains('(');
+ if (quote)
+ result += '"';
+ result += arg;
+ if (quote)
+ result += '"';
+ }
+ result += ')';
+ return result;
+}
+
+static CXTranslationUnit createTranslationUnit(CXIndex index,
+ const QByteArrayList &args,
+ bool addCompilerSupportArguments,
+ LanguageLevel level,
+ unsigned flags = 0)
+{
+ // courtesy qdoc
+ const unsigned defaultFlags = CXTranslationUnit_Incomplete;
+
+ static const QByteArrayList defaultArgs = {
+#ifndef Q_OS_WIN
+ "-fPIC",
+#endif
+#ifdef Q_OS_MACOS
+ "-Wno-expansion-to-defined", // Workaround for warnings in Darwin stdlib, see
+ // https://github.com/darlinghq/darling/issues/204
+#endif
+ "-Wno-constant-logical-operand",
+ "-x",
+ "c++" // Treat .h as C++, not C
+ };
+
+ QByteArrayList clangArgs;
+ if (addCompilerSupportArguments) {
+ clangArgs += emulatedCompilerOptions(level);
+ clangArgs += defaultArgs;
+ }
+ clangArgs += detectVulkan();
+ clangArgs += args;
+ QScopedArrayPointer<const char *> argv(byteArrayListToFlatArgV(clangArgs));
+ qDebug().noquote().nospace() << msgCreateTranslationUnit(clangArgs, flags);
+
+ CXTranslationUnit tu;
+ CXErrorCode err = clang_parseTranslationUnit2(index, nullptr, argv.data(),
+ clangArgs.size(), nullptr, 0,
+ defaultFlags | flags, &tu);
+ if (err || !tu) {
+ qWarning().noquote().nospace() << "Could not parse "
+ << clangArgs.constLast().constData() << ", error code: " << err;
+ return nullptr;
+ }
+ return tu;
+}
+
+/* clangFlags are flags to clang_parseTranslationUnit2() such as
+ * CXTranslationUnit_KeepGoing (from CINDEX_VERSION_MAJOR/CINDEX_VERSION_MINOR 0.35)
+ */
+
+bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments,
+ LanguageLevel level, unsigned clangFlags, BaseVisitor &bv)
+{
+ CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */,
+ 1 /* displayDiagnostics */);
+ if (!index) {
+ qWarning() << "clang_createIndex() failed!";
+ return false;
+ }
+
+ CXTranslationUnit translationUnit =
+ createTranslationUnit(index, clangArgs, addCompilerSupportArguments,
+ level, clangFlags);
+ if (!translationUnit)
+ return false;
+
+ CXCursor rootCursor = clang_getTranslationUnitCursor(translationUnit);
+
+ clang_visitChildren(rootCursor, visitorCallback, reinterpret_cast<CXClientData>(&bv));
+
+ QList<Diagnostic> diagnostics = getDiagnostics(translationUnit);
+ diagnostics.append(bv.diagnostics());
+ bv.setDiagnostics(diagnostics);
+
+ const bool ok = maxSeverity(diagnostics) < CXDiagnostic_Error;
+ if (!ok) {
+ QDebug debug = qWarning();
+ debug.noquote();
+ debug.nospace();
+ debug << "Errors in "
+ << QDir::toNativeSeparators(QFile::decodeName(clangArgs.constLast())) << ":\n";
+ for (const Diagnostic &diagnostic : std::as_const(diagnostics))
+ debug << diagnostic << '\n';
+ }
+
+ clang_disposeTranslationUnit(translationUnit);
+ clang_disposeIndex(index);
+ return ok;
+}
+
+} // namespace clang
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h
new file mode 100644
index 000000000..22e0a50cd
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CLANGPARSER_H
+#define CLANGPARSER_H
+
+#include <clang-c/Index.h>
+
+#include <QtCore/QByteArrayList>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+#include <string_view>
+#include <utility>
+
+enum class LanguageLevel;
+
+namespace clang {
+
+struct Diagnostic;
+
+class SourceFileCache {
+public:
+ std::string_view getCodeSnippet(const CXCursor &cursor, QString *errorMessage = nullptr);
+ QString getFileName(CXFile file);
+
+private:
+ using FileBufferCache = QHash<CXFile, QByteArray>;
+ using FileNameCache = QHash<CXFile, QString>;
+
+ FileBufferCache m_fileBufferCache;
+ FileNameCache m_fileNameCache;
+};
+
+enum class LocationType
+{
+ Main, // Main header parsed for bindings
+ Other, // A header parsed for bindings
+ System, // A system header
+ Unknown // Clang internal
+};
+
+class BaseVisitor {
+ Q_DISABLE_COPY_MOVE(BaseVisitor)
+public:
+ using Diagnostics = QList<Diagnostic>;
+
+ enum StartTokenResult { Error, Skip, Recurse };
+
+ BaseVisitor();
+ virtual ~BaseVisitor();
+
+ // Whether location should be visited.
+ // defaults to clang_Location_isFromMainFile()
+ virtual bool visitLocation(const QString &fileName, LocationType locationType) const;
+
+ virtual StartTokenResult startToken(const CXCursor &cursor) = 0;
+ virtual bool endToken(const CXCursor &cursor) = 0;
+
+ StartTokenResult cbHandleStartToken(const CXCursor &cursor);
+ bool cbHandleEndToken(const CXCursor &cursor, StartTokenResult startResult);
+
+ QString getFileName(CXFile file) { return m_fileCache.getFileName(file); }
+ std::string_view getCodeSnippet(const CXCursor &cursor);
+ QString getCodeSnippetString(const CXCursor &cursor);
+
+ Diagnostics diagnostics() const;
+ void setDiagnostics(const Diagnostics &d);
+ void appendDiagnostic(const Diagnostic &d);
+
+ // For usage by the parser
+ bool _handleVisitLocation( const CXSourceLocation &location);
+
+private:
+ SourceFileCache m_fileCache;
+ Diagnostics m_diagnostics;
+ CXFile m_currentCxFile{};
+ bool m_visitCurrent = true;
+};
+
+bool parse(const QByteArrayList &clangArgs,
+ bool addCompilerSupportArguments,
+ LanguageLevel level, unsigned clangFlags, BaseVisitor &ctx);
+
+} // namespace clang
+
+#endif // !CLANGPARSER_H
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangutils.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangutils.cpp
new file mode 100644
index 000000000..1651e09ec
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangutils.cpp
@@ -0,0 +1,320 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "clangutils.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QHashFunctions>
+#include <QtCore/QProcess>
+
+#include <string_view>
+
+bool operator==(const CXCursor &c1, const CXCursor &c2) noexcept
+{
+ return c1.kind == c2.kind
+ && c1.xdata == c2.xdata
+ && std::equal(c1.data, c1.data + sizeof(c1.data) / sizeof(c1.data[0]), c2.data);
+}
+
+size_t qHash(const CXCursor &c, size_t seed) noexcept
+{
+ return qHashMulti(seed, c.kind, c.xdata, c.data[0], c.data[1], c.data[2]);
+}
+
+bool operator==(const CXType &t1, const CXType &t2) noexcept
+{
+ return t1.kind == t2.kind && t1.data[0] == t2.data[0]
+ && t1.data[1] == t2.data[1];
+}
+
+size_t qHash(const CXType &ct, size_t seed) noexcept
+{
+ return qHashMulti(seed, ct.kind, ct.data[0], ct.data[1]);
+}
+
+namespace clang {
+
+SourceLocation getExpansionLocation(const CXSourceLocation &location)
+{
+ SourceLocation result;
+ clang_getExpansionLocation(location, &result.file, &result.line, &result.column, &result.offset);
+ return result;
+}
+
+QString getFileName(CXFile file)
+{
+ QString result;
+ const CXString cxFileName = clang_getFileName(file);
+ // Has been observed to be 0 for invalid locations
+ if (const char *cFileName = clang_getCString(cxFileName))
+ result = QString::fromUtf8(cFileName);
+ clang_disposeString(cxFileName);
+ return result;
+}
+
+SourceLocation getCursorLocation(const CXCursor &cursor)
+{
+ const CXSourceRange extent = clang_getCursorExtent(cursor);
+ return getExpansionLocation(clang_getRangeStart(extent));
+}
+
+CXString getFileNameFromLocation(const CXSourceLocation &location)
+{
+ CXFile file;
+ unsigned line;
+ unsigned column;
+ unsigned offset;
+ clang_getExpansionLocation(location, &file, &line, &column, &offset);
+ return clang_getFileName(file);
+}
+
+SourceRange getCursorRange(const CXCursor &cursor)
+{
+ const CXSourceRange extent = clang_getCursorExtent(cursor);
+ return std::make_pair(getExpansionLocation(clang_getRangeStart(extent)),
+ getExpansionLocation(clang_getRangeEnd(extent)));
+}
+
+QString getCursorKindName(CXCursorKind cursorKind)
+{
+ CXString kindName = clang_getCursorKindSpelling(cursorKind);
+ const QString result = QString::fromUtf8(clang_getCString(kindName));
+ clang_disposeString(kindName);
+ return result;
+}
+
+QString getCursorSpelling(const CXCursor &cursor)
+{
+ CXString cursorSpelling = clang_getCursorSpelling(cursor);
+ const QString result = QString::fromUtf8(clang_getCString(cursorSpelling));
+ clang_disposeString(cursorSpelling);
+ return result;
+}
+
+QString getCursorDisplayName(const CXCursor &cursor)
+{
+ CXString displayName = clang_getCursorDisplayName(cursor);
+ const QString result = QString::fromUtf8(clang_getCString(displayName));
+ clang_disposeString(displayName);
+ return result;
+}
+
+static inline bool isBuiltinType(CXTypeKind kind)
+{
+ return kind >= CXType_FirstBuiltin && kind <= CXType_LastBuiltin;
+}
+
+// Resolve elaborated types occurring with clang 16
+static CXType resolveElaboratedType(const CXType &type)
+{
+ if (!isBuiltinType(type.kind)) {
+ CXCursor decl = clang_getTypeDeclaration(type);
+ auto resolvedType = clang_getCursorType(decl);
+ if (resolvedType.kind != CXType_Invalid && resolvedType.kind != type.kind)
+ return resolvedType;
+ }
+ return type;
+}
+
+// Resolve typedefs
+static CXType resolveTypedef(const CXType &type)
+{
+ auto result = type;
+ while (result.kind == CXType_Typedef) {
+ auto decl = clang_getTypeDeclaration(result);
+ auto resolved = clang_getTypedefDeclUnderlyingType(decl);
+ if (resolved.kind == CXType_Invalid)
+ break;
+ result = resolved;
+ }
+ return result;
+}
+
+// Fully resolve a type from elaborated & typedefs
+CXType fullyResolveType(const CXType &type)
+{
+ return resolveTypedef(resolveElaboratedType(type));
+}
+
+QString getTypeName(const CXType &type)
+{
+ CXString typeSpelling = clang_getTypeSpelling(type);
+ const QString result = QString::fromUtf8(clang_getCString(typeSpelling));
+ clang_disposeString(typeSpelling);
+ return result;
+}
+
+// Quick check for "::Type"
+bool hasScopeResolution(const CXType &type)
+{
+ CXString typeSpelling = clang_getTypeSpelling(type);
+ std::string_view spelling = clang_getCString(typeSpelling);
+ const bool result = spelling.compare(0, 2, "::") == 0
+ || spelling.find(" ::") != std::string::npos;
+ clang_disposeString(typeSpelling);
+ return result;
+}
+
+// Resolve elaborated types occurring with clang 16
+QString getResolvedTypeName(const CXType &type)
+{
+ return getTypeName(resolveElaboratedType(type));
+}
+
+Diagnostic::Diagnostic(const QString &m, const CXCursor &c, CXDiagnosticSeverity s)
+ : message(m), source(Other), severity(s)
+{
+ setLocation(getCursorLocation(c));
+}
+
+Diagnostic Diagnostic::fromCXDiagnostic(CXDiagnostic cd)
+{
+ Diagnostic result;
+ result.source = Clang;
+ CXString spelling = clang_getDiagnosticSpelling(cd);
+ result.message = QString::fromUtf8(clang_getCString(spelling));
+ clang_disposeString(spelling);
+ result.severity = clang_getDiagnosticSeverity(cd);
+ result.setLocation(getExpansionLocation(clang_getDiagnosticLocation(cd)));
+
+ CXDiagnosticSet childDiagnostics = clang_getChildDiagnostics(cd);
+ if (const unsigned childCount = clang_getNumDiagnosticsInSet(childDiagnostics)) {
+ result.childMessages.reserve(int(childCount));
+ const unsigned format = clang_defaultDiagnosticDisplayOptions();
+ for (unsigned i = 0; i < childCount; ++i) {
+ CXDiagnostic childDiagnostic = clang_getDiagnosticInSet(childDiagnostics, i);
+ CXString cdm = clang_formatDiagnostic(childDiagnostic, format);
+ result.childMessages.append(QString::fromUtf8(clang_getCString(cdm)));
+ clang_disposeString(cdm);
+ clang_disposeDiagnostic(childDiagnostic);
+ }
+ }
+
+ return result;
+}
+
+void Diagnostic::setLocation(const SourceLocation &sourceLocation)
+{
+ file = getFileName(sourceLocation.file);
+ line = sourceLocation.line;
+ column = sourceLocation.column;
+ offset = sourceLocation.offset;
+}
+
+QList<Diagnostic> getDiagnostics(CXTranslationUnit tu)
+{
+ QList<Diagnostic> result;
+ const unsigned count = clang_getNumDiagnostics(tu);
+ result.reserve(int(count));
+ for (unsigned i = 0; i < count; ++i) {
+ const CXDiagnostic d = clang_getDiagnostic(tu, i);
+ result.append(Diagnostic::fromCXDiagnostic(d));
+ clang_disposeDiagnostic(d);
+ }
+ return result;
+}
+
+std::pair<qsizetype, qsizetype>
+ parseTemplateArgumentList(const QString &l,
+ const TemplateArgumentHandler &handler,
+ qsizetype from)
+{
+ const auto ltPos = l.indexOf(u'<', from);
+ if (ltPos == - 1)
+ return std::make_pair(-1, -1);
+ auto startPos = ltPos + 1;
+ int level = 1;
+ for (qsizetype p = startPos, end = l.size(); p < end; ) {
+ const char c = l.at(p).toLatin1();
+ switch (c) {
+ case ',':
+ case '>':
+ handler(level, QStringView{l}.mid(startPos, p - startPos).trimmed());
+ ++p;
+ if (c == '>') {
+ if (--level == 0)
+ return std::make_pair(ltPos, p);
+ // Skip over next ',': "a<b<c,d>,e>"
+ for (; p < end && (l.at(p).isSpace() || l.at(p) == u','); ++p) {}
+ }
+ startPos = p;
+ break;
+ case '<':
+ handler(level, QStringView{l}.mid(startPos, p - startPos).trimmed());
+ ++level;
+ startPos = ++p;
+ break;
+ default:
+ ++p;
+ break;
+ }
+ }
+ return std::make_pair(-1, -1);
+}
+
+CXDiagnosticSeverity maxSeverity(const QList<Diagnostic> &ds)
+{
+ CXDiagnosticSeverity result = CXDiagnostic_Ignored;
+ for (const Diagnostic& d : ds) {
+ if (d.severity > result)
+ result = d.severity;
+ }
+ return result;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<(QDebug s, const SourceLocation &l)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ s.noquote();
+ s << QDir::toNativeSeparators(clang::getFileName(l.file)) << ':' << l.line;
+ if (l.column)
+ s << ':' << l.column;
+ return s;
+}
+
+// Roughly follow g++ format:
+// file.cpp:214:37: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
+QDebug operator<<(QDebug s, const Diagnostic &d)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ s.noquote();
+ s << d.file << ':'<< d.line << ':' << d.column << ": ";
+ switch (d.severity) {
+ case CXDiagnostic_Ignored:
+ s << "ignored";
+ break;
+ case CXDiagnostic_Note:
+ s << "note";
+ break;
+ case CXDiagnostic_Warning:
+ s << "warning";
+ break;
+ case CXDiagnostic_Error:
+ s << "error";
+ break;
+ case CXDiagnostic_Fatal:
+ s << "fatal";
+ break;
+ }
+ s << ": " << d.message;
+
+ if (d.source != Diagnostic::Clang)
+ s << " [other]";
+
+ if (const auto childMessagesCount = d.childMessages.size()) {
+ s << '\n';
+ for (qsizetype i = 0; i < childMessagesCount; ++i)
+ s << " " << d.childMessages.at(i) << '\n';
+ }
+
+ return s;
+}
+
+#endif // QT_NO_DEBUG_STREAM
+
+} // namespace clang
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangutils.h b/sources/shiboken6/ApiExtractor/clangparser/clangutils.h
new file mode 100644
index 000000000..fbbf95f1b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangutils.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CLANGUTILS_H
+#define CLANGUTILS_H
+
+#include <clang-c/Index.h>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QtCompare>
+#include <QtCore/QList>
+
+#include <functional>
+#include <utility>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+bool operator==(const CXCursor &c1, const CXCursor &c2) noexcept;
+size_t qHash(const CXCursor &c, size_t seed = 0) noexcept;
+
+bool operator==(const CXType &t1, const CXType &t2) noexcept;
+size_t qHash(const CXType &ct, size_t seed = 0) noexcept;
+
+namespace clang {
+
+QString getCursorKindName(CXCursorKind cursorKind);
+QString getCursorSpelling(const CXCursor &cursor);
+QString getCursorDisplayName(const CXCursor &cursor);
+QString getTypeName(const CXType &type);
+bool hasScopeResolution(const CXType &type);
+CXType fullyResolveType(const CXType &type);
+QString getResolvedTypeName(const CXType &type);
+inline QString getCursorTypeName(const CXCursor &cursor)
+ { return getTypeName(clang_getCursorType(cursor)); }
+inline QString getCursorResultTypeName(const CXCursor &cursor)
+ { return getTypeName(clang_getCursorResultType(cursor)); }
+
+inline bool isCursorValid(const CXCursor &c)
+{
+ return c.kind < CXCursor_FirstInvalid || c.kind > CXCursor_LastInvalid;
+}
+
+QString getFileName(CXFile file); // Uncached,see BaseVisitor for a cached version
+
+struct SourceLocation
+{
+ bool equals(const SourceLocation &rhs) const;
+
+ CXFile file = nullptr;
+ unsigned line = 0;
+ unsigned column = 0;
+ unsigned offset = 0;
+
+ friend constexpr bool comparesEqual(const SourceLocation &lhs,
+ const SourceLocation &rhs) noexcept
+ {
+ return lhs.file == rhs.file && lhs.offset == rhs.offset;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(SourceLocation)
+};
+
+SourceLocation getExpansionLocation(const CXSourceLocation &location);
+
+using SourceRange = std::pair<SourceLocation, SourceLocation>;
+
+SourceLocation getCursorLocation(const CXCursor &cursor);
+CXString getFileNameFromLocation(const CXSourceLocation &location);
+SourceRange getCursorRange(const CXCursor &cursor);
+
+struct Diagnostic {
+ enum Source { Clang, Other };
+
+ Diagnostic() = default;
+ // Clang
+ static Diagnostic fromCXDiagnostic(CXDiagnostic cd);
+ // Other
+ explicit Diagnostic(const QString &m, const CXCursor &c, CXDiagnosticSeverity s = CXDiagnostic_Warning);
+ void setLocation(const SourceLocation &);
+
+ QString message;
+ QStringList childMessages;
+ QString file;
+ unsigned line = 0;
+ unsigned column = 0;
+ unsigned offset = 0;
+ Source source = Clang;
+ CXDiagnosticSeverity severity = CXDiagnostic_Warning;
+};
+
+QList<Diagnostic> getDiagnostics(CXTranslationUnit tu);
+CXDiagnosticSeverity maxSeverity(const QList<Diagnostic> &ds);
+
+// Parse a template argument list "a<b<c,d>,e>" and invoke a handler
+// with each match (level and string). Return begin and end of the list.
+using TemplateArgumentHandler = std::function<void (int, QStringView)>;
+
+std::pair<qsizetype, qsizetype>
+ parseTemplateArgumentList(const QString &l,
+ const TemplateArgumentHandler &handler,
+ qsizetype from = 0);
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug, const SourceLocation &);
+QDebug operator<<(QDebug, const Diagnostic &);
+#endif // QT_NO_DEBUG_STREAM
+} // namespace clang
+
+#endif // CLANGUTILS_H
diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
new file mode 100644
index 000000000..20224020b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
@@ -0,0 +1,456 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "compilersupport.h"
+#include "header_paths.h"
+#include "clangutils.h"
+
+#include <reporthandler.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QProcess>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QStringList>
+#include <QtCore/QVersionNumber>
+
+#include <clang-c/Index.h>
+
+#include <algorithm>
+#include <iterator>
+
+using namespace Qt::StringLiterals;
+
+namespace clang {
+
+QVersionNumber libClangVersion()
+{
+ return QVersionNumber(CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR);
+}
+
+static Compiler _compiler =
+#if defined (Q_CC_CLANG)
+ Compiler::Clang;
+#elif defined (Q_CC_MSVC)
+ Compiler::Msvc;
+#else
+ Compiler::Gpp;
+#endif
+
+Compiler compiler() { return _compiler; }
+
+bool setCompiler(const QString &name)
+{
+ bool result = true;
+ if (name == u"msvc")
+ _compiler = Compiler::Msvc;
+ else if (name == u"g++")
+ _compiler = Compiler::Gpp;
+ else if (name == u"clang")
+ _compiler = Compiler::Clang;
+ else
+ result = false;
+ return result;
+}
+
+QString _compilerPath; // Pre-defined compiler path (from command line)
+
+const QString &compilerPath()
+{
+ return _compilerPath;
+}
+
+void setCompilerPath(const QString &name)
+{
+ _compilerPath = name;
+}
+
+static Platform _platform =
+#if defined (Q_OS_DARWIN)
+ Platform::macOS;
+#elif defined (Q_OS_WIN)
+ Platform::Windows;
+#else
+ Platform::Unix;
+#endif
+
+Platform platform() { return _platform; }
+
+bool setPlatform(const QString &name)
+{
+ bool result = true;
+ if (name == u"windows")
+ _platform = Platform::Windows;
+ else if (name == u"darwin")
+ _platform = Platform::macOS;
+ else if (name == u"unix")
+ _platform = Platform::Unix;
+ else
+ result = false;
+ return result;
+}
+
+// 3/2024: Use a recent MSVC2022 for libclang 18.X
+static QByteArray msvcCompatVersion()
+{
+ return libClangVersion() >= QVersionNumber(0, 64) ? "19.39"_ba : "19.26"_ba;
+}
+
+static bool runProcess(const QString &program, const QStringList &arguments,
+ QByteArray *stdOutIn = nullptr, QByteArray *stdErrIn = nullptr)
+{
+ QProcess process;
+ process.start(program, arguments, QProcess::ReadWrite);
+ if (!process.waitForStarted()) {
+ qCWarning(lcShiboken).noquote().nospace() << "Unable to start "
+ << process.program() << ": " << process.errorString();
+ return false;
+ }
+ process.closeWriteChannel();
+ const bool finished = process.waitForFinished();
+ const QByteArray stdErr = process.readAllStandardError();
+ if (stdErrIn)
+ *stdErrIn = stdErr;
+ if (stdOutIn)
+ *stdOutIn = process.readAllStandardOutput();
+
+ if (!finished) {
+ qCWarning(lcShiboken).noquote().nospace() << process.program() << " timed out: " << stdErr;
+ process.kill();
+ return false;
+ }
+
+ if (process.exitStatus() != QProcess::NormalExit) {
+ qCWarning(lcShiboken).noquote().nospace() << process.program() << " crashed: " << stdErr;
+ return false;
+ }
+
+ if (process.exitCode() != 0) {
+ qCWarning(lcShiboken).noquote().nospace() << process.program() << " exited "
+ << process.exitCode() << ": " << stdErr;
+ return false;
+ }
+
+ return true;
+}
+
+static QByteArray frameworkPath() { return QByteArrayLiteral(" (framework directory)"); }
+
+static void filterHomebrewHeaderPaths(HeaderPaths &headerPaths)
+{
+ QByteArray homebrewPrefix = qgetenv("HOMEBREW_OPT");
+
+ // If HOMEBREW_OPT is found we assume that the build is happening
+ // inside a brew environment, which means we need to filter out
+ // the -isystem flags added by the brew clang shim. This is needed
+ // because brew passes the Qt include paths as system include paths
+ // and because our parser ignores system headers, Qt classes won't
+ // be found and thus compilation errors will occur.
+ if (homebrewPrefix.isEmpty())
+ return;
+
+ qCInfo(lcShiboken) << "Found HOMEBREW_OPT with value:" << homebrewPrefix
+ << "Assuming homebrew build environment.";
+
+ HeaderPaths::iterator it = headerPaths.begin();
+ while (it != headerPaths.end()) {
+ if (it->path.startsWith(homebrewPrefix)) {
+ qCInfo(lcShiboken) << "Filtering out homebrew include path: "
+ << it->path;
+ it = headerPaths.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+// Determine g++'s internal include paths from the output of
+// g++ -E -x c++ - -v </dev/null
+// Output looks like:
+// #include <...> search starts here:
+// /usr/local/include
+// /System/Library/Frameworks (framework directory)
+// End of search list.
+static HeaderPaths gppInternalIncludePaths(const QString &compiler)
+{
+ HeaderPaths result;
+ QStringList arguments{u"-E"_s, u"-x"_s, u"c++"_s, u"-"_s, u"-v"_s};
+ QByteArray stdOut;
+ QByteArray stdErr;
+ if (!runProcess(compiler, arguments, &stdOut, &stdErr))
+ return result;
+ const QByteArrayList stdErrLines = stdErr.split('\n');
+ bool isIncludeDir = false;
+
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug))
+ qCInfo(lcShiboken()).noquote().nospace()
+ << "gppInternalIncludePaths:\n compiler: " << compiler
+ << "\n stdOut: " << stdOut
+ << "\n stdErr: " << stdErr;
+
+ for (const QByteArray &line : stdErrLines) {
+ if (isIncludeDir) {
+ if (line.startsWith(QByteArrayLiteral("End of search list"))) {
+ isIncludeDir = false;
+ } else {
+ HeaderPath headerPath{line.trimmed(), HeaderType::System};
+ if (headerPath.path.endsWith(frameworkPath())) {
+ headerPath.type = HeaderType::FrameworkSystem;
+ headerPath.path.truncate(headerPath.path.size() - frameworkPath().size());
+ }
+ result.append(headerPath);
+ }
+ } else if (line.startsWith(QByteArrayLiteral("#include <...> search starts here"))) {
+ isIncludeDir = true;
+ }
+ }
+
+ if (platform() == Platform::macOS)
+ filterHomebrewHeaderPaths(result);
+
+ return result;
+}
+
+// Detect Vulkan as supported from Qt 5.10 by checking the environment variables.
+QByteArrayList detectVulkan()
+{
+ static const char *vulkanVariables[] = {"VULKAN_SDK", "VK_SDK_PATH"};
+ for (const char *vulkanVariable : vulkanVariables) {
+ if (qEnvironmentVariableIsSet(vulkanVariable)) {
+ const auto option = QByteArrayLiteral("-isystem")
+ + qgetenv(vulkanVariable)
+ + QByteArrayLiteral("/include");
+ return {option};
+ }
+ }
+ return {};
+}
+
+// For MSVC, we set the MS compatibility version and let Clang figure out its own
+// options and include paths.
+// For the others, we pass "-nostdinc" since libclang tries to add it's own system
+// include paths, which together with the clang compiler paths causes some clash
+// which causes std types not being found and construct -I/-F options from the
+// include paths of the host compiler.
+
+static QByteArray noStandardIncludeOption() { return QByteArrayLiteral("-nostdinc"); }
+
+// The clang builtin includes directory is used to find the definitions for
+// intrinsic functions and builtin types. It is necessary to use the clang
+// includes to prevent redefinition errors. The default toolchain includes
+// should be picked up automatically by clang without specifying
+// them implicitly.
+
+// Besides g++/Linux, as of MSVC 19.28.29334, MSVC needs clang includes
+// due to PYSIDE-1433, LLVM-47099
+
+static bool needsClangBuiltinIncludes()
+{
+ return platform() != Platform::macOS;
+}
+
+static QString queryLlvmConfigDir(const QString &arg)
+{
+ static const QString llvmConfig = QStandardPaths::findExecutable(u"llvm-config"_s);
+ if (llvmConfig.isEmpty())
+ return {};
+ QByteArray stdOut;
+ if (!runProcess(llvmConfig, QStringList{arg}, &stdOut))
+ return {};
+ const QString path = QFile::decodeName(stdOut.trimmed());
+ if (!QFileInfo::exists(path)) {
+ qCWarning(lcShiboken, R"(%s: "%s" as returned by llvm-config "%s" does not exist.)",
+ __FUNCTION__, qPrintable(QDir::toNativeSeparators(path)), qPrintable(arg));
+ return {};
+ }
+ return path;
+}
+
+static QString findClangLibDir()
+{
+ for (const char *envVar : {"LLVM_INSTALL_DIR", "CLANG_INSTALL_DIR"}) {
+ if (qEnvironmentVariableIsSet(envVar)) {
+ const QString path = QFile::decodeName(qgetenv(envVar)) + u"/lib"_s;
+ if (QFileInfo::exists(path))
+ return path;
+ qCWarning(lcShiboken, "%s: %s as pointed to by %s does not exist.",
+ __FUNCTION__, qPrintable(path), envVar);
+ }
+ }
+ return queryLlvmConfigDir(u"--libdir"_s);
+}
+
+static QString findClangBuiltInIncludesDir()
+{
+ // Find the include directory of the highest version.
+ const QString clangPathLibDir = findClangLibDir();
+ if (!clangPathLibDir.isEmpty()) {
+ QString candidate;
+ QString clangDirName = clangPathLibDir + u"/clang"_s;
+ // PYSIDE-2769: llvm-config --libdir may report /usr/lib64 on manylinux_2_28_x86_64
+ // whereas the includes are under /usr/lib/clang/../include.
+ if (!QFileInfo::exists(clangDirName) && clangPathLibDir.endsWith("64"_L1)) {
+ const QString fallback = clangPathLibDir.sliced(0, clangPathLibDir.size() - 2);
+ clangDirName = fallback + u"/clang"_s;
+ qCWarning(lcShiboken, "%s: Falling back from %s to %s.",
+ __FUNCTION__, qPrintable(clangPathLibDir), qPrintable(fallback));
+ }
+
+ QVersionNumber lastVersionNumber(1, 0, 0);
+ QDir clangDir(clangDirName);
+ const QFileInfoList versionDirs =
+ clangDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ if (versionDirs.isEmpty())
+ qCWarning(lcShiboken, "%s: No subdirectories found in %s.",
+ __FUNCTION__, qPrintable(clangDirName));
+ for (const QFileInfo &fi : versionDirs) {
+ const QString fileName = fi.fileName();
+ if (fileName.at(0).isDigit()) {
+ const QVersionNumber versionNumber = QVersionNumber::fromString(fileName);
+ if (!versionNumber.isNull() && versionNumber > lastVersionNumber) {
+ candidate = fi.absoluteFilePath();
+ lastVersionNumber = versionNumber;
+ }
+ }
+ }
+ if (!candidate.isEmpty())
+ return candidate + "/include"_L1;
+ }
+ return queryLlvmConfigDir(u"--includedir"_s);
+}
+
+QString compilerFromCMake()
+{
+#ifdef CMAKE_CXX_COMPILER
+ return QString::fromLocal8Bit(CMAKE_CXX_COMPILER);
+#else
+ return {};
+#endif
+}
+
+// Return a compiler suitable for determining the internal include paths
+static QString compilerFromCMake(const QString &defaultCompiler)
+{
+ if (!compilerPath().isEmpty())
+ return compilerPath();
+ // Exclude macOS since cmakeCompiler returns the full path instead of the
+ // /usr/bin/clang shim, which results in the default SDK sysroot path
+ // missing (PYSIDE-1032)
+ if (platform() == Platform::macOS)
+ return defaultCompiler;
+ QString cmakeCompiler = compilerFromCMake();
+ if (cmakeCompiler.isEmpty())
+ return defaultCompiler;
+ QFileInfo fi(cmakeCompiler);
+ // Should be absolute by default, but a user may specify -DCMAKE_CXX_COMPILER=cl.exe
+ if (fi.isRelative())
+ return cmakeCompiler;
+ if (fi.exists())
+ return fi.absoluteFilePath();
+ // The compiler may not exist in case something like icecream or
+ // a non-standard-path was used on the build machine. Check
+ // the executable.
+ cmakeCompiler = QStandardPaths::findExecutable(fi.fileName());
+ return cmakeCompiler.isEmpty() ? defaultCompiler : cmakeCompiler;
+}
+
+static void appendClangBuiltinIncludes(HeaderPaths *p)
+{
+ const QString clangBuiltinIncludesDir =
+ QDir::toNativeSeparators(findClangBuiltInIncludesDir());
+ if (clangBuiltinIncludesDir.isEmpty()) {
+ qCWarning(lcShiboken, "Unable to locate Clang's built-in include directory "
+ "(neither by checking the environment variables LLVM_INSTALL_DIR, CLANG_INSTALL_DIR "
+ " nor running llvm-config). This may lead to parse errors.");
+ } else {
+ qCInfo(lcShiboken, "CLANG v%d.%d, builtins includes directory: %s",
+ CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR,
+ qPrintable(clangBuiltinIncludesDir));
+ p->append(HeaderPath{QFile::encodeName(clangBuiltinIncludesDir),
+ HeaderType::System});
+ }
+}
+
+// Returns clang options needed for emulating the host compiler
+QByteArrayList emulatedCompilerOptions(LanguageLevel level)
+{
+ QByteArrayList result;
+ HeaderPaths headerPaths;
+ switch (compiler()) {
+ case Compiler::Msvc:
+ result.append("-fms-compatibility-version="_ba + msvcCompatVersion());
+ if (level < LanguageLevel::Cpp20)
+ result.append("-fdelayed-template-parsing"_ba);
+ result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
+ result.append("/Zc:__cplusplus"_ba);
+ // Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update)
+ result.append(QByteArrayLiteral("-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH"));
+ if (needsClangBuiltinIncludes())
+ appendClangBuiltinIncludes(&headerPaths);
+ break;
+ case Compiler::Clang:
+ headerPaths.append(gppInternalIncludePaths(compilerFromCMake(u"clang++"_s)));
+ result.append(noStandardIncludeOption());
+ break;
+ case Compiler::Gpp:
+ if (needsClangBuiltinIncludes())
+ appendClangBuiltinIncludes(&headerPaths);
+
+ // Append the c++ include paths since Clang is unable to find
+ // <type_traits> etc (g++ 11.3).
+ const HeaderPaths gppPaths = gppInternalIncludePaths(compilerFromCMake(u"g++"_s));
+ for (const HeaderPath &h : gppPaths) {
+ if (h.path.contains("c++") || h.path.contains("sysroot"))
+ headerPaths.append(h);
+ }
+ break;
+ }
+
+ std::transform(headerPaths.cbegin(), headerPaths.cend(),
+ std::back_inserter(result), HeaderPath::includeOption);
+ return result;
+}
+
+LanguageLevel emulatedCompilerLanguageLevel()
+{
+ return LanguageLevel::Cpp17;
+}
+
+struct LanguageLevelMapping
+{
+ const char *option;
+ LanguageLevel level;
+};
+
+static const LanguageLevelMapping languageLevelMapping[] =
+{
+ {"c++11", LanguageLevel::Cpp11},
+ {"c++14", LanguageLevel::Cpp14},
+ {"c++17", LanguageLevel::Cpp17},
+ {"c++20", LanguageLevel::Cpp20},
+ {"c++1z", LanguageLevel::Cpp1Z}
+};
+
+const char *languageLevelOption(LanguageLevel l)
+{
+ for (const LanguageLevelMapping &m : languageLevelMapping) {
+ if (m.level == l)
+ return m.option;
+ }
+ return nullptr;
+}
+
+LanguageLevel languageLevelFromOption(const char *o)
+{
+ for (const LanguageLevelMapping &m : languageLevelMapping) {
+ if (!strcmp(m.option, o))
+ return m.level;
+ }
+ return LanguageLevel::Default;
+}
+
+} // namespace clang
diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h
new file mode 100644
index 000000000..f1d63b7c3
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef COMPILERSUPPORT_H
+#define COMPILERSUPPORT_H
+
+#include <QtCore/QByteArrayList>
+
+QT_FORWARD_DECLARE_CLASS(QVersionNumber)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+enum class LanguageLevel {
+ Default,
+ Cpp11,
+ Cpp14,
+ Cpp17,
+ Cpp20,
+ Cpp1Z
+};
+
+enum class Compiler {
+ Msvc,
+ Gpp,
+ Clang
+};
+
+enum class Platform {
+ Unix,
+ Windows,
+ macOS
+};
+
+namespace clang {
+QVersionNumber libClangVersion();
+
+QByteArrayList emulatedCompilerOptions(LanguageLevel level);
+LanguageLevel emulatedCompilerLanguageLevel();
+
+const char *languageLevelOption(LanguageLevel l);
+LanguageLevel languageLevelFromOption(const char *);
+
+QByteArrayList detectVulkan();
+
+Compiler compiler();
+bool setCompiler(const QString &name);
+
+QString compilerFromCMake();
+
+const QString &compilerPath();
+void setCompilerPath(const QString &name);
+
+Platform platform();
+bool setPlatform(const QString &name);
+} // namespace clang
+
+#endif // COMPILERSUPPORT_H
diff --git a/sources/shiboken6/ApiExtractor/classdocumentation.cpp b/sources/shiboken6/ApiExtractor/classdocumentation.cpp
new file mode 100644
index 000000000..637e4a422
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/classdocumentation.cpp
@@ -0,0 +1,381 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "classdocumentation.h"
+#include "messages.h"
+#include "debughelpers_p.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QBuffer>
+#include <QtCore/QFile>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamAttributes>
+#include <QtCore/QXmlStreamWriter>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+// Sort functions by name and argument count
+static bool functionDocumentationLessThan(const FunctionDocumentation &f1,
+ const FunctionDocumentation &f2)
+{
+ const int nc = f1.name.compare(f2.name);
+ if (nc != 0)
+ return nc < 0;
+ return f1.parameters.size() < f2.parameters.size();
+}
+
+static void sortDocumentation(ClassDocumentation *cd)
+{
+ std::stable_sort(cd->enums.begin(), cd->enums.end(),
+ [] (const EnumDocumentation &e1, const EnumDocumentation &e2) {
+ return e1.name < e2.name; });
+ std::stable_sort(cd->properties.begin(), cd->properties.end(),
+ [] (const PropertyDocumentation &p1, const PropertyDocumentation &p2) {
+ return p1.name < p2.name; });
+ std::stable_sort(cd->functions.begin(), cd->functions.end(),
+ functionDocumentationLessThan);
+}
+
+qsizetype ClassDocumentation::indexOfEnum(const QString &name) const
+{
+ for (qsizetype i = 0, size = enums.size(); i < size; ++i) {
+ if (enums.at(i).name == name)
+ return i;
+ }
+ return -1;
+}
+
+FunctionDocumentationList ClassDocumentation::findFunctionCandidates(const QString &name,
+ bool constant) const
+{
+ FunctionDocumentationList result;
+ std::copy_if(functions.cbegin(), functions.cend(),
+ std::back_inserter(result),
+ [name, constant](const FunctionDocumentation &fd) {
+ return fd.constant == constant && fd.name == name;
+ });
+ return result;
+}
+
+static bool matches(const FunctionDocumentation &fd, const FunctionDocumentationQuery &q)
+{
+ return fd.name == q.name && fd.constant == q.constant && fd.parameters == q.parameters;
+}
+
+qsizetype ClassDocumentation::indexOfFunction(const FunctionDocumentationList &fl,
+ const FunctionDocumentationQuery &q)
+{
+ for (qsizetype i = 0, size = fl.size(); i < size; ++i) {
+ if (matches(fl.at(i), q))
+ return i;
+ }
+ return -1;
+}
+
+qsizetype ClassDocumentation::indexOfProperty(const QString &name) const
+{
+ for (qsizetype i = 0, size = properties.size(); i < size; ++i) {
+ if (properties.at(i).name == name)
+ return i;
+ }
+ return -1;
+}
+
+enum class WebXmlCodeTag
+{
+ Class, Description, Enum, Function, Header, Parameter, Property, Typedef, Other
+};
+
+static WebXmlCodeTag tag(QStringView name)
+{
+ if (name == u"class" || name == u"namespace")
+ return WebXmlCodeTag::Class;
+ if (name == u"enum")
+ return WebXmlCodeTag::Enum;
+ if (name == u"function")
+ return WebXmlCodeTag::Function;
+ if (name == u"description")
+ return WebXmlCodeTag::Description;
+ if (name == u"header")
+ return WebXmlCodeTag::Header;
+ if (name == u"parameter")
+ return WebXmlCodeTag::Parameter;
+ if (name == u"property")
+ return WebXmlCodeTag::Property;
+ if (name == u"typedef")
+ return WebXmlCodeTag::Typedef;
+ return WebXmlCodeTag::Other;
+}
+
+static void parseWebXmlElement(WebXmlCodeTag tag, const QXmlStreamAttributes &attributes,
+ ClassDocumentation *cd)
+{
+ switch (tag) {
+ case WebXmlCodeTag::Class:
+ cd->name = attributes.value(u"name"_s).toString();
+ cd->type = ClassDocumentation::Class;
+ break;
+ case WebXmlCodeTag::Header:
+ cd->name = attributes.value(u"name"_s).toString();
+ cd->type = ClassDocumentation::Header;
+ break;
+ case WebXmlCodeTag::Enum: {
+ EnumDocumentation ed;
+ ed.name = attributes.value(u"name"_s).toString();
+ cd->enums.append(ed);
+ }
+ break;
+ case WebXmlCodeTag::Function: {
+ FunctionDocumentation fd;
+ fd.name = attributes.value(u"name"_s).toString();
+ fd.signature = attributes.value(u"signature"_s).toString();
+ fd.returnType = attributes.value(u"type"_s).toString();
+ fd.constant = attributes.value(u"const"_s) == u"true";
+ cd->functions.append(fd);
+ }
+ break;
+ case WebXmlCodeTag::Parameter:
+ Q_ASSERT(!cd->functions.isEmpty());
+ cd->functions.last().parameters.append(attributes.value(u"type"_s).toString());
+ break;
+ case WebXmlCodeTag::Property: {
+ PropertyDocumentation pd;
+ pd.name = attributes.value(u"name"_s).toString();
+ pd.brief = attributes.value(u"brief"_s).toString();
+ cd->properties.append(pd);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// Retrieve the contents of <description>
+static QString extractWebXmlDescription(QXmlStreamReader &reader)
+{
+ QBuffer buffer;
+ buffer.open(QIODeviceBase::WriteOnly);
+ QXmlStreamWriter writer(&buffer);
+
+ do {
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ writer.writeStartElement(reader.name().toString());
+ writer.writeAttributes(reader.attributes());
+ break;
+ case QXmlStreamReader::Characters:
+ writer.writeCharacters(reader.text().toString());
+ break;
+ case QXmlStreamReader::EndElement:
+ writer.writeEndElement();
+ if (reader.name() == u"description") {
+ buffer.close();
+ return QString::fromUtf8(buffer.buffer()).trimmed();
+ }
+ break;
+ default:
+ break;
+ }
+ reader.readNext();
+ } while (!reader.atEnd());
+
+ return {};
+}
+
+static QString msgXmlError(const QString &fileName, const QXmlStreamReader &reader)
+{
+ QString result;
+ QTextStream(&result) << fileName << ':' << reader.lineNumber() << ':'
+ << reader.columnNumber() << ':' << reader.errorString();
+ return result;
+}
+
+std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage)
+{
+ ClassDocumentation result;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ *errorMessage = msgCannotOpenForReading(file);
+ return std::nullopt;
+ }
+
+ WebXmlCodeTag lastTag = WebXmlCodeTag::Other;
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::StartElement: {
+ const auto currentTag = tag(reader.name());
+ parseWebXmlElement(currentTag, reader.attributes(), &result);
+ switch (currentTag) { // Store relevant tags in lastTag
+ case WebXmlCodeTag::Class:
+ case WebXmlCodeTag::Function:
+ case WebXmlCodeTag::Enum:
+ case WebXmlCodeTag::Header:
+ case WebXmlCodeTag::Property:
+ case WebXmlCodeTag::Typedef:
+ lastTag = currentTag;
+ break;
+ case WebXmlCodeTag::Description: { // Append the description to the element
+ QString *target = nullptr;
+ switch (lastTag) {
+ case WebXmlCodeTag::Class:
+ target = &result.description;
+ break;
+ case WebXmlCodeTag::Function:
+ target = &result.functions.last().description;
+ break;
+ case WebXmlCodeTag::Enum:
+ target = &result.enums.last().description;
+ break;
+ case WebXmlCodeTag::Property:
+ target = &result.properties.last().description;
+ default:
+ break;
+ }
+ if (target != nullptr && target->isEmpty())
+ *target = extractWebXmlDescription(reader);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ if (reader.error() != QXmlStreamReader::NoError) {
+ *errorMessage= msgXmlError(fileName, reader);
+ return std::nullopt;
+ }
+
+ sortDocumentation(&result);
+ return result;
+}
+
+QString webXmlModuleDescription(const QString &fileName, QString *errorMessage)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ *errorMessage = msgCannotOpenForReading(file);
+ return {};
+ }
+
+ QString result;
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == u"description")
+ result = extractWebXmlDescription(reader);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (reader.error() != QXmlStreamReader::NoError) {
+ *errorMessage= msgXmlError(fileName, reader);
+ return {};
+ }
+
+ return result;
+}
+
+static void formatDescription(QDebug &debug, const QString &desc)
+{
+ debug << "description=";
+ if (desc.isEmpty()) {
+ debug << "<empty>";
+ return;
+ }
+ if (debug.verbosity() < 3)
+ debug << desc.size() << " chars";
+ else
+ debug << '"' << desc << '"';
+}
+
+QDebug operator<<(QDebug debug, const EnumDocumentation &e)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Enum(";
+ if (e.name.isEmpty()) {
+ debug << "invalid";
+ } else {
+ debug << e.name << ", ";
+ formatDescription(debug, e.description);
+ }
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const PropertyDocumentation &p)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Property(";
+ if (p.name.isEmpty()) {
+ debug << "invalid";
+ } else {
+ debug << p.name << ", ";
+ formatDescription(debug, p.description);
+ }
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const FunctionDocumentation &f)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Function(";
+ if (f.name.isEmpty()) {
+ debug << "invalid";
+ } else {
+ debug << f.name;
+ if (!f.returnType.isEmpty())
+ debug << ", returns " << f.returnType;
+ if (f.constant)
+ debug << ", const";
+ formatList(debug, ", parameters", f.parameters, ", ");
+ debug << ", signature=\"" << f.signature << "\", ";
+ formatDescription(debug, f.description);
+ }
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const FunctionDocumentationQuery &q)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "FunctionQuery(" << q.name;
+ if (q.constant)
+ debug << ", const";
+ formatList(debug, ", parameters", q.parameters);
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const ClassDocumentation &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Class(" << c.name << ", ";
+ formatDescription(debug, c.description);
+ formatList(debug, ", enums", c.enums);
+ formatList(debug, ", properties", c.properties);
+ formatList(debug, ", functions", c.functions);
+ debug << ')';
+ return debug;
+}
diff --git a/sources/shiboken6/ApiExtractor/classdocumentation.h b/sources/shiboken6/ApiExtractor/classdocumentation.h
new file mode 100644
index 000000000..d47101389
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/classdocumentation.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CLASSDOCUMENTATION_H
+#define CLASSDOCUMENTATION_H
+
+#include <QtCore/QStringList>
+
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+/// An enumeration in a WebXML/doxygen document
+struct EnumDocumentation
+{
+ QString name;
+ QString description;
+};
+
+/// A QObject property in a WebXML/doxygen document
+struct PropertyDocumentation
+{
+ QString name;
+ QString brief;
+ QString description;
+};
+
+/// Helper struct for querying a function in a WebXML/doxygen document
+struct FunctionDocumentationQuery
+{
+ QString name;
+ QStringList parameters;
+ bool constant = false;
+};
+
+/// A function in a WebXML/doxygen document
+struct FunctionDocumentation : public FunctionDocumentationQuery
+{
+ QString signature;
+ QString returnType;
+ QString description;
+};
+
+using FunctionDocumentationList = QList<FunctionDocumentation>;
+
+/// A WebXML/doxygen document
+struct ClassDocumentation
+{
+ enum Type {
+ Class, // <class>, class/namespace
+ Header // <header>, grouped global functions/enums
+ };
+
+ qsizetype indexOfEnum(const QString &name) const;
+ FunctionDocumentationList findFunctionCandidates(const QString &name,
+ bool constant) const;
+ static qsizetype indexOfFunction(const FunctionDocumentationList &fl,
+ const FunctionDocumentationQuery &q);
+ qsizetype indexOfProperty(const QString &name) const;
+
+ Type type = Type::Class;
+ QString name;
+ QString description;
+
+ QList<EnumDocumentation> enums;
+ QList<PropertyDocumentation> properties;
+ FunctionDocumentationList functions;
+};
+
+/// Parse a WebXML class/namespace document
+std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage);
+
+/// Extract the module description from a WebXML module document
+QString webXmlModuleDescription(const QString &fileName, QString *errorMessage);
+
+QDebug operator<<(QDebug debug, const EnumDocumentation &e);
+QDebug operator<<(QDebug debug, const PropertyDocumentation &p);
+QDebug operator<<(QDebug debug, const FunctionDocumentationQuery &q);
+QDebug operator<<(QDebug debug, const FunctionDocumentation &f);
+QDebug operator<<(QDebug debug, const ClassDocumentation &c);
+
+#endif // CLASSDOCUMENTATION_H
diff --git a/sources/shiboken6/ApiExtractor/cmake_uninstall.cmake b/sources/shiboken6/ApiExtractor/cmake_uninstall.cmake
new file mode 100644
index 000000000..4031b4e1a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/cmake_uninstall.cmake
@@ -0,0 +1,24 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+ MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
+ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+STRING(REGEX REPLACE "\n" ";" files "${files}")
+FOREACH(file ${files})
+ MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
+ IF(EXISTS "$ENV{DESTDIR}${file}")
+ EXEC_PROGRAM(
+ "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+ OUTPUT_VARIABLE rm_out
+ RETURN_VALUE rm_retval
+ )
+ IF(NOT "${rm_retval}" STREQUAL 0)
+ MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
+ ENDIF(NOT "${rm_retval}" STREQUAL 0)
+ ELSE(EXISTS "$ENV{DESTDIR}${file}")
+ MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
+ ENDIF(EXISTS "$ENV{DESTDIR}${file}")
+ENDFOREACH(file)
diff --git a/sources/shiboken6/ApiExtractor/codesnip.cpp b/sources/shiboken6/ApiExtractor/codesnip.cpp
new file mode 100644
index 000000000..e2cd5eb35
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/codesnip.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "codesnip.h"
+
+#include "qtcompat.h"
+#include "exception.h"
+#include "typedatabase.h"
+
+#include <QtCore/QDebug>
+
+using namespace Qt::StringLiterals;
+
+QString TemplateInstance::expandCode() const
+{
+ const auto templateEntry = TypeDatabase::instance()->findTemplate(m_name);
+ if (!templateEntry) {
+ const QString m = u"<insert-template> referring to non-existing template '"_s
+ + m_name + u"'."_s;
+ throw Exception(m);
+ }
+
+ QString code = templateEntry->code();
+ for (auto it = replaceRules.cbegin(), end = replaceRules.cend(); it != end; ++it)
+ code.replace(it.key(), it.value());
+ while (!code.isEmpty() && code.at(code.size() - 1).isSpace())
+ code.chop(1);
+ QString result = u"// TEMPLATE - "_s + m_name + u" - START"_s;
+ if (!code.startsWith(u'\n'))
+ result += u'\n';
+ result += code;
+ result += u"\n// TEMPLATE - "_s + m_name + u" - END\n"_s;
+ return result;
+}
+
+// ---------------------- CodeSnipFragment
+QString CodeSnipFragment::code() const
+{
+ return m_instance ? m_instance->expandCode() : m_code;
+}
+
+// ---------------------- CodeSnipAbstract
+QString CodeSnipAbstract::code() const
+{
+ QString res;
+ for (const CodeSnipFragment &codeFrag : codeList)
+ res.append(codeFrag.code());
+
+ return res;
+}
+
+void CodeSnipAbstract::addCode(const QString &code)
+{
+ codeList.append(CodeSnipFragment(fixSpaces(code)));
+}
+
+void CodeSnipAbstract::purgeEmptyFragments()
+{
+ auto end = std::remove_if(codeList.begin(), codeList.end(),
+ [](const CodeSnipFragment &f) { return f.isEmpty(); });
+ codeList.erase(end, codeList.end());
+}
+
+QRegularExpression CodeSnipAbstract::placeHolderRegex(int index)
+{
+ return QRegularExpression(u'%' + QString::number(index) + "\\b"_L1);
+}
+
+void purgeEmptyCodeSnips(QList<CodeSnip> *list)
+{
+ for (auto it = list->begin(); it != list->end(); ) {
+ it->purgeEmptyFragments();
+ if (it->isEmpty())
+ it = list->erase(it);
+ else
+ ++it;
+ }
+}
diff --git a/sources/shiboken6/ApiExtractor/codesnip.h b/sources/shiboken6/ApiExtractor/codesnip.h
new file mode 100644
index 000000000..86834a1db
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/codesnip.h
@@ -0,0 +1,107 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CODESNIP_H
+#define CODESNIP_H
+
+#include "codesniphelpers.h"
+#include "typesystem_enums.h"
+
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+
+#include <memory>
+
+class TemplateInstance
+{
+public:
+ explicit TemplateInstance(const QString &name) : m_name(name) {}
+
+ void addReplaceRule(const QString &name, const QString &value)
+ {
+ replaceRules[name] = value;
+ }
+
+ QString expandCode() const;
+
+ QString name() const
+ {
+ return m_name;
+ }
+
+private:
+ const QString m_name;
+ QHash<QString, QString> replaceRules;
+};
+
+using TemplateInstancePtr = std::shared_ptr<TemplateInstance>;
+
+class CodeSnipFragment
+{
+public:
+ CodeSnipFragment() = default;
+ explicit CodeSnipFragment(const QString &code) : m_code(code) {}
+ explicit CodeSnipFragment(const TemplateInstancePtr &instance) : m_instance(instance) {}
+
+ bool isEmpty() const { return m_code.isEmpty() && !m_instance; }
+
+ QString code() const;
+
+ TemplateInstancePtr instance() const { return m_instance; }
+
+private:
+ QString m_code;
+ std::shared_ptr<TemplateInstance> m_instance;
+};
+
+class CodeSnipAbstract : public CodeSnipHelpers
+{
+public:
+ QString code() const;
+
+ void addCode(const QString &code);
+ void addCode(QStringView code) { addCode(code.toString()); }
+
+ void addTemplateInstance(const TemplateInstancePtr &ti)
+ {
+ codeList.append(CodeSnipFragment(ti));
+ }
+
+ bool isEmpty() const { return codeList.isEmpty(); }
+ void purgeEmptyFragments();
+
+ QList<CodeSnipFragment> codeList;
+
+ static QRegularExpression placeHolderRegex(int index);
+};
+
+class TemplateEntry : public CodeSnipAbstract
+{
+public:
+ explicit TemplateEntry(const QString &name) : m_name(name) {}
+
+ QString name() const
+ {
+ return m_name;
+ }
+
+private:
+ QString m_name;
+};
+
+class CodeSnip : public CodeSnipAbstract
+{
+public:
+ CodeSnip() = default;
+ explicit CodeSnip(TypeSystem::Language lang) : language(lang) {}
+
+ TypeSystem::Language language = TypeSystem::TargetLangCode;
+ TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionAny;
+};
+
+/// Purge empty fragments and snippets caused by new line characters in
+/// conjunction with <insert-template>.
+void purgeEmptyCodeSnips(QList<CodeSnip> *list);
+
+#endif // CODESNIP_H
diff --git a/sources/shiboken6/ApiExtractor/codesniphelpers.cpp b/sources/shiboken6/ApiExtractor/codesniphelpers.cpp
new file mode 100644
index 000000000..775cf10af
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/codesniphelpers.cpp
@@ -0,0 +1,77 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "codesniphelpers.h"
+
+#include <QtCore/QStringList>
+
+#include <algorithm>
+
+static inline int firstNonBlank(QStringView s)
+{
+ const auto it = std::find_if(s.cbegin(), s.cend(),
+ [] (QChar c) { return !c.isSpace(); });
+ return int(it - s.cbegin());
+}
+
+static inline bool isEmpty(QStringView s)
+{
+ return s.isEmpty()
+ || std::all_of(s.cbegin(), s.cend(),
+ [] (QChar c) { return c.isSpace(); });
+}
+
+QString CodeSnipHelpers::dedent(const QString &code)
+{
+ if (code.isEmpty())
+ return code;
+ // Right trim if indent=0, or trim if single line
+ if (!code.at(0).isSpace() || !code.contains(u'\n'))
+ return code.trimmed();
+ const auto lines = QStringView{code}.split(u'\n');
+ int spacesToRemove = std::numeric_limits<int>::max();
+ for (const auto &line : lines) {
+ if (!isEmpty(line)) {
+ const int nonSpacePos = firstNonBlank(line);
+ if (nonSpacePos < spacesToRemove)
+ spacesToRemove = nonSpacePos;
+ if (spacesToRemove == 0)
+ return code;
+ }
+ }
+ QString result;
+ for (const auto &line : lines) {
+ if (!isEmpty(line) && spacesToRemove < line.size())
+ result += line.mid(spacesToRemove).toString();
+ result += u'\n';
+ }
+ return result;
+}
+
+QString CodeSnipHelpers::fixSpaces(QString code)
+{
+ code.remove(u'\r');
+ // Check for XML <tag>\n<space>bla...
+ if (code.startsWith(u"\n "))
+ code.remove(0, 1);
+ while (!code.isEmpty() && code.back().isSpace())
+ code.chop(1);
+ code = dedent(code);
+ if (!code.isEmpty() && !code.endsWith(u'\n'))
+ code.append(u'\n');
+ return code;
+}
+
+// Prepend a line to the code, observing indentation
+void CodeSnipHelpers::prependCode(QString *code, QString firstLine)
+{
+ while (!code->isEmpty() && code->front() == u'\n')
+ code->remove(0, 1);
+ if (!code->isEmpty() && code->front().isSpace()) {
+ const int indent = firstNonBlank(*code);
+ firstLine.prepend(QString(indent, u' '));
+ }
+ if (!firstLine.endsWith(u'\n'))
+ firstLine += u'\n';
+ code->prepend(firstLine);
+}
diff --git a/sources/shiboken6/ApiExtractor/codesniphelpers.h b/sources/shiboken6/ApiExtractor/codesniphelpers.h
new file mode 100644
index 000000000..e7a7545da
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/codesniphelpers.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CODESNIPHELPERS_H
+#define CODESNIPHELPERS_H
+
+#include <QtCore/QString>
+
+class CodeSnipHelpers
+{
+public:
+ static QString fixSpaces(QString code);
+ static QString dedent(const QString &code);
+ static void prependCode(QString *code, QString firstLine);
+};
+
+#endif // CODESNIPHELPERS_H
diff --git a/sources/shiboken6/ApiExtractor/complextypeentry.h b/sources/shiboken6/ApiExtractor/complextypeentry.h
new file mode 100644
index 000000000..5b884f2cc
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/complextypeentry.h
@@ -0,0 +1,179 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef COMPLEXTYPEENTRY_H
+#define COMPLEXTYPEENTRY_H
+
+#include "configurabletypeentry.h"
+#include "typesystem_enums.h"
+#include "modifications_typedefs.h"
+#include "pymethoddefentry.h"
+
+#include <QtCore/QSet>
+
+class ComplexTypeEntryPrivate;
+
+struct TypeSystemPyMethodDefEntry : public PyMethodDefEntry
+{
+ QStringList signatures;
+};
+
+struct TypeSystemProperty
+{
+ bool isValid() const { return !name.isEmpty() && !read.isEmpty() && !type.isEmpty(); }
+
+ QString type;
+ QString name;
+ QString read;
+ QString write;
+ QString reset;
+ QString designable;
+ QString notify; // Q_PROPERTY/C++ only
+ // Indicates whether actual code is generated instead of relying on libpyside.
+ bool generateGetSetDef = false;
+};
+
+class ComplexTypeEntry : public ConfigurableTypeEntry
+{
+public:
+ enum TypeFlag {
+ DisableWrapper = 0x1,
+ Deprecated = 0x4,
+ ForceAbstract = 0x8,
+ // Indicates that the instances are used to create hierarchies
+ // like widgets; parent ownership heuristics are enabled for them.
+ ParentManagement = 0x10,
+ DisableQtMetaObjectFunctions = 0x20,
+ Typedef = 0x40 // Result of a <typedef-type>
+ };
+ Q_DECLARE_FLAGS(TypeFlags, TypeFlag)
+
+ enum CopyableFlag {
+ CopyableSet,
+ NonCopyableSet,
+ Unknown
+ };
+
+ explicit ComplexTypeEntry(const QString &entryName, Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ bool isComplex() const override;
+
+ TypeFlags typeFlags() const;
+ void setTypeFlags(TypeFlags flags);
+
+ // Override command line options to generate nb_bool from
+ // operator bool or method isNull().
+ TypeSystem::BoolCast operatorBoolMode() const;
+ void setOperatorBoolMode(TypeSystem::BoolCast b);
+ TypeSystem::BoolCast isNullMode() const;
+ void setIsNullMode(TypeSystem::BoolCast b);
+
+ FunctionModificationList functionModifications() const;
+ void setFunctionModifications(const FunctionModificationList &functionModifications);
+ void addFunctionModification(const FunctionModification &functionModification);
+ FunctionModificationList functionModifications(const QStringList &signatures) const;
+
+ const CodeSnipList &codeSnips() const;
+ CodeSnipList &codeSnips();
+ void setCodeSnips(const CodeSnipList &codeSnips);
+ void addCodeSnip(const CodeSnip &codeSnip);
+
+ void setDocModification(const DocModificationList& docMods);
+ /// Class documentation modifications
+ DocModificationList docModifications() const;
+ /// Function documentation modifications (matching signature)
+ DocModificationList functionDocModifications() const;
+
+ /// Extra includes for function arguments determined by the meta builder.
+ const IncludeList &argumentIncludes() const;
+ void addArgumentInclude(const Include &newInclude);
+
+ AddedFunctionList addedFunctions() const;
+ void setAddedFunctions(const AddedFunctionList &addedFunctions);
+ void addNewFunction(const AddedFunctionPtr &addedFunction);
+
+ const QList<TypeSystemPyMethodDefEntry> &addedPyMethodDefEntrys() const;
+ void addPyMethodDef(const TypeSystemPyMethodDefEntry &p);
+
+ // Functions specified in the "generate-functions" attribute
+ const QSet<QString> &generateFunctions() const;
+ void setGenerateFunctions(const QSet<QString> &f);
+
+ void setFieldModifications(const FieldModificationList &mods);
+ FieldModificationList fieldModifications() const;
+
+ const QList<TypeSystemProperty> &properties() const;
+ void addProperty(const TypeSystemProperty &p);
+
+ QString defaultSuperclass() const;
+ void setDefaultSuperclass(const QString &sc);
+
+ QString qualifiedCppName() const override;
+
+ void setIsPolymorphicBase(bool on);
+ bool isPolymorphicBase() const;
+
+ void setPolymorphicIdValue(const QString &value);
+ QString polymorphicIdValue() const;
+
+ QString polymorphicNameFunction() const;
+ void setPolymorphicNameFunction(const QString &n);
+
+ QString targetType() const;
+ void setTargetType(const QString &code);
+
+ bool isGenericClass() const;
+ void setGenericClass(bool isGeneric);
+
+ bool deleteInMainThread() const;
+ void setDeleteInMainThread(bool d);
+
+ CopyableFlag copyable() const;
+ void setCopyable(CopyableFlag flag);
+
+ TypeSystem::QtMetaTypeRegistration qtMetaTypeRegistration() const;
+ void setQtMetaTypeRegistration(TypeSystem::QtMetaTypeRegistration r);
+
+ QString hashFunction() const;
+ void setHashFunction(const QString &hashFunction);
+
+ void setBaseContainerType(const ComplexTypeEntryCPtr &baseContainer);
+
+ ComplexTypeEntryCPtr baseContainerType() const;
+
+ TypeSystem::ExceptionHandling exceptionHandling() const;
+ void setExceptionHandling(TypeSystem::ExceptionHandling e);
+
+ TypeSystem::AllowThread allowThread() const;
+ void setAllowThread(TypeSystem::AllowThread allowThread);
+
+ QString defaultConstructor() const;
+ void setDefaultConstructor(const QString& defaultConstructor);
+ bool hasDefaultConstructor() const;
+
+ TypeEntry *clone() const override;
+
+ void useAsTypedef(const ComplexTypeEntryCPtr &source);
+
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase sc);
+
+ // Determined by AbstractMetaBuilder from the code model.
+ bool isValueTypeWithCopyConstructorOnly() const;
+ void setValueTypeWithCopyConstructorOnly(bool v);
+
+ // FIXME PYSIDE 7: Remove this
+ static bool isParentManagementEnabled();
+ static void setParentManagementEnabled(bool e);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &debug) const override;
+#endif
+protected:
+ explicit ComplexTypeEntry(ComplexTypeEntryPrivate *d);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ComplexTypeEntry::TypeFlags)
+
+#endif // COMPLEXTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/conditionalstreamreader.cpp b/sources/shiboken6/ApiExtractor/conditionalstreamreader.cpp
new file mode 100644
index 000000000..b6eda651c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/conditionalstreamreader.cpp
@@ -0,0 +1,211 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "conditionalstreamreader.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QHash>
+
+using namespace Qt::StringLiterals;
+
+// ProxyEntityResolver proxies a QXmlStreamEntityResolver set by the user
+// on ConditionalStreamReader and stores entity definitions from the
+// <?entity name value?> processing instruction in a cache
+// (which is also used for the proxied resolver).
+class ProxyEntityResolver : public QXmlStreamEntityResolver
+{
+public:
+ QString resolveEntity(const QString& publicId,
+ const QString& systemId) override;
+ QString resolveUndeclaredEntity(const QString &name) override;
+
+ QXmlStreamEntityResolver *source() const { return m_source; }
+ void setSource(QXmlStreamEntityResolver *s) { m_source = s; }
+
+ void defineEntity(const QString &name, const QString &value)
+ {
+ m_undeclaredEntityCache.insert(name, value);
+ }
+
+private:
+ QHash<QString, QString> m_undeclaredEntityCache;
+ QXmlStreamEntityResolver *m_source = nullptr;
+};
+
+QString ProxyEntityResolver::resolveEntity(const QString &publicId, const QString &systemId)
+{
+ QString result;
+ if (m_source != nullptr)
+ result = m_source->resolveEntity(publicId, systemId);
+ if (result.isEmpty())
+ result = QXmlStreamEntityResolver::resolveEntity(publicId, systemId);
+ return result;
+}
+
+QString ProxyEntityResolver::resolveUndeclaredEntity(const QString &name)
+{
+ const auto it = m_undeclaredEntityCache.constFind(name);
+ if (it != m_undeclaredEntityCache.constEnd())
+ return it.value();
+ if (m_source == nullptr)
+ return {};
+ const QString result = m_source->resolveUndeclaredEntity(name);
+ if (!result.isEmpty())
+ defineEntity(name, result);
+ return result;
+}
+
+ConditionalStreamReader::ConditionalStreamReader(QIODevice *iod) :
+ m_reader(iod)
+{
+ init();
+}
+
+ConditionalStreamReader::ConditionalStreamReader(const QString &s) :
+ m_reader(s)
+{
+ init();
+}
+
+void ConditionalStreamReader::init()
+{
+ m_proxyEntityResolver = new ProxyEntityResolver;
+ m_reader.setEntityResolver(m_proxyEntityResolver);
+}
+
+ConditionalStreamReader::~ConditionalStreamReader()
+{
+ m_reader.setEntityResolver(nullptr);
+ delete m_proxyEntityResolver;
+}
+
+void ConditionalStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver)
+{
+ m_proxyEntityResolver->setSource(resolver);
+}
+
+QXmlStreamEntityResolver *ConditionalStreamReader::entityResolver() const
+{
+ return m_proxyEntityResolver->source();
+}
+
+QXmlStreamReader::TokenType ConditionalStreamReader::readNext()
+{
+ auto exToken = readNextInternal();
+
+ if (exToken.second == PiTokens::EntityDefinition)
+ return readEntityDefinitonPi() ? exToken.first : QXmlStreamReader::Invalid;
+
+ if (exToken.second != PiTokens::If || conditionMatches())
+ return exToken.first;
+
+ // Condition does not match - search for endif
+ int nestingLevel = 1;
+ while (true) {
+ exToken = readNextInternal();
+ if (exToken.first == QXmlStreamReader::NoToken
+ || exToken.first == QXmlStreamReader::Invalid
+ || exToken.first == QXmlStreamReader::EndDocument) {
+ break;
+ }
+
+ if (exToken.second == PiTokens::If)
+ ++nestingLevel;
+ else if (exToken.second == PiTokens::Endif && --nestingLevel == 0)
+ break;
+ }
+ return exToken.first;
+}
+
+void ConditionalStreamReader::defineEntity(const QString &name, const QString &value)
+{
+ m_proxyEntityResolver->defineEntity(name, value);
+}
+
+// Read an entity definition: "<?entity name value?>:
+bool ConditionalStreamReader::readEntityDefinitonPi()
+{
+ const auto data = m_reader.processingInstructionData();
+ const auto separator = data.indexOf(u' ');
+ if (separator <= 0 || separator == data.size() - 1) {
+ m_reader.raiseError(u"Malformed entity definition: "_s + data.toString());
+ return false;
+ }
+ defineEntity(data.left(separator).toString(),
+ data.right(data.size() - separator - 1).toString());
+ return true;
+}
+
+bool ConditionalStreamReader::conditionMatches() const
+{
+ const auto keywords = m_reader.processingInstructionData().split(u' ', Qt::SkipEmptyParts);
+ if (keywords.isEmpty())
+ return false;
+
+ bool matches = false;
+ bool exclusionOnly = true;
+ for (const auto &keyword : keywords) {
+ if (keyword.startsWith(u'!')) { // exclusion '!windows' takes preference
+ if (m_conditions.contains(keyword.mid(1)))
+ return false;
+ } else {
+ exclusionOnly = false;
+ matches |= m_conditions.contains(keyword);
+ }
+ }
+ return exclusionOnly || matches;
+}
+
+void ConditionalStreamReader::setConditions(const QStringList &newConditions)
+{
+ m_conditions = newConditions + platformConditions();
+}
+
+QStringList ConditionalStreamReader::platformConditions()
+{
+ QStringList result;
+#if defined (Q_OS_UNIX)
+ result << "unix"_L1;
+#endif
+
+#if defined (Q_OS_LINUX)
+ result << "linux"_L1;
+#elif defined (Q_OS_MACOS)
+ result << "darwin"_L1;
+#elif defined (Q_OS_WINDOWS)
+ result << "windows"_L1;
+#endif
+ return result;
+}
+
+ConditionalStreamReader::ExtendedToken ConditionalStreamReader::readNextInternal()
+{
+ const auto token = m_reader.readNext();
+ PiTokens piToken = PiTokens::None;
+ if (token == QXmlStreamReader::ProcessingInstruction) {
+ const auto target = m_reader.processingInstructionTarget();
+ if (target == u"if")
+ piToken = PiTokens::If;
+ else if (target == u"endif")
+ piToken = PiTokens::Endif;
+ else if (target == u"entity")
+ piToken = PiTokens::EntityDefinition;
+ }
+ return {token, piToken};
+}
+
+QDebug operator<<(QDebug dbg, const QXmlStreamAttributes &attrs)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.noquote();
+ dbg.nospace();
+ dbg << "QXmlStreamAttributes(";
+ for (qsizetype i = 0, size = attrs.size(); i < size; ++i ) {
+ if (i)
+ dbg << ", ";
+ dbg << attrs.at(i).name() << "=\"" << attrs.at(i).value() << '"';
+ }
+ dbg << ')';
+ return dbg;
+}
+
diff --git a/sources/shiboken6/ApiExtractor/conditionalstreamreader.h b/sources/shiboken6/ApiExtractor/conditionalstreamreader.h
new file mode 100644
index 000000000..730697525
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/conditionalstreamreader.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONDITIONALSTREAMREADER_H
+#define CONDITIONALSTREAMREADER_H
+
+#include <QtCore/QXmlStreamReader>
+
+#include <utility>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class ProxyEntityResolver;
+
+/// ConditionalStreamReader encapsulates QXmlStreamReader, offering the same
+/// API (except readNextStartElement() and similar conveniences) and internally
+/// uses Processing Instructions like:
+/// <?if keyword1 !keyword2?> ... <?endif?>
+/// to exclude/include sections depending on a list of condition keywords,
+/// containing for example the OS.
+/// It should be possible to use it as a drop-in replacement for
+/// QXmlStreamReader for any parsing code based on readNext().
+/// It also allows for specifying entities using a Processing Instruction:
+/// <?entity name value?>
+/// which can be used in conjunction with conditional processing.
+class ConditionalStreamReader
+{
+public:
+ Q_DISABLE_COPY_MOVE(ConditionalStreamReader)
+
+ using TokenType = QXmlStreamReader::TokenType;
+ explicit ConditionalStreamReader(QIODevice *iod);
+ explicit ConditionalStreamReader(const QString &s);
+ ~ConditionalStreamReader();
+
+ QIODevice *device() const { return m_reader.device(); }
+
+ // Note: Caching of entity values is done internally by
+ // ConditionalStreamReader.
+ void setEntityResolver(QXmlStreamEntityResolver *resolver);
+ QXmlStreamEntityResolver *entityResolver() const;
+
+ bool atEnd() const { return m_reader.atEnd(); }
+ TokenType readNext();
+
+ TokenType tokenType() const { return m_reader.tokenType(); }
+
+ qint64 lineNumber() const { return m_reader.lineNumber(); }
+ qint64 columnNumber() const { return m_reader.columnNumber(); }
+
+ QXmlStreamAttributes attributes() const { return m_reader.attributes(); }
+
+ QString readElementText(QXmlStreamReader::ReadElementTextBehaviour behaviour = QXmlStreamReader::ErrorOnUnexpectedElement)
+ { return m_reader.readElementText(behaviour); }
+
+ QStringView name() const { return m_reader.name(); }
+ QStringView qualifiedName() const { return m_reader.qualifiedName(); }
+
+ QStringView text() const { return m_reader.text(); }
+
+ QString errorString() const { return m_reader.errorString(); }
+ QXmlStreamReader::Error error() const { return m_reader.error(); }
+
+ bool hasError() const { return m_reader.hasError(); }
+
+ // Additional functions (not delegating to QXmlStreamReader)
+ void defineEntity(const QString &name, const QString &value);
+
+ const QStringList &conditions() const { return m_conditions; }
+ void setConditions(const QStringList &newConditions);
+
+ static QStringList platformConditions();
+
+private:
+ enum class PiTokens { None, If, Endif, EntityDefinition };
+
+ using ExtendedToken = std::pair<TokenType, PiTokens>;
+ ExtendedToken readNextInternal();
+ void init();
+ bool conditionMatches() const;
+ bool readEntityDefinitonPi();
+
+ QXmlStreamReader m_reader;
+ ProxyEntityResolver *m_proxyEntityResolver = nullptr;
+ QStringList m_conditions = ConditionalStreamReader::platformConditions();
+};
+
+QDebug operator<<(QDebug dbg, const QXmlStreamAttributes &a);
+
+#endif // CONDITIONALSTREAMREADER_H
diff --git a/sources/shiboken6/ApiExtractor/configurabletypeentry.h b/sources/shiboken6/ApiExtractor/configurabletypeentry.h
new file mode 100644
index 000000000..59522e16c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/configurabletypeentry.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONFIGURABLETYPEENTRY_H
+#define CONFIGURABLETYPEENTRY_H
+
+#include "typesystem.h"
+
+class ConfigurableTypeEntryPrivate;
+
+class ConfigurableTypeEntry : public TypeEntry
+{
+public:
+ explicit ConfigurableTypeEntry(const QString &entryName, Type t,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeEntry *clone() const override;
+
+ QString configCondition() const;
+ void setConfigCondition(const QString &c);
+ bool hasConfigCondition() const;
+
+protected:
+ explicit ConfigurableTypeEntry(ConfigurableTypeEntryPrivate *d);
+};
+
+#endif // CONFIGURABLETYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/constantvaluetypeentry.h b/sources/shiboken6/ApiExtractor/constantvaluetypeentry.h
new file mode 100644
index 000000000..a16a7ad12
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/constantvaluetypeentry.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONSTANTVALUETYPEENTRY_H
+#define CONSTANTVALUETYPEENTRY_H
+
+#include "typesystem.h"
+
+// For primitive values, typically to provide a dummy type for
+// example the '2' in non-type template 'Array<2>'.
+class ConstantValueTypeEntry : public TypeEntry
+{
+public:
+ explicit ConstantValueTypeEntry(const QString& name,
+ const TypeEntryCPtr &parent);
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit ConstantValueTypeEntry(TypeEntryPrivate *d);
+};
+
+#endif // CONSTANTVALUETYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/containertypeentry.h b/sources/shiboken6/ApiExtractor/containertypeentry.h
new file mode 100644
index 000000000..b2003816b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/containertypeentry.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONTAINERTYPEENTRY_H
+#define CONTAINERTYPEENTRY_H
+
+#include "complextypeentry.h"
+#include "customconversion_typedefs.h"
+
+class ContainerTypeEntryPrivate;
+
+struct OpaqueContainer // Generate an opaque container for an instantiation under name
+{
+ QStringList instantiations;
+ QString name;
+
+ QString templateParameters() const;
+};
+
+using OpaqueContainers = QList<OpaqueContainer>;
+
+class ContainerTypeEntry : public ComplexTypeEntry
+{
+public:
+
+ enum ContainerKind {
+ ListContainer,
+ SetContainer,
+ MapContainer,
+ MultiMapContainer,
+ PairContainer,
+ SpanContainer, // Fixed size
+ };
+
+ explicit ContainerTypeEntry(const QString &entryName, ContainerKind containerKind,
+ const QVersionNumber &vr, const TypeEntryCPtr &parent);
+
+ ContainerKind containerKind() const;
+
+ /// Number of template parameters (except allocators)
+ qsizetype templateParameterCount() const;
+
+ const OpaqueContainers &opaqueContainers() const;
+ void appendOpaqueContainers(const OpaqueContainers &l);
+ bool generateOpaqueContainer(const QStringList &instantiations) const;
+ QString opaqueContainerName(const QStringList &instantiations) const;
+
+ bool hasCustomConversion() const;
+ void setCustomConversion(const CustomConversionPtr &customConversion);
+ CustomConversionPtr customConversion() const;
+
+ TypeEntry *clone() const override;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+protected:
+ explicit ContainerTypeEntry(ContainerTypeEntryPrivate *d);
+};
+
+QDebug operator<<(QDebug d, const OpaqueContainer &oc);
+
+#endif // CONTAINERTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/customconversion.cpp b/sources/shiboken6/ApiExtractor/customconversion.cpp
new file mode 100644
index 000000000..4cfd1b974
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/customconversion.cpp
@@ -0,0 +1,197 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "customconversion.h"
+#include "containertypeentry.h"
+#include "customtypenentry.h"
+#include "primitivetypeentry.h"
+#include "valuetypeentry.h"
+
+#include <QtCore/qdebug.h>
+
+using namespace Qt::StringLiterals;
+
+CustomConversion::CustomConversion(const TypeEntryCPtr &ownerType) :
+ m_ownerType(ownerType)
+{
+}
+
+TypeEntryCPtr CustomConversion::ownerType() const
+{
+ return m_ownerType;
+}
+
+QString CustomConversion::nativeToTargetConversion() const
+{
+ return m_nativeToTargetConversion;
+}
+
+void CustomConversion::setNativeToTargetConversion(const QString &nativeToTargetConversion)
+{
+ m_nativeToTargetConversion = nativeToTargetConversion;
+}
+
+bool CustomConversion::replaceOriginalTargetToNativeConversions() const
+{
+ return m_replaceOriginalTargetToNativeConversions;
+}
+
+void CustomConversion::setReplaceOriginalTargetToNativeConversions(bool r)
+{
+ m_replaceOriginalTargetToNativeConversions = r;
+}
+
+bool CustomConversion::hasTargetToNativeConversions() const
+{
+ return !(m_targetToNativeConversions.isEmpty());
+}
+
+TargetToNativeConversions &CustomConversion::targetToNativeConversions()
+{
+ return m_targetToNativeConversions;
+}
+
+const TargetToNativeConversions &CustomConversion::targetToNativeConversions() const
+{
+ return m_targetToNativeConversions;
+}
+
+void CustomConversion::addTargetToNativeConversion(const QString &sourceTypeName,
+ const QString &sourceTypeCheck,
+ const QString &conversion)
+{
+ m_targetToNativeConversions.append(TargetToNativeConversion(sourceTypeName,
+ sourceTypeCheck,
+ conversion));
+}
+
+TargetToNativeConversion::TargetToNativeConversion(const QString &sourceTypeName,
+ const QString &sourceTypeCheck,
+ const QString &conversion) :
+ m_sourceTypeName(sourceTypeName), m_sourceTypeCheck(sourceTypeCheck),
+ m_conversion(conversion)
+{
+}
+
+TypeEntryCPtr TargetToNativeConversion::sourceType() const
+{
+ return m_sourceType;
+}
+
+void TargetToNativeConversion::setSourceType(const TypeEntryCPtr &sourceType)
+{
+ m_sourceType = sourceType;
+}
+
+bool TargetToNativeConversion::isCustomType() const
+{
+ return m_sourceType == nullptr;
+}
+
+QString TargetToNativeConversion::sourceTypeName() const
+{
+ return m_sourceTypeName;
+}
+
+QString TargetToNativeConversion::sourceTypeCheck() const
+{
+ if (!m_sourceTypeCheck.isEmpty())
+ return m_sourceTypeCheck;
+
+ if (m_sourceType != nullptr && m_sourceType->isCustom()) {
+ const auto cte = std::static_pointer_cast<const CustomTypeEntry>(m_sourceType);
+ if (cte->hasCheckFunction()) {
+ QString result = cte->checkFunction();
+ if (result != u"true") // For PyObject, which is always true
+ result += u"(%in)"_s;
+ return result;
+ }
+ }
+
+ return {};
+}
+
+QString TargetToNativeConversion::conversion() const
+{
+ return m_conversion;
+}
+
+void TargetToNativeConversion::setConversion(const QString &conversion)
+{
+ m_conversion = conversion;
+}
+
+void TargetToNativeConversion::formatDebug(QDebug &debug) const
+{
+ debug << "(source=\"" << m_sourceTypeName << '"';
+ if (debug.verbosity() > 2)
+ debug << ", conversion=\"" << m_conversion << '"';
+ if (isCustomType())
+ debug << ", [custom]";
+ debug << ')';
+}
+
+CustomConversionPtr CustomConversion::getCustomConversion(const TypeEntryCPtr &type)
+{
+ if (type->isPrimitive())
+ return std::static_pointer_cast<const PrimitiveTypeEntry>(type)->customConversion();
+ if (type->isContainer())
+ return std::static_pointer_cast<const ContainerTypeEntry>(type)->customConversion();
+ if (type->isValue())
+ return std::static_pointer_cast<const ValueTypeEntry>(type)->customConversion();
+ return {};
+}
+
+void CustomConversion::formatDebug(QDebug &debug) const
+{
+ debug << "(owner=\"" << m_ownerType->qualifiedCppName() << '"';
+ if (!m_nativeToTargetConversion.isEmpty())
+ debug << ", nativeToTargetConversion=\"" << m_nativeToTargetConversion << '"';
+ if (!m_targetToNativeConversions.isEmpty()) {
+ debug << ", targetToNativeConversions=[";
+ for (qsizetype i = 0, size = m_targetToNativeConversions.size(); i < size; ++i) {
+ if (i)
+ debug << ", ";
+ debug << m_targetToNativeConversions.at(i);
+
+ }
+ debug << ']';
+ }
+ if (m_replaceOriginalTargetToNativeConversions)
+ debug << ", [replaceOriginalTargetToNativeConversions]";
+ debug << ')';
+}
+
+QDebug operator<<(QDebug debug, const TargetToNativeConversion &t)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "TargetToNativeConversion";
+ t.formatDebug(debug);
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const CustomConversion &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "CustomConversion";
+ c.formatDebug(debug);
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const CustomConversionPtr &cptr)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "CustomConversionPtr";
+ if (auto *c = cptr.get()) {
+ c->formatDebug(debug);
+ } else {
+ debug << "(0)";
+ }
+ return debug;
+}
diff --git a/sources/shiboken6/ApiExtractor/customconversion.h b/sources/shiboken6/ApiExtractor/customconversion.h
new file mode 100644
index 000000000..fd0a67759
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/customconversion.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CUSTOMCONVERSION_H
+#define CUSTOMCONVERSION_H
+
+#include "customconversion_typedefs.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class TypeEntry;
+
+class TargetToNativeConversion
+{
+public:
+ explicit TargetToNativeConversion(const QString &sourceTypeName,
+ const QString &sourceTypeCheck,
+ const QString &conversion = {});
+
+ TypeEntryCPtr sourceType() const;
+ void setSourceType(const TypeEntryCPtr &sourceType);
+ bool isCustomType() const;
+ QString sourceTypeName() const;
+ QString sourceTypeCheck() const;
+ QString conversion() const;
+ void setConversion(const QString &conversion);
+
+ void formatDebug(QDebug &d) const;
+
+private:
+ TypeEntryCPtr m_sourceType;
+ QString m_sourceTypeName;
+ QString m_sourceTypeCheck;
+ QString m_conversion;
+};
+
+using TargetToNativeConversions = QList<TargetToNativeConversion>;
+
+class CustomConversion
+{
+public:
+ explicit CustomConversion(const TypeEntryCPtr &ownerType);
+
+ TypeEntryCPtr ownerType() const;
+ QString nativeToTargetConversion() const;
+ void setNativeToTargetConversion(const QString &nativeToTargetConversion);
+
+ /// Returns true if the target to C++ custom conversions should
+ /// replace the original existing ones, and false if the custom
+ /// conversions should be added to the original.
+ bool replaceOriginalTargetToNativeConversions() const;
+ void setReplaceOriginalTargetToNativeConversions(bool r);
+
+ bool hasTargetToNativeConversions() const;
+ TargetToNativeConversions &targetToNativeConversions();
+ const TargetToNativeConversions &targetToNativeConversions() const;
+ void addTargetToNativeConversion(const QString &sourceTypeName,
+ const QString &sourceTypeCheck,
+ const QString &conversion = QString());
+
+ /// Return the custom conversion of a type; helper for type system parser
+ static CustomConversionPtr getCustomConversion(const TypeEntryCPtr &type);
+
+ void formatDebug(QDebug &debug) const;
+
+private:
+ TypeEntryCPtr m_ownerType;
+ QString m_nativeToTargetConversion;
+ TargetToNativeConversions m_targetToNativeConversions;
+ bool m_replaceOriginalTargetToNativeConversions = false;
+};
+
+QDebug operator<<(QDebug debug, const TargetToNativeConversion &t);
+QDebug operator<<(QDebug debug, const CustomConversion &c);
+QDebug operator<<(QDebug debug, const CustomConversionPtr &cptr);
+
+#endif // CUSTOMCONVERSION_H
diff --git a/sources/shiboken6/ApiExtractor/customconversion_typedefs.h b/sources/shiboken6/ApiExtractor/customconversion_typedefs.h
new file mode 100644
index 000000000..6528f7d7b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/customconversion_typedefs.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CUSTOMCONVERSION_TYPEDEFS_H
+#define CUSTOMCONVERSION_TYPEDEFS_H
+
+#include <QtCore/QList>
+
+#include <memory>
+
+class CustomConversion;
+using CustomConversionPtr = std::shared_ptr<CustomConversion>;
+
+#endif // CUSTOMCONVERSION_TYPEDEFS_H
diff --git a/sources/shiboken6/ApiExtractor/customtypenentry.h b/sources/shiboken6/ApiExtractor/customtypenentry.h
new file mode 100644
index 000000000..a57bb858f
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/customtypenentry.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CUSTOMTYPENENTRY_H
+#define CUSTOMTYPENENTRY_H
+
+#include "typesystem.h"
+
+class CustomTypeEntry : public TypeEntry
+{
+public:
+ explicit CustomTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeEntry *clone() const override;
+
+ bool hasCheckFunction() const;
+ QString checkFunction() const;
+ void setCheckFunction(const QString &f);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+protected:
+ explicit CustomTypeEntry(TypeEntryPrivate *d);
+};
+
+
+#endif // CUSTOMTYPENENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/debughelpers_p.h b/sources/shiboken6/ApiExtractor/debughelpers_p.h
new file mode 100644
index 000000000..81ebbb3b9
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/debughelpers_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DEBUGHELPERS_P_H
+#define DEBUGHELPERS_P_H
+
+#include <QtCore/QDebug>
+#include <memory>
+
+template <class T>
+inline QDebug operator<<(QDebug debug, const std::shared_ptr<T> &ptr)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug << "std::shared_ptr(" << ptr.get() << ")";
+ return debug;
+}
+
+template <class It>
+inline void formatSequence(QDebug &d, It i1, It i2,
+ const char *separator=", ")
+{
+ for (It i = i1; i != i2; ++i) {
+ if (i != i1)
+ d << separator;
+ d << *i;
+ }
+}
+
+template <class It>
+inline static void formatPtrSequence(QDebug &d, It i1, It i2,
+ const char *separator=", ")
+{
+ for (It i = i1; i != i2; ++i) {
+ if (i != i1)
+ d << separator;
+ d << i->get();
+ }
+}
+
+template <class Container>
+static void formatList(QDebug &d, const char *name, const Container &c,
+ const char *separator=", ")
+{
+ if (const auto size = c.size()) {
+ d << ", " << name << '[' << size << "]=(";
+ for (qsizetype i = 0; i < size; ++i) {
+ if (i)
+ d << separator;
+ d << c.at(i);
+ }
+ d << ')';
+ }
+}
+
+#endif // DEBUGHELPERS_P_H
diff --git a/sources/shiboken6/ApiExtractor/dependency.h b/sources/shiboken6/ApiExtractor/dependency.h
new file mode 100644
index 000000000..aa280de03
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/dependency.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DEPENDENCY_H
+#define DEPENDENCY_H
+
+#include <QtCore/QList>
+
+#include <utility>
+
+// Dependencies for topologically sorting classes
+
+class AbstractMetaClass;
+
+struct Dependency {
+ AbstractMetaClassPtr parent;
+ AbstractMetaClassPtr child;
+};
+
+using Dependencies = QList<Dependency>;
+
+#endif // DEPENDENCY_H
diff --git a/sources/shiboken6/ApiExtractor/docparser.cpp b/sources/shiboken6/ApiExtractor/docparser.cpp
new file mode 100644
index 000000000..468fe1098
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/docparser.cpp
@@ -0,0 +1,232 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "docparser.h"
+#include "abstractmetaargument.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafunction.h"
+#include "abstractmetalang.h"
+#include "abstractmetatype.h"
+#include "messages.h"
+#include "modifications.h"
+#include "reporthandler.h"
+#include "enumtypeentry.h"
+#include "complextypeentry.h"
+#include "xmlutils.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+
+#include "qtcompat.h"
+
+#include <cstdlib>
+#ifdef HAVE_LIBXSLT
+# include <libxslt/xsltutils.h>
+# include <libxslt/transform.h>
+#endif
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+static inline bool isXpathDocModification(const DocModification &mod)
+{
+ return mod.mode() == TypeSystem::DocModificationXPathReplace;
+}
+
+static inline bool isNotXpathDocModification(const DocModification &mod)
+{
+ return mod.mode() != TypeSystem::DocModificationXPathReplace;
+}
+
+static void removeXpathDocModifications(DocModificationList *l)
+{
+ l->erase(std::remove_if(l->begin(), l->end(), isXpathDocModification), l->end());
+}
+
+static void removeNonXpathDocModifications(DocModificationList *l)
+{
+ l->erase(std::remove_if(l->begin(), l->end(), isNotXpathDocModification), l->end());
+}
+
+DocParser::DocParser() = default;
+DocParser::~DocParser() = default;
+
+void DocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &)
+{
+}
+
+void DocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &)
+{
+}
+
+QString DocParser::getDocumentation(const XQueryPtr &xquery, const QString& query,
+ const DocModificationList& mods)
+{
+ QString doc = execXQuery(xquery, query);
+ return applyDocModifications(mods, doc.trimmed());
+}
+
+QString DocParser::execXQuery(const XQueryPtr &xquery, const QString& query)
+{
+ QString errorMessage;
+ const QString result = xquery->evaluate(query, &errorMessage);
+ if (!errorMessage.isEmpty())
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return result;
+}
+
+static bool usesRValueReference(const AbstractMetaArgument &a)
+{
+ return a.type().referenceType() == RValueReference;
+}
+
+bool DocParser::skipForQuery(const AbstractMetaFunctionCPtr &func)
+{
+ // Skip private functions and copies created by AbstractMetaClass::fixFunctions()
+ // Note: Functions inherited from templates will cause warnings about missing
+ // documentation, but they should at least be listed.
+ if (!func || func->isPrivate()
+ || func->attributes().testFlag(AbstractMetaFunction::AddedMethod)
+ || func->isModifiedRemoved()
+ || func->declaringClass() != func->ownerClass()
+ || func->isConversionOperator()) {
+ return true;
+ }
+ switch (func->functionType()) {
+ case AbstractMetaFunction::MoveConstructorFunction:
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveAssignmentOperatorFunction:
+ return true;
+ default:
+ break;
+ }
+
+ return std::any_of(func->arguments().cbegin(), func->arguments().cend(),
+ usesRValueReference);
+}
+
+DocModificationList DocParser::getDocModifications(const AbstractMetaClassCPtr &cppClass)
+
+{
+ auto result = cppClass->typeEntry()->docModifications();
+ removeXpathDocModifications(&result);
+ return result;
+}
+
+static void filterBySignature(const AbstractMetaFunctionCPtr &func, DocModificationList *l)
+{
+ if (!l->isEmpty()) {
+ const QString minimalSignature = func->minimalSignature();
+ const auto filter = [&minimalSignature](const DocModification &mod) {
+ return mod.signature() != minimalSignature;
+ };
+ l->erase(std::remove_if(l->begin(), l->end(), filter), l->end());
+ }
+}
+
+DocModificationList DocParser::getDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass)
+{
+ DocModificationList result;
+ if (func->isUserAdded()) {
+ result = func->addedFunctionDocModifications();
+ removeXpathDocModifications(&result);
+ } else if (cppClass != nullptr) {
+ result = cppClass->typeEntry()->functionDocModifications();
+ removeXpathDocModifications(&result);
+ filterBySignature(func, &result);
+ }
+ return result;
+}
+
+DocModificationList DocParser::getXpathDocModifications(const AbstractMetaClassCPtr &cppClass)
+{
+ auto result = cppClass->typeEntry()->docModifications();
+ removeNonXpathDocModifications(&result);
+ return result;
+}
+
+DocModificationList DocParser::getXpathDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass)
+{
+ DocModificationList result;
+ if (func->isUserAdded()) {
+ result = func->addedFunctionDocModifications();
+ removeNonXpathDocModifications(&result);
+ } else if (cppClass != nullptr) {
+ result = cppClass->typeEntry()->functionDocModifications();
+ removeNonXpathDocModifications(&result);
+ filterBySignature(func, &result);
+ }
+ return result;
+}
+
+QString DocParser::enumBaseClass(const AbstractMetaEnum &e)
+{
+ switch (e.typeEntry()->pythonEnumType()) {
+ case TypeSystem::PythonEnumType::IntEnum:
+ return u"IntEnum"_s;
+ case TypeSystem::PythonEnumType::Flag:
+ return u"Flag"_s;
+ case TypeSystem::PythonEnumType::IntFlag:
+ return u"IntFlag"_s;
+ default:
+ break;
+ }
+ return e.typeEntry()->flags() != nullptr ? u"Flag"_s : u"Enum"_s;
+}
+
+AbstractMetaFunctionCList DocParser::documentableFunctions(const AbstractMetaClassCPtr &metaClass)
+{
+ auto result = metaClass->functionsInTargetLang();
+ for (auto i = result.size() - 1; i >= 0; --i) {
+ if (DocParser::skipForQuery(result.at(i)) || result.at(i)->isUserAdded())
+ result.removeAt(i);
+ }
+ result.append(metaClass->cppSignalFunctions());
+ return result;
+}
+
+QString DocParser::applyDocModifications(const DocModificationList& xpathMods,
+ const QString& xml)
+{
+ const char xslPrefix[] =
+R"(<xsl:template match="/">
+ <xsl:apply-templates />
+</xsl:template>
+<xsl:template match="*">
+<xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+</xsl:copy>
+</xsl:template>
+)";
+
+ if (xpathMods.isEmpty() || xml.isEmpty())
+ return xml;
+
+ QString xsl = QLatin1StringView(xslPrefix);
+ for (const DocModification &mod : xpathMods) {
+ Q_ASSERT(isXpathDocModification(mod));
+ QString xpath = mod.xpath();
+ xpath.replace(u'"', u"&quot;"_s);
+ xsl += "<xsl:template match=\""_L1 + xpath + "\">"_L1
+ + mod.code() + "</xsl:template>\n"_L1;
+ }
+
+ QString errorMessage;
+ const QString result = xsl_transform(xml, xsl, &errorMessage);
+ if (!errorMessage.isEmpty())
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgXpathDocModificationError(xpathMods, errorMessage)));
+ if (result == xml) {
+ const QString message = u"Query did not result in any modifications to \""_s
+ + xml + u'"';
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgXpathDocModificationError(xpathMods, message)));
+ }
+ return result;
+}
diff --git a/sources/shiboken6/ApiExtractor/docparser.h b/sources/shiboken6/ApiExtractor/docparser.h
new file mode 100644
index 000000000..6d458b25a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/docparser.h
@@ -0,0 +1,125 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef DOCPARSER_H
+#define DOCPARSER_H
+
+#include "abstractmetalang_typedefs.h"
+#include "modifications_typedefs.h"
+
+#include <QtCore/QString>
+
+#include <memory>
+
+class AbstractMetaClass;
+class DocModification;
+class Documentation;
+
+class XQuery;
+
+struct FunctionDocumentation;
+
+class DocParser
+{
+public:
+ Q_DISABLE_COPY_MOVE(DocParser)
+
+ using XQueryPtr = std::shared_ptr<XQuery>;
+
+ DocParser();
+ virtual ~DocParser();
+ virtual void fillDocumentation(const AbstractMetaClassPtr &metaClass) = 0;
+ virtual void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f);
+ virtual void fillGlobalEnumDocumentation(AbstractMetaEnum &e);
+
+ /**
+ * Process and retrieves documentation concerning the entire
+ * module or library.
+ * \return object containing module/library documentation information
+ */
+ virtual Documentation retrieveModuleDocumentation() = 0;
+
+ void setDocumentationDataDirectory(const QString& dir)
+ {
+ m_docDataDir = dir;
+ }
+
+ /**
+ * Informs the location of the XML data generated by the tool
+ * (e.g.: DoxyGen, qdoc) used to extract the library's documentation
+ * comment.
+ * \return the path for the directory containing the XML data created
+ * from the library's documentation beign parsed.
+ */
+ QString documentationDataDirectory() const
+ {
+ return m_docDataDir;
+ }
+
+ void setLibrarySourceDirectory(const QString& dir)
+ {
+ m_libSourceDir = dir;
+ }
+ /**
+ * Informs the location of the library being parsed. The library
+ * source code is parsed for the documentation comments.
+ * \return the path for the directory containing the source code of
+ * the library beign parsed.
+ */
+ QString librarySourceDirectory() const
+ {
+ return m_libSourceDir;
+ }
+
+ void setPackageName(const QString& packageName)
+ {
+ m_packageName = packageName;
+ }
+ /**
+ * Retrieves the name of the package (or module or library) being parsed.
+ * \return the name of the package (module/library) being parsed
+ */
+ QString packageName() const
+ {
+ return m_packageName;
+ }
+
+ /**
+ * Process and retrieves documentation concerning the entire
+ * module or library.
+ * \param name module name
+ * \return object containing module/library documentation information
+ * \todo Merge with retrieveModuleDocumentation() on next ABI change.
+ */
+ virtual Documentation retrieveModuleDocumentation(const QString& name) = 0;
+
+ static bool skipForQuery(const AbstractMetaFunctionCPtr &func);
+
+ /// Helper to return the documentation modifications for a class
+ /// or a member function.
+ static DocModificationList getDocModifications(const AbstractMetaClassCPtr &cppClass);
+ static DocModificationList getDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {});
+ static DocModificationList getXpathDocModifications(const AbstractMetaClassCPtr &cppClass);
+ static DocModificationList getXpathDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {});
+
+ static QString enumBaseClass(const AbstractMetaEnum &e);
+
+protected:
+ static QString getDocumentation(const XQueryPtr &xquery,
+ const QString &query,
+ const DocModificationList &mods);
+
+ static AbstractMetaFunctionCList documentableFunctions(const AbstractMetaClassCPtr &metaClass);
+
+ static QString applyDocModifications(const DocModificationList &xpathMods, const QString &xml);
+
+private:
+ QString m_packageName;
+ QString m_docDataDir;
+ QString m_libSourceDir;
+
+ static QString execXQuery(const XQueryPtr &xquery, const QString &query) ;
+};
+
+#endif // DOCPARSER_H
diff --git a/sources/shiboken6/ApiExtractor/documentation.cpp b/sources/shiboken6/ApiExtractor/documentation.cpp
new file mode 100644
index 000000000..33cf0e9fb
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/documentation.cpp
@@ -0,0 +1,65 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "documentation.h"
+
+#include <QtCore/QDebug>
+
+Documentation::Documentation(const QString &detailed,
+ const QString &brief,
+ Format fmt) :
+ m_detailed(detailed.trimmed()), m_brief(brief.trimmed()), m_format(fmt)
+{
+}
+
+bool Documentation::isEmpty() const
+{
+ return m_detailed.isEmpty() && m_brief.isEmpty();
+}
+
+Documentation::Format Documentation::format() const
+{
+ return m_format;
+}
+
+void Documentation::setValue(const QString &value, Documentation::Type t)
+{
+ if (t == Brief)
+ setBrief(value);
+ else
+ setDetailed(value);
+}
+
+void Documentation::setFormat(Documentation::Format f)
+{
+ m_format = f;
+}
+
+void Documentation::setDetailed(const QString &detailed)
+{
+ m_detailed = detailed.trimmed();
+}
+
+void Documentation::setBrief(const QString &brief)
+{
+ m_brief = brief.trimmed();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const Documentation &d)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Documentation(";
+ if (!d.isEmpty()) {
+ debug << "format=" << d.format();
+ if (!d.brief().isEmpty())
+ debug << ", brief=\"" << d.brief() << '"';
+ if (!d.detailed().isEmpty())
+ debug << ", detailed=\"" << d.detailed() << '"';
+ }
+ debug << ')';
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/documentation.h b/sources/shiboken6/ApiExtractor/documentation.h
new file mode 100644
index 000000000..df9d5d614
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/documentation.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DOCUMENTATION_H
+#define DOCUMENTATION_H
+
+#include <QtCore/QString>
+#include <QtCore/QtCompare>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class Documentation
+{
+public:
+ enum Format {
+ Native, // XML
+ Target // RST
+ };
+
+ enum Type {
+ Detailed,
+ Brief,
+ Last
+ };
+
+ Documentation() = default;
+ explicit Documentation(const QString &detailed,
+ const QString &brief,
+ Format fmt = Documentation::Native);
+
+ bool isEmpty() const;
+
+ void setValue(const QString& value, Type t = Documentation::Detailed);
+
+ Documentation::Format format() const;
+ void setFormat(Format f);
+
+ bool equals(const Documentation &rhs) const;
+
+ const QString &detailed() const { return m_detailed; }
+ void setDetailed(const QString &detailed);
+
+ bool hasBrief() const { return !m_brief.isEmpty(); }
+ const QString &brief() const { return m_brief; }
+ void setBrief(const QString &brief);
+
+private:
+ friend bool comparesEqual(const Documentation &lhs,
+ const Documentation &rhs) noexcept
+ {
+ return lhs.m_format == rhs.m_format && lhs.m_detailed == rhs.m_detailed
+ && lhs.m_brief == rhs.m_brief;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(Documentation)
+
+ QString m_detailed;
+ QString m_brief;
+ Format m_format = Documentation::Native;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const Documentation &);
+#endif
+
+#endif // DOCUMENTATION_H
diff --git a/sources/shiboken6/ApiExtractor/dotview.cpp b/sources/shiboken6/ApiExtractor/dotview.cpp
new file mode 100644
index 000000000..0bd192257
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/dotview.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "dotview.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QProcess>
+#include <QtCore/QTemporaryFile>
+
+using namespace Qt::StringLiterals;
+
+bool showDotGraph(const QString &name, const QString &graph)
+{
+ constexpr auto imageType = "jpg"_L1;
+
+ // Write out the graph to a temporary file
+ QTemporaryFile dotFile(QDir::tempPath() + u'/' + name + u"_XXXXXX.dot"_s);
+ if (!dotFile.open()) {
+ qWarning("Cannot open temporary file: %s", qPrintable(dotFile.errorString()));
+ return false;
+ }
+ const QString tempDotFile = dotFile.fileName();
+ dotFile.write(graph.toUtf8());
+ dotFile.close();
+
+ // Convert to image using "dot"
+ const QString imageFile = tempDotFile.left(tempDotFile.size() - 3) + imageType;
+ QProcess process;
+ process.start(u"dot"_s, {u"-T"_s + imageType, u"-o"_s + imageFile, tempDotFile});
+ if (!process.waitForStarted() || !process.waitForFinished()) {
+ qWarning("Image conversion failed: %s", qPrintable(process.errorString()));
+ return false;
+ }
+ if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) {
+ qWarning("Image conversion failed (%d): %s",
+ process.exitCode(),
+ process.readAllStandardError().constData());
+ return false;
+ }
+
+ // Launch image. Should use QDesktopServices::openUrl(),
+ // but we don't link against QtGui
+#ifdef Q_OS_UNIX
+ constexpr auto imageViewer = "gwenview"_L1;
+#else
+ constexpr auto imageViewer = "mspaint"_L1;
+#endif
+ if (!QProcess::startDetached(imageViewer, {imageFile})) {
+ qWarning("Failed to launch viewer: %s", qPrintable(imageViewer));
+ return false;
+ }
+ qInfo().noquote().nospace() << "Viewing: "
+ << QDir::toNativeSeparators(tempDotFile)
+ << ' ' << QDir::toNativeSeparators(imageFile);
+ return true;
+}
diff --git a/sources/shiboken6/ApiExtractor/dotview.h b/sources/shiboken6/ApiExtractor/dotview.h
new file mode 100644
index 000000000..87fb7db65
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/dotview.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DOTVIEW_H
+#define DOTVIEW_H
+
+#include <QtCore/QString>
+
+/// Show a dot digraph in an image viewer
+/// \param name base name for files
+/// \param graph graph
+bool showDotGraph(const QString &name, const QString &graph);
+
+#endif // DOTVIEW_H
diff --git a/sources/shiboken6/ApiExtractor/doxygenparser.cpp b/sources/shiboken6/ApiExtractor/doxygenparser.cpp
new file mode 100644
index 000000000..da790015f
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/doxygenparser.cpp
@@ -0,0 +1,224 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "doxygenparser.h"
+#include "abstractmetaargument.h"
+#include "abstractmetalang.h"
+#include "abstractmetafield.h"
+#include "abstractmetafunction.h"
+#include "abstractmetaenum.h"
+#include "abstractmetatype.h"
+#include "documentation.h"
+#include "messages.h"
+#include "modifications.h"
+#include "propertyspec.h"
+#include "reporthandler.h"
+#include "complextypeentry.h"
+#include "xmlutils.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+
+using namespace Qt::StringLiterals;
+
+static QString getSectionKindAttr(const AbstractMetaFunctionCPtr &func)
+{
+ if (func->isSignal())
+ return u"signal"_s;
+ QString kind = func->isPublic()
+ ? u"public"_s : u"protected"_s;
+ if (func->isStatic())
+ kind += u"-static"_s;
+ else if (func->isSlot())
+ kind += u"-slot"_s;
+ return kind;
+}
+
+Documentation DoxygenParser::retrieveModuleDocumentation()
+{
+ return retrieveModuleDocumentation(packageName());
+}
+
+void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
+{
+ if (!metaClass)
+ return;
+
+ QString doxyFileSuffix;
+ if (metaClass->enclosingClass()) {
+ doxyFileSuffix += metaClass->enclosingClass()->name();
+ doxyFileSuffix += u"_1_1"_s; // FIXME: Check why _1_1!!
+ }
+ doxyFileSuffix += metaClass->name();
+ doxyFileSuffix += u".xml"_s;
+
+ static constexpr QLatin1StringView prefixes[] = { "class"_L1, "struct"_L1, "namespace"_L1 };
+ bool isProperty = false;
+
+ QString doxyFilePath;
+ for (const auto &prefix : prefixes) {
+ doxyFilePath = documentationDataDirectory() + u'/' + prefix + doxyFileSuffix;
+ if (QFile::exists(doxyFilePath))
+ break;
+ doxyFilePath.clear();
+ }
+
+ if (doxyFilePath.isEmpty()) {
+ qCWarning(lcShibokenDoc).noquote().nospace()
+ << "Can't find doxygen file for class " << metaClass->name() << ", tried: "
+ << QDir::toNativeSeparators(documentationDataDirectory())
+ << "/{struct|class|namespace}"<< doxyFileSuffix;
+ return;
+ }
+
+ QString errorMessage;
+ XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage);
+ if (!xquery) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return;
+ }
+
+ static const QList<std::pair<Documentation::Type, QString>> docTags = {
+ { Documentation::Brief, u"briefdescription"_s },
+ { Documentation::Detailed, u"detaileddescription"_s }
+ };
+ // Get class documentation
+ Documentation classDoc;
+
+ for (const auto &tag : docTags) {
+ const QString classQuery = u"/doxygen/compounddef/"_s + tag.second;
+ QString doc = getDocumentation(xquery, classQuery,
+ metaClass->typeEntry()->docModifications());
+ if (doc.isEmpty())
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(doxyFilePath, "class", metaClass->name(),
+ classQuery)));
+ else
+ classDoc.setValue(doc, tag.first);
+ }
+ metaClass->setDocumentation(classDoc);
+
+ //Functions Documentation
+ const auto &funcs = DocParser::documentableFunctions(metaClass);
+ for (const auto &func : funcs) {
+ QString query = u"/doxygen/compounddef/sectiondef"_s;
+ // properties
+ if (func->isPropertyReader() || func->isPropertyWriter()
+ || func->isPropertyResetter()) {
+ const auto prop = metaClass->propertySpecs().at(func->propertySpecIndex());
+ query += u"[@kind=\"property\"]/memberdef/name[text()=\""_s
+ + prop.name() + u"\"]"_s;
+ isProperty = true;
+ } else { // normal methods
+ QString kind = getSectionKindAttr(func);
+ query += u"[@kind=\""_s + kind
+ + u"-func\"]/memberdef/name[text()=\""_s
+ + func->originalName() + u"\"]"_s;
+
+ if (func->arguments().isEmpty()) {
+ QString args = func->isConstant() ? u"() const"_s : u"()"_s;
+ query += u"/../argsstring[text()=\""_s + args + u"\"]"_s;
+ } else {
+ int i = 1;
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ if (!arg.type().isPrimitive()) {
+ query += u"/../param["_s + QString::number(i)
+ + u"]/type/ref[text()=\""_s
+ + arg.type().cppSignature().toHtmlEscaped()
+ + u"\"]/../.."_s;
+ } else {
+ query += u"/../param["_s + QString::number(i)
+ + u"]/type[text(), \""_s
+ + arg.type().cppSignature().toHtmlEscaped()
+ + u"\"]/.."_s;
+ }
+ ++i;
+ }
+ }
+ }
+ Documentation funcDoc;
+ for (const auto &tag : docTags) {
+ QString funcQuery(query);
+ if (!isProperty) {
+ funcQuery += u"/../"_s + tag.second;
+ } else {
+ funcQuery = u'(' + funcQuery;
+ funcQuery += u"/../"_s + tag.second + u")[1]"_s;
+ }
+
+ QString doc = getDocumentation(xquery, funcQuery,
+ DocParser::getXpathDocModifications(func, metaClass));
+ if (doc.isEmpty()) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(doxyFilePath, func.get(),
+ funcQuery)));
+ } else {
+ funcDoc.setValue(doc, tag.first);
+ }
+ }
+ std::const_pointer_cast<AbstractMetaFunction>(func)->setDocumentation(funcDoc);
+ isProperty = false;
+ }
+
+ //Fields
+ for (AbstractMetaField &field : metaClass->fields()) {
+ if (field.isPrivate())
+ return;
+
+ Documentation fieldDoc;
+ for (const auto &tag : docTags) {
+ QString query = u"/doxygen/compounddef/sectiondef/memberdef/name[text()=\""_s
+ + field.name() + u"\"]/../"_s + tag.second;
+ QString doc = getDocumentation(xquery, query, DocModificationList());
+ if (doc.isEmpty()) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, field,
+ query)));
+ } else {
+ fieldDoc.setValue(doc, tag.first);
+ }
+ }
+ field.setDocumentation(fieldDoc);
+ }
+
+ //Enums
+ for (AbstractMetaEnum &meta_enum : metaClass->enums()) {
+ QString query = u"/doxygen/compounddef/sectiondef/memberdef[@kind=\"enum\"]/name[text()=\""_s
+ + meta_enum.name() + u"\"]/.."_s;
+ QString doc = getDocumentation(xquery, query, DocModificationList());
+ if (doc.isEmpty()) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, meta_enum, query)));
+ }
+ meta_enum.setDocumentation(Documentation(doc, {}));
+ }
+
+}
+
+Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){
+
+ QString sourceFile = documentationDataDirectory() + u"/indexpage.xml"_s;
+
+ if (!QFile::exists(sourceFile)) {
+ qCWarning(lcShibokenDoc).noquote().nospace()
+ << "Can't find doxygen XML file for module " << name << ", tried: "
+ << QDir::toNativeSeparators(sourceFile);
+ return {};
+ }
+
+ QString errorMessage;
+ XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage);
+ if (!xquery) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return {};
+ }
+
+ // Module documentation
+ QString query = u"/doxygen/compounddef/detaileddescription"_s;
+ const QString doc = getDocumentation(xquery, query, DocModificationList());
+ return Documentation(doc, {});
+}
+
diff --git a/sources/shiboken6/ApiExtractor/doxygenparser.h b/sources/shiboken6/ApiExtractor/doxygenparser.h
new file mode 100644
index 000000000..4f6a9e53c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/doxygenparser.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DOXYGENPARSER_H
+#define DOXYGENPARSER_H
+
+#include "docparser.h"
+
+class DoxygenParser : public DocParser
+{
+public:
+ DoxygenParser() = default;
+ void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+ Documentation retrieveModuleDocumentation() override;
+ Documentation retrieveModuleDocumentation(const QString& name) override;
+};
+
+#endif // DOXYGENPARSER_H
diff --git a/sources/shiboken6/ApiExtractor/enclosingclassmixin.cpp b/sources/shiboken6/ApiExtractor/enclosingclassmixin.cpp
new file mode 100644
index 000000000..2421ae527
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/enclosingclassmixin.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "enclosingclassmixin.h"
+#include "abstractmetalang.h"
+#include "namespacetypeentry.h"
+
+AbstractMetaClassCPtr EnclosingClassMixin::targetLangEnclosingClass() const
+{
+ auto result = m_enclosingClass.lock();
+ while (result && !NamespaceTypeEntry::isVisibleScope(result->typeEntry()))
+ result = result->enclosingClass();
+ return result;
+}
diff --git a/sources/shiboken6/ApiExtractor/enclosingclassmixin.h b/sources/shiboken6/ApiExtractor/enclosingclassmixin.h
new file mode 100644
index 000000000..8d735d5ec
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/enclosingclassmixin.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ENCLOSINGCLASSMIXIN_H
+#define ENCLOSINGCLASSMIXIN_H
+
+#include "abstractmetalang_typedefs.h"
+
+class AbstractMetaClass;
+
+class EnclosingClassMixin {
+public:
+
+ const AbstractMetaClassCPtr enclosingClass() const
+ { return m_enclosingClass.lock(); }
+ void setEnclosingClass(const AbstractMetaClassCPtr &cls)
+ { m_enclosingClass = cls; }
+ AbstractMetaClassCPtr targetLangEnclosingClass() const;
+
+private:
+ std::weak_ptr<const AbstractMetaClass> m_enclosingClass;
+};
+
+#endif // ENCLOSINGCLASSMIXIN_H
diff --git a/sources/shiboken6/ApiExtractor/enumtypeentry.h b/sources/shiboken6/ApiExtractor/enumtypeentry.h
new file mode 100644
index 000000000..3360d7db5
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/enumtypeentry.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ENUMTYPEENTRY_H
+#define ENUMTYPEENTRY_H
+
+#include "configurabletypeentry.h"
+#include "typesystem_enums.h"
+
+class EnumTypeEntryPrivate;
+
+// EnumTypeEntry is configurable for global enums only
+class EnumTypeEntry : public ConfigurableTypeEntry
+{
+public:
+ explicit EnumTypeEntry(const QString &entryName,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeSystem::PythonEnumType pythonEnumType() const;
+ void setPythonEnumType(TypeSystem::PythonEnumType t);
+
+ QString targetLangQualifier() const;
+
+ QString qualifier() const;
+
+ EnumValueTypeEntryCPtr nullValue() const;
+ void setNullValue(const EnumValueTypeEntryCPtr &n);
+
+ void setFlags(const FlagsTypeEntryPtr &flags);
+ FlagsTypeEntryPtr flags() const;
+
+ QString cppType() const;
+ void setCppType(const QString &t);
+
+ bool isEnumValueRejected(const QString &name) const;
+ void addEnumValueRejection(const QString &name);
+ QStringList enumValueRejections() const;
+
+ QString docFile() const;
+ void setDocFile(const QString &df);
+
+ TypeEntry *clone() const override;
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+protected:
+ explicit EnumTypeEntry(EnumTypeEntryPrivate *d);
+};
+
+#endif // ENUMTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/enumvaluetypeentry.h b/sources/shiboken6/ApiExtractor/enumvaluetypeentry.h
new file mode 100644
index 000000000..006b84e0a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/enumvaluetypeentry.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ENUMVALUETYPEENTRY_H
+#define ENUMVALUETYPEENTRY_H
+
+#include "typesystem.h"
+
+class EnumTypeEntry;
+class EnumValueTypeEntryPrivate;
+
+// EnumValueTypeEntry is used for resolving integer type templates
+// like array<EnumValue>. Note: Dummy entries for integer values will
+// be created for non-type template parameters, where m_enclosingEnum==nullptr.
+class EnumValueTypeEntry : public TypeEntry
+{
+public:
+ explicit EnumValueTypeEntry(const QString& name, const QString& value,
+ const EnumTypeEntryCPtr &enclosingEnum,
+ bool isScopedEnum, const QVersionNumber &vr);
+
+ QString value() const;
+ EnumTypeEntryCPtr enclosingEnum() const;
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit EnumValueTypeEntry(EnumValueTypeEntryPrivate *d);
+};
+
+#endif // ENUMVALUETYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/exception.h b/sources/shiboken6/ApiExtractor/exception.h
new file mode 100644
index 000000000..396b56f5d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/exception.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef EXCEPTION_H
+#define EXCEPTION_H
+
+#include <QtCore/QString>
+
+#include <string>
+#include <exception>
+
+class Exception : public std::exception
+{
+public:
+ explicit Exception(const QString &message) : m_message(message.toUtf8()) {}
+ explicit Exception(const char *message) : m_message(message) {}
+
+ const char *what() const noexcept override
+ {
+ return m_message.c_str();
+ }
+
+private:
+ const std::string m_message;
+};
+
+#endif // EXCEPTION
diff --git a/sources/shiboken6/ApiExtractor/fileout.cpp b/sources/shiboken6/ApiExtractor/fileout.cpp
new file mode 100644
index 000000000..6f9ec4d8a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/fileout.cpp
@@ -0,0 +1,198 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "fileout.h"
+#include "messages.h"
+#include "reporthandler.h"
+#include "exception.h"
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+
+#include <cstdio>
+
+bool FileOut::m_dryRun = false;
+bool FileOut::m_diff = false;
+
+#ifdef Q_OS_LINUX
+static const char colorDelete[] = "\033[31m";
+static const char colorAdd[] = "\033[32m";
+static const char colorInfo[] = "\033[36m";
+static const char colorReset[] = "\033[0m";
+#else
+static const char colorDelete[] = "";
+static const char colorAdd[] = "";
+static const char colorInfo[] = "";
+static const char colorReset[] = "";
+#endif
+
+FileOut::FileOut(QString n) :
+ stream(&m_buffer),
+ m_name(std::move(n)),
+ m_isDone(false)
+{
+}
+
+FileOut::~FileOut()
+{
+ if (!m_isDone) {
+ qCWarning(lcShiboken).noquote().nospace() << __FUNCTION__
+ << " file " << m_name << " not written.";
+ }
+}
+
+static QList<qsizetype> lcsLength(const QByteArrayList &a, const QByteArrayList &b)
+{
+ const auto height = a.size() + 1;
+ const auto width = b.size() + 1;
+
+ QList<qsizetype> res(width * height, 0);
+
+ for (qsizetype row = 1; row < height; row++) {
+ for (qsizetype col = 1; col < width; col++) {
+ if (a.at(row - 1) == b.at(col - 1))
+ res[width * row + col] = res[width * (row - 1) + col - 1] + 1;
+ else
+ res[width * row + col] = qMax(res[width * row + col - 1],
+ res[width * (row - 1) + col]);
+ }
+ }
+ return res;
+}
+
+enum Type {
+ Add,
+ Delete,
+ Unchanged
+};
+
+struct Unit
+{
+ Type type;
+ qsizetype start;
+ qsizetype end;
+
+ void print(const QByteArrayList &a, const QByteArrayList &b) const;
+};
+
+void Unit::print(const QByteArrayList &a, const QByteArrayList &b) const
+{
+ switch (type) {
+ case Unchanged:
+ if ((end - start) > 9) {
+ for (auto i = start; i <= start + 2; ++i)
+ std::printf(" %s\n", a.at(i).constData());
+ std::printf("%s=\n= %d more lines\n=%s\n",
+ colorInfo, int(end - start - 6), colorReset);
+ for (auto i = end - 2; i <= end; ++i)
+ std::printf(" %s\n", a.at(i).constData());
+ } else {
+ for (auto i = start; i <= end; ++i)
+ std::printf(" %s\n", a.at(i).constData());
+ }
+ break;
+ case Add:
+ std::fputs(colorAdd, stdout);
+ for (auto i = start; i <= end; ++i)
+ std::printf("+ %s\n", b.at(i).constData());
+ std::fputs(colorReset, stdout);
+ break;
+ case Delete:
+ std::fputs(colorDelete, stdout);
+ for (auto i = start; i <= end; ++i)
+ std::printf("- %s\n", a.at(i).constData());
+ std::fputs(colorReset, stdout);
+ break;
+ }
+}
+
+static void unitAppend(Type type, qsizetype pos, QList<Unit> *units)
+{
+ if (!units->isEmpty() && units->last().type == type)
+ units->last().end = pos;
+ else
+ units->append(Unit{type, pos, pos});
+}
+
+static QList<Unit> diffHelper(const QList<qsizetype> &lcs,
+ const QByteArrayList &a, const QByteArrayList &b,
+ qsizetype row, qsizetype col)
+{
+ if (row > 0 && col > 0 && a.at(row - 1) == b.at(col - 1)) {
+ QList<Unit> result = diffHelper(lcs, a, b, row - 1, col - 1);
+ unitAppend(Unchanged, row - 1, &result);
+ return result;
+ }
+
+ const auto width = b.size() + 1;
+ if (col > 0
+ && (row == 0 || lcs.at(width * row + col -1 ) >= lcs.at(width * (row - 1) + col))) {
+ QList<Unit> result = diffHelper(lcs, a, b, row, col - 1);
+ unitAppend(Add, col - 1, &result);
+ return result;
+ }
+ if (row > 0
+ && (col == 0 || lcs.at(width * row + col-1) < lcs.at(width * (row - 1) + col))) {
+ QList<Unit> result = diffHelper(lcs, a, b, row - 1, col);
+ unitAppend(Delete, row - 1, &result);
+ return result;
+ }
+ return {};
+}
+
+static void diff(const QByteArrayList &a, const QByteArrayList &b)
+{
+ const QList<Unit> res = diffHelper(lcsLength(a, b), a, b, a.size(), b.size());
+ for (const Unit &unit : res)
+ unit.print(a, b);
+}
+
+FileOut::State FileOut::done()
+{
+ if (m_isDone)
+ return Success;
+
+ bool fileEqual = false;
+ QFile fileRead(m_name);
+ QFileInfo info(fileRead);
+ stream.flush();
+ QByteArray original;
+ if (info.exists() && (m_diff || (info.size() == m_buffer.size()))) {
+ if (!fileRead.open(QIODevice::ReadOnly))
+ throw Exception(msgCannotOpenForReading(fileRead));
+
+ original = fileRead.readAll();
+ fileRead.close();
+ fileEqual = (original == m_buffer);
+ }
+
+ if (fileEqual) {
+ m_isDone = true;
+ return Unchanged;
+ }
+
+ if (!FileOut::m_dryRun) {
+ QDir dir(info.absolutePath());
+ if (!dir.mkpath(dir.absolutePath())) {
+ const QString message = QString::fromLatin1("Unable to create directory '%1'")
+ .arg(QDir::toNativeSeparators(dir.absolutePath()));
+ throw Exception(message);
+ }
+
+ QFile fileWrite(m_name);
+ if (!fileWrite.open(QIODevice::WriteOnly))
+ throw Exception(msgCannotOpenForWriting(fileWrite));
+ if (fileWrite.write(m_buffer) == -1 || !fileWrite.flush())
+ throw Exception(msgWriteFailed(fileWrite, m_buffer.size()));
+ }
+ if (m_diff) {
+ std::printf("%sFile: %s%s\n", colorInfo, qPrintable(m_name), colorReset);
+ ::diff(original.split('\n'), m_buffer.split('\n'));
+ std::printf("\n");
+ }
+
+ m_isDone = true;
+
+ return Success;
+}
diff --git a/sources/shiboken6/ApiExtractor/fileout.h b/sources/shiboken6/ApiExtractor/fileout.h
new file mode 100644
index 000000000..b11ad1e20
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/fileout.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef FILEOUT_H
+#define FILEOUT_H
+
+#include "textstream.h"
+
+class Exception;
+
+QT_FORWARD_DECLARE_CLASS(QFile)
+
+class FileOut
+{
+ QByteArray m_buffer;
+public:
+ Q_DISABLE_COPY_MOVE(FileOut)
+
+ enum State { Unchanged, Success };
+
+ explicit FileOut(QString name);
+ ~FileOut();
+
+ QString filePath() const { return m_name; }
+
+ State done() noexcept(false);
+
+ TextStream stream;
+
+ static bool diff() { return m_diff; }
+ static void setDiff(bool diff) { m_diff = diff; }
+
+ static bool dryRun() { return m_dryRun; }
+ static void setDryRun(bool dryRun) { m_dryRun = dryRun; }
+
+private:
+ QString m_name;
+ bool m_isDone;
+ static bool m_dryRun;
+ static bool m_diff;
+};
+
+#endif // FILEOUT_H
diff --git a/sources/shiboken6/ApiExtractor/flagstypeentry.h b/sources/shiboken6/ApiExtractor/flagstypeentry.h
new file mode 100644
index 000000000..6eddcd12b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/flagstypeentry.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef FLAGSTYPEENTRY_H
+#define FLAGSTYPEENTRY_H
+
+#include "typesystem.h"
+
+class EnumTypeEntry;
+class FlagsTypeEntryPrivate;
+
+// FlagsTypeEntry is configurable for global flags only
+class FlagsTypeEntry : public TypeEntry
+{
+public:
+ explicit FlagsTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ QString originalName() const;
+ void setOriginalName(const QString &s);
+
+ QString flagsName() const;
+ void setFlagsName(const QString &name);
+
+ EnumTypeEntryPtr originator() const;
+ void setOriginator(const EnumTypeEntryPtr &e);
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit FlagsTypeEntry(FlagsTypeEntryPrivate *d);
+
+ QString buildTargetLangName() const override;
+};
+
+#endif // FLAGSTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/functiontypeentry.h b/sources/shiboken6/ApiExtractor/functiontypeentry.h
new file mode 100644
index 000000000..53aa1fad6
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/functiontypeentry.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef FUNCTIONTYPEENTRY_H
+#define FUNCTIONTYPEENTRY_H
+
+#include "typesystem.h"
+
+class FunctionTypeEntryPrivate;
+
+class FunctionTypeEntry : public TypeEntry
+{
+public:
+ explicit FunctionTypeEntry(const QString& name, const QString& signature,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ const QStringList &signatures() const;
+ bool hasSignature(const QString& signature) const;
+ void addSignature(const QString& signature);
+
+ QString docFile() const;
+ void setDocFile(const QString &df);
+
+ TypeEntry *clone() const override;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+protected:
+ explicit FunctionTypeEntry(FunctionTypeEntryPrivate *d);
+};
+
+#endif // FUNCTIONTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/graph.h b/sources/shiboken6/ApiExtractor/graph.h
new file mode 100644
index 000000000..447a26da0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/graph.h
@@ -0,0 +1,321 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef GRAPH_H
+#define GRAPH_H
+
+#include "dotview.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QHash>
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+#include <algorithm>
+
+/// Result of topologically sorting of a graph (list of nodes in order
+/// or list of nodes that have cyclic dependencies).
+template <class Node>
+struct GraphSortResult
+{
+ using NodeList = QList<Node>;
+
+ bool isValid() const { return !result.isEmpty() && cyclic.isEmpty(); }
+ void format(QDebug &debug) const;
+
+ NodeList result;
+ NodeList cyclic;
+};
+
+/// A graph that can have its nodes topologically sorted. The nodes need to
+/// have operator==().
+template <class Node>
+class Graph
+{
+public:
+ using NodeList = QList<Node>;
+
+ Graph() = default;
+
+ // Construct from a sequence of nodes
+ template<class It>
+ explicit Graph(It i1, It i2) { setNodes(i1, i2); }
+
+ template<class It>
+ void setNodes(It i1, It i2)
+ {
+ for (; i1 != i2; ++i1)
+ addNode(*i1);
+ }
+
+ bool addNode(Node n);
+
+ /// Returns whether node was registered
+ bool hasNode(Node node) { return indexOfNode(node) != -1; }
+
+ /// Returns the numbed of nodes in this graph.
+ qsizetype nodeCount() const { return m_nodeEntries.size(); }
+
+ /// Returns true if the graph contains the edge from -> to
+ bool containsEdge(Node from, Node to) const;
+ /// Returns true if the graph has any edges
+ bool hasEdges() const;
+
+ /// Adds an edge to this graph.
+ bool addEdge(Node from, Node to);
+ /// Removes an edge from this graph.
+ bool removeEdge(Node from, Node to);
+ /// Clears the graph
+ void clear() { m_nodeEntries.clear(); }
+
+ /// Dumps a dot graph to a file named \p filename.
+ /// \param fileName file name where the output should be written.
+ /// \param f function returning the name of a node
+ template <class NameFunction>
+ bool dumpDot(const QString& fileName, NameFunction f) const;
+ template <class NameFunction>
+ void formatDot(QTextStream &str, NameFunction f) const;
+ template <class NameFunction>
+ bool showGraph(const QString &name, NameFunction f) const;
+
+ void format(QDebug &debug) const;
+
+ /**
+ * Topologically sort this graph.
+ * \return A collection with all nodes topologically sorted or an empty collection if a cyclic
+ * dependency was found.
+ */
+ GraphSortResult<Node> topologicalSort() const;
+
+private:
+ enum Color { WHITE, GRAY, BLACK };
+
+ struct NodeEntry
+ {
+ Node node;
+ NodeList targets;
+ mutable Color color;
+ };
+
+ Color colorAt(qsizetype i) const { return m_nodeEntries.at(i).color; }
+ qsizetype indexOfNode(Node n) const;
+ void depthFirstVisit(qsizetype i, NodeList &result) const;
+
+ QList<NodeEntry> m_nodeEntries;
+};
+
+template <class Node>
+qsizetype Graph<Node>::indexOfNode(Node n) const
+{
+ for (qsizetype i = 0, size = m_nodeEntries.size(); i < size; ++i) {
+ if (m_nodeEntries.at(i).node == n)
+ return i;
+ }
+ return -1;
+}
+
+template <class Node>
+bool Graph<Node>::addNode(Node n)
+{
+ if (hasNode(n))
+ return false;
+ m_nodeEntries.append({n, {}, WHITE});
+ return true;
+}
+
+template <class Node>
+void Graph<Node>::depthFirstVisit(qsizetype i, NodeList &result) const
+{
+ m_nodeEntries[i].color = GRAY;
+
+ for (auto to : m_nodeEntries[i].targets) {
+ const qsizetype toIndex = indexOfNode(to);
+ Q_ASSERT(toIndex != -1);
+ switch (colorAt(toIndex)) {
+ case WHITE:
+ depthFirstVisit(toIndex, result);
+ break;
+ case GRAY:
+ return; // This is not a DAG!
+ case BLACK:
+ break;
+ }
+ }
+
+ m_nodeEntries[i].color = BLACK;
+
+ result.append(m_nodeEntries.at(i).node);
+}
+
+template <class Node>
+GraphSortResult<Node> Graph<Node>::topologicalSort() const
+{
+ const qsizetype size = m_nodeEntries.size();
+
+ GraphSortResult<Node> result;
+ result.result.reserve(size);
+
+ if (hasEdges()) {
+ for (qsizetype i = 0; i < size; ++i)
+ m_nodeEntries[i].color = WHITE;
+ for (qsizetype i = 0; i < size; ++i) {
+ if (colorAt(i) == WHITE) // recursive calls may have set it to black
+ depthFirstVisit(i, result.result);
+ }
+ } else { // no edges, shortcut
+ std::transform(m_nodeEntries.cbegin(), m_nodeEntries.cend(),
+ std::back_inserter(result.result),
+ [](const NodeEntry &ne) { return ne.node; });
+ }
+
+ if (result.result.size() == size) {
+ std::reverse(result.result.begin(), result.result.end());
+ } else {
+ for (const auto &nodeEntry : m_nodeEntries) {
+ if (!result.result.contains(nodeEntry.node))
+ result.cyclic.append(nodeEntry.node);
+ }
+ result.result.clear(); // Not a DAG!
+ }
+ return result;
+}
+
+template <class Node>
+bool Graph<Node>::containsEdge(Node from, Node to) const
+{
+ const qsizetype i = indexOfNode(from);
+ return i != -1 && m_nodeEntries.at(i).targets.contains(to);
+}
+
+template <class Node>
+bool Graph<Node>::hasEdges() const
+{
+ for (const auto &nodeEntry : m_nodeEntries) {
+ if (!nodeEntry.targets.isEmpty())
+ return true;
+ }
+ return false;
+}
+
+template <class Node>
+bool Graph<Node>::addEdge(Node from, Node to)
+{
+ const qsizetype i = indexOfNode(from);
+ if (i == -1 || !hasNode(to) || m_nodeEntries.at(i).targets.contains(to))
+ return false;
+ m_nodeEntries[i].targets.append(to);
+ return true;
+}
+
+template <class Node>
+bool Graph<Node>::removeEdge(Node from, Node to)
+{
+ const qsizetype i = indexOfNode(from);
+ if (i == -1)
+ return false;
+ auto &targets = m_nodeEntries[i].targets;
+ const qsizetype toIndex = targets.indexOf(to);
+ if (toIndex == -1)
+ return false;
+ targets.removeAt(toIndex);
+ return true;
+}
+
+template <class Node>
+template <class NameFunction>
+bool Graph<Node>::dumpDot(const QString& fileName,
+ NameFunction nameFunction) const
+{
+ QFile output(fileName);
+ if (!output.open(QIODevice::WriteOnly))
+ return false;
+ QTextStream s(&output);
+ formatDot(s, nameFunction);
+ return true;
+}
+
+template <class Node>
+template <class NameFunction>
+void Graph<Node>::formatDot(QTextStream &s,
+ NameFunction nameFunction) const
+{
+ s << "digraph D {\n";
+ for (const auto &nodeEntry : m_nodeEntries) {
+ if (!nodeEntry.targets.isEmpty()) {
+ const QString fromName = nameFunction(nodeEntry.node);
+ for (const Node &to : nodeEntry.targets)
+ s << '"' << fromName << "\" -> \"" << nameFunction(to) << "\"\n";
+ }
+ }
+ s << "}\n";
+}
+
+template <class Node>
+template <class NameFunction>
+bool Graph<Node>::showGraph(const QString &name, NameFunction f) const
+{
+ QString graph;
+ QTextStream s(&graph);
+ formatDot(s, f);
+ return showDotGraph(name, graph);
+}
+
+template <class Node>
+void Graph<Node>::format(QDebug &debug) const
+{
+ const qsizetype size = m_nodeEntries.size();
+ debug << "nodes[" << size << "] = (";
+ for (qsizetype i = 0; i < size; ++i) {
+ const auto &nodeEntry = m_nodeEntries.at(i);
+ if (i)
+ debug << ", ";
+ debug << nodeEntry.node;
+ if (!nodeEntry.targets.isEmpty()) {
+ debug << " -> [";
+ for (qsizetype t = 0, tsize = nodeEntry.targets.size(); t < tsize; ++t) {
+ if (t)
+ debug << ", ";
+ debug << nodeEntry.targets.at(t);
+ }
+ debug << ']';
+ }
+ }
+ debug << ')';
+}
+
+template <class Node>
+QDebug operator<<(QDebug debug, const Graph<Node> &g)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Graph(";
+ g.format(debug);
+ debug << ')';
+ return debug;
+}
+
+template <class Node>
+void GraphSortResult<Node>::format(QDebug &debug) const
+{
+ if (isValid())
+ debug << "Valid, " << result;
+ else
+ debug << "Invalid, cyclic dependencies: " << cyclic;
+}
+
+template <class Node>
+QDebug operator<<(QDebug debug, const GraphSortResult<Node> &r)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Graph::SortResult(";
+ r.format(debug);
+ debug << ')';
+ return debug;
+}
+
+#endif // GRAPH_H
diff --git a/sources/shiboken6/ApiExtractor/header_paths.h b/sources/shiboken6/ApiExtractor/header_paths.h
new file mode 100644
index 000000000..af4a768e8
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/header_paths.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef HEADER_PATHS_H
+#define HEADER_PATHS_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+enum class HeaderType
+{
+ Standard,
+ System, // -isystem
+ Framework, // macOS framework path
+ FrameworkSystem // macOS framework system path
+};
+
+class HeaderPath {
+public:
+ QByteArray path;
+ HeaderType type;
+
+ static QByteArray includeOption(const HeaderPath &p)
+ {
+ QByteArray option;
+ switch (p.type) {
+ case HeaderType::Standard:
+ option = QByteArrayLiteral("-I");
+ break;
+ case HeaderType::System:
+ option = QByteArrayLiteral("-isystem");
+ break;
+ case HeaderType::Framework:
+ option = QByteArrayLiteral("-F");
+ break;
+ case HeaderType::FrameworkSystem:
+ option = QByteArrayLiteral("-iframework");
+ break;
+ }
+ return option + p.path;
+ }
+};
+
+using HeaderPaths = QList<HeaderPath>;
+
+#endif // HEADER_PATHS_H
diff --git a/sources/shiboken6/ApiExtractor/icecc.cmake b/sources/shiboken6/ApiExtractor/icecc.cmake
new file mode 100644
index 000000000..fa8d3b7cf
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/icecc.cmake
@@ -0,0 +1,14 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+include (CMakeForceCompiler)
+option(ENABLE_ICECC "Enable icecc checking, for distributed compilation")
+if (ENABLE_ICECC)
+ find_program(ICECC icecc)
+ if (ICECC)
+ message(STATUS "icecc found! Distributed compilation for all!! huhuhu.")
+ cmake_force_cxx_compiler(${ICECC} icecc)
+ else(ICECC)
+ message(FATAL_ERROR "icecc NOT found! re-run cmake without -DENABLE_ICECC")
+ endif(ICECC)
+endif(ENABLE_ICECC)
diff --git a/sources/shiboken6/ApiExtractor/include.cpp b/sources/shiboken6/ApiExtractor/include.cpp
new file mode 100644
index 000000000..aee6b7337
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/include.cpp
@@ -0,0 +1,79 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "include.h"
+#include "textstream.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QHash>
+#include <QtCore/QTextStream>
+
+#include "qtcompat.h"
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+QString Include::toString() const
+{
+ if (m_type == IncludePath)
+ return u"#include <"_s + m_name + u'>';
+ if (m_type == LocalPath)
+ return u"#include \""_s + m_name + u'"';
+ return u"import "_s + m_name + u';';
+}
+
+Qt::strong_ordering compareThreeWay(const Include &lhs, const Include &rhs) noexcept
+{
+ if (lhs.m_type < rhs.m_type)
+ return Qt::strong_ordering::less;
+ if (lhs.m_type > rhs.m_type)
+ return Qt::strong_ordering::greater;
+ if (auto c = lhs.m_name.compare(rhs.m_name))
+ return c < 0 ? Qt::strong_ordering::less : Qt::strong_ordering::greater;
+ return Qt::strong_ordering::equal;
+}
+
+QTextStream& operator<<(QTextStream& out, const Include& g)
+{
+ if (g.isValid())
+ out << g.toString() << Qt::endl;
+ return out;
+}
+
+TextStream& operator<<(TextStream& out, const Include& include)
+{
+ if (include.isValid())
+ out << include.toString() << '\n';
+ return out;
+}
+
+TextStream& operator<<(TextStream &out, const IncludeGroup& g)
+{
+ if (!g.includes.isEmpty()) {
+ if (!g.title.isEmpty())
+ out << "\n// " << g.title << "\n";
+ auto includes = g.includes;
+ std::sort(includes.begin(), includes.end());
+ for (const Include &inc : std::as_const(includes))
+ out << inc.toString() << '\n';
+ }
+ return out;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const Include &i)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "Include(";
+ if (i.isValid())
+ d << "type=" << i.type() << ", file=\"" << QDir::toNativeSeparators(i.name()) << '"';
+ else
+ d << "invalid";
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/include.h b/sources/shiboken6/ApiExtractor/include.h
new file mode 100644
index 000000000..875a941f9
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/include.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef INCLUDE_H
+#define INCLUDE_H
+
+#include <QtCore/QtCompare>
+#include <QtCore/QHashFunctions>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+QT_END_NAMESPACE
+
+class TextStream;
+
+class Include
+{
+public:
+ enum IncludeType {
+ IncludePath,
+ LocalPath,
+ TargetLangImport
+ };
+
+ Include() = default;
+ Include(IncludeType t, const QString &nam) : m_type(t), m_name(nam) {};
+
+ bool isValid() const
+ {
+ return !m_name.isEmpty();
+ }
+
+ IncludeType type() const
+ {
+ return m_type;
+ }
+
+ QString name() const
+ {
+ return m_name;
+ }
+
+ QString toString() const;
+
+ int compare(const Include &rhs) const;
+
+private:
+ friend size_t qHash(Include &inc, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, inc.m_type, inc.m_name);
+ }
+ friend bool comparesEqual(const Include &lhs, const Include &rhs) noexcept
+ {
+ return lhs.m_type == rhs.m_type && lhs.m_name == rhs.m_name;
+ }
+ friend Qt::strong_ordering compareThreeWay(const Include &lhs,
+ const Include &rhs) noexcept;
+ Q_DECLARE_STRONGLY_ORDERED(Include)
+
+ IncludeType m_type = IncludePath;
+ QString m_name;
+};
+
+QTextStream& operator<<(QTextStream& out, const Include& include);
+TextStream& operator<<(TextStream& out, const Include& include);
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const Include &i);
+#endif
+
+using IncludeList = QList<Include>;
+
+struct IncludeGroup
+{
+ QString title;
+ IncludeList includes;
+
+ void append(const Include &include)
+ {
+ IncludeGroup::appendInclude(include, &includes);
+ }
+
+ static void appendInclude(const Include &include, IncludeList *list)
+ {
+ if (include.isValid() && !list->contains(include))
+ list->append(include);
+ }
+};
+
+TextStream& operator<<(TextStream &out, const IncludeGroup& include);
+
+using IncludeGroupList = QList<IncludeGroup>;
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/merge.xsl b/sources/shiboken6/ApiExtractor/merge.xsl
new file mode 100644
index 000000000..c6cab5a42
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/merge.xsl
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+ <xsl:output method="xml" indent="yes"/>
+ <xsl:param name="lang" />
+ <xsl:param name="source" />
+
+ <xsl:template match="processing-instruction()" />
+
+ <xsl:template match="/typesystem">
+ <xsl:copy>
+ <xsl:for-each select="@*">
+ <xsl:copy>
+ <xsl:value-of select="." />
+ </xsl:copy>
+ </xsl:for-each>
+
+ <xsl:for-each select="document($source)/typesystem/@*">
+ <xsl:copy>
+ <xsl:value-of select="." />
+ </xsl:copy>
+ </xsl:for-each>
+
+ <xsl:variable name="other" select="document($source)/typesystem/*[not(self::object-type | self::value-type | self::interface-type | self::namespace-type)]" />
+ <xsl:if test="$other">
+ <xsl:choose>
+ <xsl:when test="$lang != ''">
+ <xsl:element name="language">
+ <xsl:attribute name="name" ><xsl:value-of select="$lang" /></xsl:attribute>
+ <xsl:copy-of select="$other" />
+ </xsl:element>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$other" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <xsl:apply-templates select="node()" />
+
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="/typesystem/*[self::object-type | self::value-type | self::interface-type | self::namespace-type]">
+ <xsl:variable name="name" select="name()" />
+ <xsl:copy>
+ <xsl:for-each select="@*">
+ <xsl:copy>
+ <xsl:value-of select="." />
+ </xsl:copy>
+ </xsl:for-each>
+
+ <xsl:apply-templates select="node()" />
+
+ <xsl:variable name="other" select="document($source)/typesystem/*[name() = $name][@name = current()/@name]" />
+ <xsl:if test="$other">
+ <xsl:choose>
+ <xsl:when test="$lang != ''">
+ <xsl:element name="language">
+ <xsl:attribute name="name" ><xsl:value-of select="$lang" /></xsl:attribute>
+ <xsl:copy-of select="$other/node()" />
+ </xsl:element>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$other/node()" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Plain identity transform. -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*"/>
+ <xsl:apply-templates select="node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/sources/shiboken6/ApiExtractor/messages.cpp b/sources/shiboken6/ApiExtractor/messages.cpp
new file mode 100644
index 000000000..170595660
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/messages.cpp
@@ -0,0 +1,1004 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "messages.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafield.h"
+#include "abstractmetafunction.h"
+#include "abstractmetalang.h"
+#include "modifications.h"
+#include "sourcelocation.h"
+#include "typedatabase.h"
+#include "functiontypeentry.h"
+#include "enumtypeentry.h"
+#include "smartpointertypeentry.h"
+#include <codemodel.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QStringList>
+#include <QtCore/QXmlStreamReader>
+
+using namespace Qt::StringLiterals;
+
+// abstractmetabuilder.cpp
+
+static QTextStream &operator<<(QTextStream &s, Access a)
+{
+ switch (a) {
+ case Access::Public:
+ s << "public";
+ break;
+ case Access::Protected:
+ s << "protected";
+ break;
+ case Access::Private:
+ s << "private";
+ break;
+ }
+ return s;
+}
+
+QString msgNoFunctionForModification(const AbstractMetaClassCPtr &klass,
+ const QString &signature,
+ const QString &originalSignature,
+ const QStringList &possibleSignatures,
+ const AbstractMetaFunctionCList &allFunctions)
+{
+ QString result;
+ QTextStream str(&result);
+ str << klass->typeEntry()->sourceLocation() << "signature '"
+ << signature << '\'';
+ if (!originalSignature.isEmpty() && originalSignature != signature)
+ str << " (specified as '" << originalSignature << "')";
+ str << " for function modification in '"
+ << klass->qualifiedCppName() << "' not found.";
+ if (!possibleSignatures.isEmpty()) {
+ str << "\n Possible candidates:\n";
+ for (const auto &s : possibleSignatures)
+ str << " " << s << '\n';
+ } else if (!allFunctions.isEmpty()) {
+ str << "\n No candidates were found. Member functions:\n";
+ const auto maxCount = qMin(qsizetype(10), allFunctions.size());
+ for (qsizetype f = 0; f < maxCount; ++f)
+ str << " " << allFunctions.at(f)->minimalSignature() << '\n';
+ if (maxCount < allFunctions.size())
+ str << " ...\n";
+ }
+ return result;
+}
+
+QString msgArgumentIndexOutOfRange(const AbstractMetaFunction *func, int index)
+{
+ QString result;
+ QTextStream str(&result);
+ str <<"Index " << index << " out of range for " << func->classQualifiedSignature() << '.';
+ return result;
+}
+
+QString msgTypeModificationFailed(const QString &type, int n,
+ const AbstractMetaFunction *func,
+ const QString &why)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Unable to modify the ";
+ if (n == 0)
+ str << "return type";
+ else
+ str << "type of argument " << n;
+
+ str << " of ";
+ if (auto c = func->ownerClass())
+ str << c->name() << "::";
+ str << func->signature() << " to \"" << type << "\": " << why;
+ return result;
+}
+
+QString msgInvalidArgumentModification(const AbstractMetaFunctionCPtr &func,
+ int argIndex)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Invalid ";
+ if (argIndex == 0)
+ str << "return type modification";
+ else
+ str << "modification of argument " << argIndex;
+ str << " for " << func->classQualifiedSignature();
+ return result;
+}
+
+QString msgArgumentOutOfRange(int number, int minValue, int maxValue)
+{
+ QString result;
+ QTextStream(&result) << "Argument number " << number
+ << " out of range " << minValue << ".." << maxValue << '.';
+ return result;
+}
+
+QString msgArgumentRemovalFailed(const AbstractMetaFunction *func, int n,
+ const QString &why)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Unable to remove argument " << n << " of ";
+ if (auto c = func->ownerClass())
+ str << c->name() << "::";
+ str << func->signature() << ": " << why;
+ return result;
+}
+
+template <class Stream>
+static void msgFormatEnumType(Stream &str,
+ const EnumModelItem &enumItem,
+ const QString &className)
+{
+ switch (enumItem->enumKind()) {
+ case CEnum:
+ str << "Enum '" << enumItem->qualifiedName().join(u"::"_s) << '\'';
+ break;
+ case AnonymousEnum: {
+ const EnumeratorList &values = enumItem->enumerators();
+ str << "Anonymous enum (";
+ switch (values.size()) {
+ case 0:
+ break;
+ case 1:
+ str << values.constFirst()->name();
+ break;
+ case 2:
+ str << values.at(0)->name() << ", " << values.at(1)->name();
+ break;
+ default:
+ str << values.at(0)->name() << ", ... , "
+ << values.at(values.size() - 1)->name();
+ break;
+ }
+ str << ')';
+ }
+ break;
+ case EnumClass:
+ str << "Scoped enum '" << enumItem->qualifiedName().join(u"::"_s) << '\'';
+ break;
+ }
+ if (!className.isEmpty())
+ str << " (class: " << className << ')';
+}
+
+static void formatAddedFuncError(const QString &addedFuncName,
+ const AbstractMetaClassCPtr &context,
+ QTextStream &str)
+{
+ if (context) {
+ str << context->typeEntry()->sourceLocation()
+ << "Unable to traverse function \"" << addedFuncName
+ << "\" added to \"" << context->name() << "\": ";
+ } else {
+ str << "Unable to traverse added global function \""
+ << addedFuncName << "\": ";
+ }
+}
+
+QString msgAddedFunctionInvalidArgType(const QString &addedFuncName,
+ const QStringList &typeName,
+ int pos, const QString &why,
+ const AbstractMetaClassCPtr &context)
+{
+ QString result;
+ QTextStream str(&result);
+ formatAddedFuncError(addedFuncName, context, str);
+ str << "Unable to translate type \"" << typeName.join(u"::"_s)
+ << "\" of argument " << pos << " of added function \""
+ << addedFuncName << "\": " << why;
+ return result;
+}
+
+QString msgAddedFunctionInvalidReturnType(const QString &addedFuncName,
+ const QStringList &typeName, const QString &why,
+ const AbstractMetaClassCPtr &context)
+{
+ QString result;
+ QTextStream str(&result);
+ formatAddedFuncError(addedFuncName, context, str);
+ str << "Unable to translate return type \"" << typeName.join(u"::"_s)
+ << "\" of added function \"" << addedFuncName << "\": "
+ << why;
+ return result;
+}
+
+QString msgUnnamedArgumentDefaultExpression(const AbstractMetaClassCPtr &context,
+ int n, const QString &className,
+ const AbstractMetaFunction *f)
+{
+ QString result;
+ QTextStream str(&result);
+ if (context)
+ str << context->sourceLocation();
+ str << "Argument " << n << " on function '" << className << "::"
+ << f->minimalSignature() << "' has default expression but does not have name.";
+ return result;
+}
+
+QString msgClassOfEnumNotFound(const EnumTypeEntryCPtr &entry)
+{
+ QString result;
+ QTextStream str(&result);
+ str << entry->sourceLocation() << "AbstractMeta::findEnum(), unknown class '"
+ << entry->parent()->qualifiedCppName() << "' in '"
+ << entry->qualifiedCppName() << '\'';
+ return result;
+}
+
+QString msgNoEnumTypeEntry(const EnumModelItem &enumItem,
+ const QString &className)
+{
+ QString result;
+ QTextStream str(&result);
+ str << enumItem->sourceLocation();
+ msgFormatEnumType(str, enumItem, className);
+ str << " does not have a type entry (type systems: "
+ << TypeDatabase::instance()->loadedTypeSystemNames() << ')';
+ return result;
+}
+
+QString msgNoEnumTypeConflict(const EnumModelItem &enumItem,
+ const QString &className,
+ const TypeEntryCPtr &t)
+{
+ QString result;
+ QDebug debug(&result); // Use the debug operator for TypeEntry::Type
+ debug.noquote();
+ debug.nospace();
+ debug << enumItem->sourceLocation().toString();
+ msgFormatEnumType(debug, enumItem, className);
+ debug << " is not an enum (type: " << t->type() << ')';
+ return result;
+}
+
+QString msgNamespaceNoTypeEntry(const NamespaceModelItem &item,
+ const QString &fullName)
+{
+ QString result;
+ QTextStream str(&result);
+ str << item->sourceLocation() << "namespace '" << fullName
+ << "' does not have a type entry (type systems: "
+ << TypeDatabase::instance()->loadedTypeSystemNames() << ')';
+ return result;
+}
+
+QString msgNamespaceNotFound(const QString &name)
+{
+ return u"namespace '"_s + name + u"' not found."_s;
+}
+
+QString msgAmbiguousVaryingTypesFound(const QString &qualifiedName, const TypeEntryCList &te)
+{
+ QString result = u"Ambiguous types of varying types found for \""_s + qualifiedName
+ + u"\": "_s;
+ QDebug(&result) << te;
+ return result;
+}
+
+QString msgAmbiguousTypesFound(const QString &qualifiedName, const TypeEntryCList &te)
+{
+ QString result = u"Ambiguous types found for \""_s + qualifiedName
+ + u"\": "_s;
+ QDebug(&result) << te;
+ return result;
+}
+
+QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n,
+ const QString &why)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "unmatched type '" << arg->type().toString() << "' in parameter #"
+ << (n + 1);
+ if (!arg->name().isEmpty())
+ str << " \"" << arg->name() << '"';
+ str << ": " << why;
+ return result;
+}
+
+QString msgUnmatchedReturnType(const FunctionModelItem &functionItem,
+ const QString &why)
+{
+ return u"unmatched return type '"_s
+ + functionItem->type().toString()
+ + u"': "_s + why;
+}
+
+QString msgSkippingFunction(const FunctionModelItem &functionItem,
+ const QString &signature, const QString &why)
+{
+ QString result;
+ QTextStream str(&result);
+ str << functionItem->sourceLocation() << "skipping "
+ << functionItem->accessPolicy() << ' ';
+ const bool isAbstract = functionItem->attributes().testFlag(FunctionAttribute::Abstract);
+ if (isAbstract)
+ str << "abstract ";
+ str << "function '" << signature << "', " << why;
+ if (isAbstract) {
+ str << "\nThis will lead to compilation errors due to not "
+ "being able to instantiate the wrapper.";
+ }
+ return result;
+}
+
+QString msgShadowingFunction(const AbstractMetaFunction *f1,
+ const AbstractMetaFunction *f2)
+{
+ auto f2Class = f2->implementingClass();
+ QString result;
+ QTextStream str(&result);
+ str << f2Class->sourceLocation() << "Shadowing: " << f1->classQualifiedSignature()
+ << " and " << f2->classQualifiedSignature();
+ return result;
+}
+
+QString msgSignalOverloaded(const AbstractMetaClassCPtr &c,
+ const AbstractMetaFunction *f)
+{
+ QString result;
+ QTextStream str(&result);
+ str << c->sourceLocation() << "signal '" << f->name() << "' in class '"
+ << c->name() << "' is overloaded.";
+ return result;
+}
+
+QString msgSkippingField(const VariableModelItem &field, const QString &className,
+ const QString &type)
+{
+ QString result;
+ QTextStream str(&result);
+ str << field->sourceLocation() << "skipping " << field->accessPolicy()
+ << " field '" << className << "::" << field->name()
+ << "' with unmatched type '" << type << '\'';
+ return result;
+}
+
+static const char msgCompilationError[] =
+ "This could potentially lead to compilation errors.";
+
+QString msgTypeNotDefined(const TypeEntryCPtr &entry)
+{
+ QString result;
+ QTextStream str(&result);
+ const bool hasConfigCondition = entry->isComplex()
+ && std::static_pointer_cast<const ConfigurableTypeEntry>(entry)->hasConfigCondition();
+ str << entry->sourceLocation() << "type '" <<entry->qualifiedCppName()
+ << "' is specified in typesystem, but not defined";
+ if (hasConfigCondition)
+ str << " (disabled by configuration?).";
+ else
+ str << ". " << msgCompilationError;
+ return result;
+}
+
+QString msgGlobalFunctionNotDefined(const FunctionTypeEntryCPtr &fte,
+ const QString &signature,
+ const QStringList &candidates)
+{
+ QString result;
+ QTextStream str(&result);
+ str << fte->sourceLocation() << "Global function '" << signature
+ << "' is specified in typesystem, but not defined.";
+ if (!candidates.isEmpty())
+ str << " Candidates are: " << candidates.join(u", "_s);
+ str << ' ' << msgCompilationError;
+ return result;
+}
+
+QString msgStrippingArgument(const FunctionModelItem &f, int i,
+ const QString &originalSignature,
+ const ArgumentModelItem &arg,
+ const QString &reason)
+{
+ QString result;
+ QTextStream str(&result);
+ str << f->sourceLocation() << "Stripping argument #" << (i + 1) << " of "
+ << originalSignature << " due to unmatched type \""
+ << arg->type().toString() << "\" with default expression \""
+ << arg->defaultValueExpression() << "\": " << reason;
+ return result;
+}
+
+QString msgEnumNotDefined(const EnumTypeEntryCPtr &t)
+{
+ QString result;
+ QTextStream str(&result);
+ str << t->sourceLocation() << "enum '" << t->qualifiedCppName()
+ << "' is specified in typesystem, but not declared.";
+ return result;
+}
+
+QString msgUnknownBase(const AbstractMetaClassCPtr &metaClass,
+ const QString &baseClassName)
+{
+ QString result;
+ QTextStream str(&result);
+ str << metaClass->sourceLocation() << "Base class '" << baseClassName << "' of class '"
+ << metaClass->name() << "' not found in the code for setting up inheritance.";
+ return result;
+}
+
+QString msgBaseNotInTypeSystem(const AbstractMetaClassCPtr &metaClass,
+ const QString &baseClassName)
+{
+ QString result;
+ QTextStream str(&result);
+ str << metaClass->sourceLocation() << "Base class '" << baseClassName << "' of class '"
+ << metaClass->name() << "' not found in the type system for setting up inheritance.";
+ return result;
+}
+
+QString msgArrayModificationFailed(const FunctionModelItem &functionItem,
+ const QString &className,
+ const QString &errorMessage)
+{
+ QString result;
+ QTextStream str(&result);
+ str << functionItem->sourceLocation() << "While traversing " << className
+ << ": " << errorMessage;
+ return result;
+}
+
+QString msgCannotResolveEntity(const QString &name, const QString &reason)
+{
+ return u"Cannot resolve entity \""_s + name + u"\": "_s + reason;
+}
+
+QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason)
+{
+ return function + u": Cannot use parameter "_s
+ + QString::number(i + 1) + u" as an array: "_s + reason;
+}
+
+QString msgUnableToTranslateType(const QString &t, const QString &why)
+{
+ return u"Unable to translate type \""_s + t + u"\": "_s + why;
+}
+
+QString msgUnableToTranslateType(const TypeInfo &typeInfo,
+ const QString &why)
+{
+ return msgUnableToTranslateType(typeInfo.toString(), why);
+}
+
+QString msgCannotFindTypeEntry(const QString &t)
+{
+ return u"Cannot find type entry for \""_s + t + u"\"."_s;
+}
+
+QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &smartPointerType)
+{
+ return u"Cannot find type entry \""_s + t
+ + u"\" for instantiation of \""_s +smartPointerType + u"\"."_s;
+}
+
+QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass,
+ const TypeInfo &info,
+ const QString &what)
+{
+ return "While inheriting template "_L1 + subclass->name()
+ + " from "_L1 + info.toString() + ": "_L1 + what;
+}
+
+QString msgIgnoringTemplateParameter(const QString &typeName,
+ const char *why)
+{
+ return "Ignoring template parameter "_L1 + typeName +
+ ": "_L1 + QLatin1StringView(why);
+}
+
+QString msgInvalidSmartPointerType(const TypeInfo &i)
+{
+ return u"Invalid smart pointer type \""_s +i.toString() + u"\"."_s;
+}
+
+QString msgCannotFindSmartPointerInstantion(const TypeInfo &i)
+{
+ return u"Cannot find instantiation of smart pointer type for \""_s
+ + i.toString() + u"\"."_s;
+}
+
+QString msgCannotTranslateTemplateArgument(int i,
+ const TypeInfo &typeInfo,
+ const QString &why)
+{
+ QString result;
+ QTextStream(&result) << "Unable to translate template argument "
+ << (i + 1) << typeInfo.toString() << ": " << why;
+ return result;
+}
+
+QString msgDisallowThread(const AbstractMetaFunction *f)
+{
+ QString result;
+ QTextStream str(&result);
+ str <<"Disallowing threads for ";
+ if (auto c = f->declaringClass())
+ str << c->name() << "::";
+ str << f->name() << "().";
+ return result;
+}
+
+QString msgNamespaceToBeExtendedNotFound(const QString &namespaceName, const QString &packageName)
+{
+ return u"The namespace '"_s + namespaceName
+ + u"' to be extended cannot be found in package "_s
+ + packageName + u'.';
+}
+
+QString msgPropertyTypeParsingFailed(const QString &name, const QString &typeName,
+ const QString &why)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Unable to decide type of property: \"" << name << "\" (" << typeName
+ << "): " << why;
+ return result;
+}
+
+QString msgPropertyExists(const QString &className, const QString &name)
+{
+ return u"class "_s + className + u" already has a property \""_s
+ + name + u"\" (defined by Q_PROPERTY)."_s;
+}
+
+QString msgFunctionVisibilityModified(const AbstractMetaClassCPtr &c,
+ const AbstractMetaFunction *f)
+{
+ QString result;
+ QTextStream str(&result);
+ str << c->sourceLocation() << "Visibility of function '" << f->name()
+ << "' modified in class '"<< c->name() << '\'';
+ return result;
+}
+
+QString msgUsingMemberClassNotFound(const AbstractMetaClassCPtr &c,
+ const QString &baseClassName,
+ const QString &memberName)
+{
+ QString result;
+ QTextStream str(&result);
+ str << c->sourceLocation() << "base class \"" << baseClassName
+ << "\" of \"" << c->qualifiedCppName() << "\" for using member \""
+ << memberName << "\" not found.";
+ return result;
+}
+// docparser.cpp
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const char *what, const QString &name,
+ const QString &query)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Cannot find documentation for " << what
+ << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName);
+ if (!query.isEmpty())
+ str << "\n using query:\n " << query;
+ return result;
+}
+
+QString msgFallbackForDocumentation(const QString &fileName,
+ const char *what, const QString &name,
+ const QString &query)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Fallback used while trying to find documentation for " << what
+ << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName);
+ if (!query.isEmpty())
+ str << "\n using query:\n " << query;
+ return result;
+}
+
+static QString functionDescription(const AbstractMetaFunction *function)
+{
+ QString result = u'"' + function->classQualifiedSignature() + u'"';
+ if (function->flags().testFlag(AbstractMetaFunction::Flag::HiddenFriend))
+ result += u" (hidden friend)"_s;
+ if (function->flags().testFlag(AbstractMetaFunction::Flag::InheritedFromTemplate))
+ result += u" (inherited from template)"_s;
+ return result;
+}
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const AbstractMetaFunction *function,
+ const QString &query)
+{
+ return msgCannotFindDocumentation(fileName, "function",
+ functionDescription(function), query);
+}
+
+QString msgFallbackForDocumentation(const QString &fileName,
+ const AbstractMetaFunction *function,
+ const QString &query)
+{
+ return msgFallbackForDocumentation(fileName, "function",
+ functionDescription(function), query);
+}
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaEnum &e,
+ const QString &query)
+{
+ QString name = e.name();
+ if (metaClass != nullptr)
+ name.prepend(metaClass->name() + "::"_L1);
+ return msgCannotFindDocumentation(fileName, "enum", name, query);
+}
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaField &f,
+ const QString &query)
+{
+ QString name = f.name();
+ if (metaClass != nullptr)
+ name.prepend(metaClass->name() + "::"_L1);
+ return msgCannotFindDocumentation(fileName, "field", name, query);
+}
+
+QString msgXpathDocModificationError(const DocModificationList& mods,
+ const QString &what)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Error when applying modifications (";
+ for (const DocModification &mod : mods) {
+ if (mod.mode() == TypeSystem::DocModificationXPathReplace) {
+ str << '"' << mod.xpath() << "\" -> \"";
+ const QString simplified = mod.code().simplified();
+ if (simplified.size() > 20)
+ str << QStringView{simplified}.left(20) << "...";
+ else
+ str << simplified;
+ str << '"';
+ }
+ }
+ str << "): " << what;
+ return result;
+}
+
+// fileout.cpp
+
+QString msgCannotOpenForReading(const QFile &f)
+{
+ return QString::fromLatin1("Failed to open file '%1' for reading: %2")
+ .arg(QDir::toNativeSeparators(f.fileName()), f.errorString());
+}
+
+QString msgCannotOpenForWriting(const QFile &f)
+{
+ return QString::fromLatin1("Failed to open file '%1' for writing: %2")
+ .arg(QDir::toNativeSeparators(f.fileName()), f.errorString());
+}
+
+QString msgWriteFailed(const QFile &f, qsizetype size)
+{
+ QString result;
+ QTextStream(&result) << "Failed to write " << size << "bytes to '"
+ << QDir::toNativeSeparators(f.fileName()) << "': "
+ << f.errorString();
+ return result;
+}
+
+// generator.cpp
+
+QString msgCannotUseEnumAsInt(const QString &name)
+{
+ return u"Cannot convert the protected scoped enum \""_s + name
+ + u"\" to type int when generating wrappers for the protected hack. "
+ "Compilation errors may occur when used as a function argument."_s;
+}
+
+QString msgCannotFindSmartPointerGetter(const SmartPointerTypeEntryCPtr &te)
+{
+ return u"Getter \""_s + te->getter() + u"()\" of smart pointer \""_s
+ + te->name() + u"\" not found."_s;
+}
+
+QString msgCannotFindSmartPointerMethod(const SmartPointerTypeEntryCPtr &te, const QString &m)
+{
+ return u"Method \""_s + m + u"()\" of smart pointer \""_s
+ + te->name() + u"\" not found."_s;
+}
+
+QString msgMethodNotFound(const AbstractMetaClassCPtr &klass, const QString &name)
+{
+ return u"Method \""_s + name + u"\" not found in class "_s
+ + klass->name() + u'.';
+}
+
+// main.cpp
+
+QString msgLeftOverArguments(const QString &remainingArgs, const QStringList &argV)
+{
+ QString message;
+ QTextStream str(&message);
+ str << "shiboken: Unprocessed arguments: " << remainingArgs
+ << "\nCommand line: " << argV.join(u' ');
+ return message;
+}
+
+QString msgInvalidVersion(const QString &package, const QString &version)
+{
+ return u"Invalid version \""_s + version
+ + u"\" specified for package "_s + package + u'.';
+}
+
+QString msgCyclicDependency(const QString &funcName, const QString &graphName,
+ const AbstractMetaFunctionCList &cyclic,
+ const AbstractMetaFunctionCList &involvedConversions)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Cyclic dependency found on overloaddata for \"" << funcName
+ << "\" method! The graph boy saved the graph at \"" << QDir::toNativeSeparators(graphName)
+ << "\". Cyclic functions:";
+ for (const auto &c : cyclic)
+ str << ' ' << c->signature();
+ if (const auto count = involvedConversions.size()) {
+ str << " Implicit conversions (" << count << "): ";
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ str << ", \"";
+ str << involvedConversions.at(i)->signature() << '"';
+ if (const auto c = involvedConversions.at(i)->implementingClass())
+ str << '(' << c->name() << ')';
+ }
+ }
+ return result;
+}
+
+// shibokengenerator.cpp
+
+QString msgClassNotFound(const TypeEntryCPtr &t)
+{
+ return u"Could not find class \""_s
+ + t->qualifiedCppName()
+ + u"\" in the code model. Maybe it is forward declared?"_s;
+}
+
+QString msgEnclosingClassNotFound(const TypeEntryCPtr &t)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Warning: Enclosing class \"" << t->parent()->name()
+ << "\" of class \"" << t->name() << "\" not found.";
+ return result;
+}
+
+QString msgUnknownOperator(const AbstractMetaFunction *func)
+{
+ QString result = u"Unknown operator: \""_s + func->originalName()
+ + u'"';
+ if (const auto c = func->implementingClass())
+ result += u" in class: "_s + c->name();
+ return result;
+}
+
+QString msgWrongIndex(const char *varName, const QString &capture,
+ const AbstractMetaFunction *func)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Wrong index for " << varName << " variable (" << capture << ") on ";
+ if (const auto c = func->implementingClass())
+ str << c->name() << "::";
+ str << func->signature();
+ return result;
+}
+
+QString msgCannotFindType(const QString &type, const QString &variable,
+ const QString &why)
+{
+ QString result;
+ QTextStream(&result) << "Could not find type '"
+ << type << "' for use in '" << variable << "' conversion: " << why
+ << "\nMake sure to use the full C++ name, e.g. 'Namespace::Class'.";
+ return result;
+}
+
+QString msgCannotBuildMetaType(const QString &s)
+{
+ return u"Unable to build meta type for \""_s + s + u"\": "_s;
+}
+
+QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type, const QString &why)
+{
+ QString result;
+ QTextStream str(&result);
+ str << where << ": Could not find a minimal constructor for type '" << type << '\'';
+ if (why.isEmpty())
+ str << '.';
+ else
+ str << ": " << why << ' ';
+ str << "This will result in a compilation error.";
+ return result;
+}
+
+// typedatabase.cpp
+
+QString msgRejectReason(const TypeRejection &r, const QString &needle)
+{
+ QString result;
+ QTextStream str(&result);
+ switch (r.matchType) {
+ case TypeRejection::ExcludeClass:
+ str << "matches class exclusion \"" << r.className.pattern() << '"';
+ break;
+ case TypeRejection::Function:
+ case TypeRejection::Field:
+ case TypeRejection::Enum:
+ str << "matches class \"" << r.className.pattern() << "\" and \""
+ << r.pattern.pattern() << '"';
+ break;
+ case TypeRejection::ArgumentType:
+ case TypeRejection::ReturnType:
+ str << "matches class \"" << r.className.pattern() << "\" and \""
+ << needle << "\" matches \"" << r.pattern.pattern() << '"';
+ break;
+ }
+ return result;
+}
+
+// typesystem.cpp
+
+QString msgCannotFindNamespaceToExtend(const QString &name,
+ const QString &extendsPackage)
+{
+ return u"Cannot find namespace "_s + name
+ + u" in package "_s + extendsPackage;
+}
+
+QString msgExtendingNamespaceRequiresPattern(const QString &name)
+{
+ return u"Namespace "_s + name
+ + u" requires a file pattern since it extends another namespace."_s;
+}
+
+QString msgInvalidRegularExpression(const QString &pattern, const QString &why)
+{
+ return u"Invalid pattern \""_s + pattern + u"\": "_s + why;
+}
+
+QString msgNoRootTypeSystemEntry()
+{
+ return u"Type system entry appears out of order, there does not seem to be a root type system element."_s;
+}
+
+QString msgIncorrectlyNestedName(const QString &name)
+{
+ return u"Nesting types by specifying '::' is no longer supported ("_s
+ + name + u")."_s;
+}
+
+QString msgCannotFindView(const QString &viewedName, const QString &name)
+{
+ return u"Unable to find viewed type "_s + viewedName
+ + u" for "_s + name;
+}
+
+QString msgCannotFindSnippet(const QString &file, const QString &snippetLabel)
+{
+ QString result;
+ QTextStream str(&result);
+ str << "Cannot find snippet \"" << snippetLabel << "\" in "
+ << QDir::toNativeSeparators(file) << '.';
+ return result;
+}
+
+QString msgSnippetError(const QString &context, const char *what)
+{
+ return "Error processing code snippet of "_L1 + context
+ + ": "_L1 + QString::fromUtf8(what);
+}
+
+QString msgUnableToResolveTypedef(const QString &sourceType, const QString &sourceName)
+{
+ QString result;
+ QTextStream(&result) << "Unable to resolve typedef \"" << sourceType
+ << "\": Could not find a value, container, object or smart pointer type named \""
+ << sourceName << "\".";
+ return result;
+}
+
+// cppgenerator.cpp
+
+QString msgPureVirtualFunctionRemoved(const AbstractMetaFunction *f)
+{
+ QString result;
+ auto klass = f->ownerClass();
+ QTextStream str(&result);
+ str << klass->sourceLocation() << "Pure virtual method '" << klass->name()
+ << "::"<< f->minimalSignature()
+ << "' must be implemented but was completely removed on type system.";
+ return result;
+}
+
+QString msgUnknownTypeInArgumentTypeReplacement(const QString &typeReplaced,
+ const AbstractMetaFunction *f)
+{
+ QString result;
+ QTextStream str(&result);
+ if (auto klass = f->ownerClass())
+ str << klass->sourceLocation();
+ str << "Unknown type '" << typeReplaced
+ << "' used as argument type replacement in function '" << f->signature()
+ << "', the generated code may be broken.";
+ return result;
+}
+
+QString msgDuplicateBuiltInTypeEntry(const QString &name)
+{
+ return u"A type entry duplicating the built-in type \""_s
+ + name + u"\" was found. It is ignored."_s;
+}
+
+QString msgDuplicateTypeEntry(const QString &name)
+{
+ return u"Duplicate type entry: '"_s + name + u"'."_s;
+}
+
+QString msgInvalidTargetLanguageApiName(const QString &name)
+{
+ return u"Invalid target language API name \""_s
+ + name + u"\"."_s;
+}
+
+QString msgUnknownCheckFunction(const TypeEntryCPtr &t)
+{
+ return u"Unknown check function for type: '"_s
+ + t->qualifiedCppName() + u"'."_s;
+}
+
+QString msgArgumentClassNotFound(const AbstractMetaFunctionCPtr &func,
+ const TypeEntryCPtr &t)
+{
+ QString result;
+ QTextStream(&result) << "Internal Error: Class \"" << t->qualifiedCppName()
+ << "\" for \"" << func->classQualifiedSignature() << "\" not found!";
+ return result;
+}
+
+QString msgMissingCustomConversion(const TypeEntryCPtr &t)
+{
+ QString result;
+ QTextStream(&result) << "Entry \"" << t->qualifiedCppName()
+ << "\" is missing a custom conversion.";
+ return result;
+}
+
+QString msgUnknownArrayPointerConversion(const QString &s)
+{
+ return u"Warning: Falling back to pointer conversion for unknown array type \""_s
+ + s + u"\""_s;
+}
+
+QString msgMissingProjectFileMarker(const QString &name, const QByteArray &startMarker)
+{
+ return u"First line of project file \""_s + QDir::toNativeSeparators(name)
+ + u"\" must be the string \""_s + QString::fromLatin1(startMarker) + u"\"."_s;
+}
+
+QString msgInvalidLanguageLevel(const QString &l)
+{
+ return u"Invalid argument for language level: \""_s + l + u"\"."_s;
+}
+
+QString msgCannotFindImage(const QString &href, const QString &context,
+ const QString &candidate)
+{
+ return "Cannot resolve image "_L1 + href + " for "_L1 + context
+ + " (tried "_L1 + QDir::toNativeSeparators(candidate) + ")."_L1;
+}
diff --git a/sources/shiboken6/ApiExtractor/messages.h b/sources/shiboken6/ApiExtractor/messages.h
new file mode 100644
index 000000000..5216b26a7
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/messages.h
@@ -0,0 +1,267 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MESSAGES_H
+#define MESSAGES_H
+
+#include "abstractmetalang_typedefs.h"
+#include "parser/codemodel_fwd.h"
+#include "modifications_typedefs.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QString>
+
+class EnumTypeEntry;
+class FunctionTypeEntry;
+class SmartPointerTypeEntry;
+class TypeEntry;
+class TypeInfo;
+struct TypeRejection;
+
+QT_FORWARD_DECLARE_CLASS(QDir)
+QT_FORWARD_DECLARE_CLASS(QFile)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
+
+QString msgAddedFunctionInvalidArgType(const QString &addedFuncName,
+ const QStringList &typeName,
+ int pos, const QString &why,
+ const AbstractMetaClassCPtr &context = {});
+
+QString msgAddedFunctionInvalidReturnType(const QString &addedFuncName,
+ const QStringList &typeName, const QString &why,
+ const AbstractMetaClassCPtr &context = {});
+
+QString msgUnnamedArgumentDefaultExpression(const AbstractMetaClassCPtr &context,
+ int n, const QString &className,
+ const AbstractMetaFunction *f);
+
+QString msgArgumentIndexOutOfRange(const AbstractMetaFunction *func, int index);
+
+QString msgNoFunctionForModification(const AbstractMetaClassCPtr &klass,
+ const QString &signature,
+ const QString &originalSignature,
+ const QStringList &possibleSignatures,
+ const AbstractMetaFunctionCList &allFunctions);
+
+QString msgTypeModificationFailed(const QString &type, int n,
+ const AbstractMetaFunction *func,
+ const QString &why);
+
+QString msgInvalidArgumentModification(const AbstractMetaFunctionCPtr &func,
+ int argIndex);
+
+QString msgArgumentOutOfRange(int number, int minValue, int maxValue);
+
+QString msgArgumentRemovalFailed(const AbstractMetaFunction *func, int n,
+ const QString &why);
+
+QString msgClassOfEnumNotFound(const EnumTypeEntryCPtr &entry);
+
+QString msgNoEnumTypeEntry(const EnumModelItem &enumItem,
+ const QString &className);
+
+
+QString msgNoEnumTypeConflict(const EnumModelItem &enumItem,
+ const QString &className,
+ const TypeEntryCPtr &t);
+
+QString msgNamespaceNoTypeEntry(const NamespaceModelItem &item,
+ const QString &fullName);
+
+QString msgNamespaceNotFound(const QString &name);
+
+QString msgAmbiguousVaryingTypesFound(const QString &qualifiedName, const TypeEntryCList &te);
+QString msgAmbiguousTypesFound(const QString &qualifiedName, const TypeEntryCList &te);
+
+QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n,
+ const QString &why);
+
+QString msgUnmatchedReturnType(const FunctionModelItem &functionItem,
+ const QString &why);
+
+QString msgShadowingFunction(const AbstractMetaFunction *f1,
+ const AbstractMetaFunction *f2);
+
+QString msgSignalOverloaded(const AbstractMetaClassCPtr &c,
+ const AbstractMetaFunction *f);
+
+QString msgSkippingFunction(const FunctionModelItem &functionItem,
+ const QString &signature, const QString &why);
+
+QString msgSkippingField(const VariableModelItem &field, const QString &className,
+ const QString &type);
+
+QString msgTypeNotDefined(const TypeEntryCPtr &entry);
+
+QString msgGlobalFunctionNotDefined(const FunctionTypeEntryCPtr &fte,
+ const QString &signature,
+ const QStringList &candidates);
+
+QString msgStrippingArgument(const FunctionModelItem &f, int i,
+ const QString &originalSignature,
+ const ArgumentModelItem &arg,
+ const QString &reason);
+
+QString msgEnumNotDefined(const EnumTypeEntryCPtr &t);
+
+QString msgUnknownBase(const AbstractMetaClassCPtr &metaClass,
+ const QString &baseClassName);
+
+QString msgBaseNotInTypeSystem(const AbstractMetaClassCPtr &metaClass,
+ const QString &baseClassName);
+
+QString msgArrayModificationFailed(const FunctionModelItem &functionItem,
+ const QString &className,
+ const QString &errorMessage);
+
+QString msgCannotResolveEntity(const QString &name, const QString &reason);
+
+QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason);
+
+QString msgUnableToTranslateType(const QString &t, const QString &why);
+
+QString msgUnableToTranslateType(const TypeInfo &typeInfo,
+ const QString &why);
+
+QString msgCannotFindTypeEntry(const QString &t);
+
+QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &smartPointerType);
+QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass,
+ const TypeInfo &info, const QString &what);
+QString msgIgnoringTemplateParameter(const QString &typeName,
+ const char *why);
+QString msgInvalidSmartPointerType(const TypeInfo &i);
+QString msgCannotFindSmartPointerInstantion(const TypeInfo &i);
+
+QString msgCannotTranslateTemplateArgument(int i,
+ const TypeInfo &typeInfo,
+ const QString &why);
+
+QString msgDisallowThread(const AbstractMetaFunction *f);
+
+QString msgNamespaceToBeExtendedNotFound(const QString &namespaceName, const QString &packageName);
+
+QString msgPropertyTypeParsingFailed(const QString &name, const QString &typeName,
+ const QString &why);
+QString msgPropertyExists(const QString &className, const QString &name);
+
+QString msgFunctionVisibilityModified(const AbstractMetaClassCPtr &c,
+ const AbstractMetaFunction *f);
+
+QString msgUsingMemberClassNotFound(const AbstractMetaClassCPtr &c,
+ const QString &baseClassName,
+ const QString &memberName);
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const char *what, const QString &name,
+ const QString &query = {});
+
+QString msgFallbackForDocumentation(const QString &fileName,
+ const char *what, const QString &name,
+ const QString &query = {});
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const AbstractMetaFunction *function,
+ const QString &query = {});
+
+QString msgFallbackForDocumentation(const QString &fileName,
+ const AbstractMetaFunction *function,
+ const QString &query = {});
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaEnum &e,
+ const QString &query = {});
+
+QString msgCannotFindDocumentation(const QString &fileName,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaField &f,
+ const QString &query);
+
+QString msgXpathDocModificationError(const DocModificationList& mods,
+ const QString &what);
+
+QString msgCannotOpenForReading(const QFile &f);
+
+QString msgCannotOpenForWriting(const QFile &f);
+
+QString msgWriteFailed(const QFile &f, qsizetype size);
+
+QString msgCannotUseEnumAsInt(const QString &name);
+
+QString msgCannotFindSmartPointerGetter(const SmartPointerTypeEntryCPtr &);
+
+QString msgCannotFindSmartPointerMethod(const SmartPointerTypeEntryCPtr &te, const QString &m);
+
+QString msgMethodNotFound(const AbstractMetaClassCPtr &klass, const QString &name);
+
+QString msgLeftOverArguments(const QString &remainingArgs, const QStringList &argV);
+
+QString msgInvalidVersion(const QString &package, const QString &version);
+
+QString msgCannotFindNamespaceToExtend(const QString &name,
+ const QString &extendsPackage);
+
+QString msgExtendingNamespaceRequiresPattern(const QString &name);
+
+QString msgInvalidRegularExpression(const QString &pattern, const QString &why);
+
+QString msgNoRootTypeSystemEntry();
+
+QString msgIncorrectlyNestedName(const QString &name);
+
+QString msgCannotFindView(const QString &viewedName, const QString &name);
+
+QString msgCannotFindSnippet(const QString &file, const QString &snippetLabel);
+QString msgSnippetError(const QString &context, const char *what);
+QString msgUnableToResolveTypedef(const QString &sourceType, const QString &sourceName);
+
+QString msgCyclicDependency(const QString &funcName, const QString &graphName,
+ const AbstractMetaFunctionCList &cyclic,
+ const AbstractMetaFunctionCList &involvedConversions);
+
+QString msgClassNotFound(const TypeEntryCPtr &t);
+
+QString msgEnclosingClassNotFound(const TypeEntryCPtr &t);
+
+QString msgUnknownOperator(const AbstractMetaFunction *func);
+
+QString msgWrongIndex(const char *varName, const QString &capture,
+ const AbstractMetaFunction *func);
+
+QString msgCannotFindType(const QString &type, const QString &variable,
+ const QString &why);
+
+QString msgCannotBuildMetaType(const QString &s);
+
+QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type,
+ const QString &why = QString());
+
+QString msgRejectReason(const TypeRejection &r, const QString &needle = QString());
+
+QString msgPureVirtualFunctionRemoved(const AbstractMetaFunction *f);
+
+QString msgUnknownTypeInArgumentTypeReplacement(const QString &typeReplaced,
+ const AbstractMetaFunction *f);
+
+QString msgDuplicateBuiltInTypeEntry(const QString &name);
+QString msgDuplicateTypeEntry(const QString &name);
+QString msgInvalidTargetLanguageApiName(const QString &name);
+
+QString msgUnknownCheckFunction(const TypeEntryCPtr &t);
+
+QString msgArgumentClassNotFound(const AbstractMetaFunctionCPtr &func,
+ const TypeEntryCPtr &t);
+
+QString msgMissingCustomConversion(const TypeEntryCPtr &t);
+
+QString msgUnknownArrayPointerConversion(const QString &s);
+
+QString msgMissingProjectFileMarker(const QString &name, const QByteArray &startMarker);
+
+QString msgInvalidLanguageLevel(const QString &l);
+
+QString msgCannotFindImage(const QString &href, const QString &context,
+ const QString &candidate);
+
+#endif // MESSAGES_H
diff --git a/sources/shiboken6/ApiExtractor/modifications.cpp b/sources/shiboken6/ApiExtractor/modifications.cpp
new file mode 100644
index 000000000..d876e8035
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/modifications.cpp
@@ -0,0 +1,672 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "modifications.h"
+#include "codesnip.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QRegularExpression>
+
+#include <algorithm>
+#include <limits>
+
+using namespace Qt::StringLiterals;
+
+// ---------------------- FieldModification
+
+class FieldModificationData : public QSharedData
+{
+public:
+ QString m_name;
+ QString m_renamedToName;
+ bool m_readable = true;
+ bool m_writable = true;
+ bool m_removed = false;
+ bool m_opaqueContainer = false;
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
+};
+
+FieldModification::FieldModification() : d(new FieldModificationData)
+{
+}
+
+FieldModification::FieldModification(const FieldModification &) = default;
+FieldModification &FieldModification::operator=(const FieldModification &) = default;
+FieldModification::FieldModification(FieldModification &&) noexcept = default;
+FieldModification &FieldModification::operator=(FieldModification &&) noexcept = default;
+FieldModification::~FieldModification() = default;
+
+QString FieldModification::name() const
+{
+ return d->m_name;
+}
+
+void FieldModification::setName(const QString &value)
+{
+ if (d->m_name != value)
+ d->m_name = value;
+}
+
+bool FieldModification::isRenameModifier() const
+{
+ return !d->m_renamedToName.isEmpty();
+}
+
+QString FieldModification::renamedToName() const
+{
+ return d->m_renamedToName;
+}
+
+void FieldModification::setRenamedToName(const QString &value)
+{
+ if (d->m_renamedToName != value)
+ d->m_renamedToName = value;
+}
+
+bool FieldModification::isReadable() const
+{
+ return d->m_readable;
+}
+
+void FieldModification::setReadable(bool e)
+{
+ if (d->m_readable != e)
+ d->m_readable = e;
+}
+
+bool FieldModification::isWritable() const
+{
+ return d->m_writable;
+}
+
+void FieldModification::setWritable(bool e)
+{
+ if (d->m_writable != e)
+ d->m_writable = e;
+}
+
+bool FieldModification::isRemoved() const
+{
+ return d->m_removed;
+}
+
+void FieldModification::setRemoved(bool r)
+{
+ if (d->m_removed != r)
+ d->m_removed = r;
+}
+
+bool FieldModification::isOpaqueContainer() const
+{
+ return d->m_opaqueContainer;
+}
+
+void FieldModification::setOpaqueContainer(bool r)
+{
+ if (d->m_opaqueContainer != r)
+ d->m_opaqueContainer = r;
+}
+
+TypeSystem::SnakeCase FieldModification::snakeCase() const
+{
+ return d->snakeCase;
+}
+
+void FieldModification::setSnakeCase(TypeSystem::SnakeCase s)
+{
+ if (d->snakeCase != s)
+ d->snakeCase = s;
+}
+
+// Remove the parameter names enclosed in '@' from an added function signature
+// so that it matches the C++ type signature.
+static QString removeParameterNames(QString signature)
+{
+ while (true) {
+ const auto ampPos = signature.indexOf(u'@');
+ if (ampPos == -1)
+ break;
+ const auto closingAmpPos = signature.indexOf(u'@', ampPos + 1);
+ if (closingAmpPos == -1)
+ break;
+ signature.remove(ampPos, closingAmpPos - ampPos + 1);
+ }
+ return signature;
+}
+
+DocModification::DocModification(const QString &xpath, const QString &signature) :
+ m_xpath(xpath), m_signature(removeParameterNames(signature))
+{
+}
+
+DocModification::DocModification(TypeSystem::DocModificationMode mode, const QString &signature) :
+ m_signature(removeParameterNames(signature)), m_mode(mode)
+{
+}
+
+void DocModification::setCode(const QString &code)
+{
+ m_code = CodeSnipAbstract::fixSpaces(code);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const ReferenceCount &r)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "ReferenceCount(" << r.varName << ", action=" << r.action << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const CodeSnip &s)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ const auto size = s.codeList.size();
+ d << "CodeSnip(language=" << s.language << ", position=" << s.position
+ << ", fragments[" << size << "]=";
+ for (qsizetype i = 0; i < size; ++i) {
+ const auto &f = s.codeList.at(i);
+ if (i)
+ d << ", ";
+ d << '#' << i << ' ';
+ if (!f.instance()) {
+ d << '"';
+ const QString &code = f.code();
+ const auto lines = QStringView{code}.split(u'\n');
+ for (qsizetype i = 0, size = lines.size(); i < size; ++i) {
+ if (i)
+ d << "\\n";
+ d << lines.at(i).trimmed();
+ }
+ d << '"';
+ } else {
+ d << "template=\"" << f.instance()->name() << '"';
+ }
+ }
+ d << ')';
+ return d;
+}
+
+class ArgumentModificationData : public QSharedData
+{
+public:
+ ArgumentModificationData(int idx = -1) : index(idx),
+ removedDefaultExpression(false), removed(false),
+ noNullPointers(false), resetAfterUse(false), array(false)
+ {}
+
+ QList<ReferenceCount> referenceCounts;
+ QString modified_type;
+ QString pyiType;
+ QString replacedDefaultExpression;
+ TypeSystem::Ownership m_targetOwnerShip = TypeSystem::UnspecifiedOwnership;
+ TypeSystem::Ownership m_nativeOwnerShip = TypeSystem::UnspecifiedOwnership;
+ CodeSnipList conversion_rules;
+ ArgumentOwner owner;
+ QString renamed_to;
+ int index = -1;
+ uint removedDefaultExpression : 1;
+ uint removed : 1;
+ uint noNullPointers : 1;
+ uint resetAfterUse : 1;
+ uint array : 1;
+};
+
+ArgumentModification::ArgumentModification() : d(new ArgumentModificationData)
+{
+}
+
+ArgumentModification::ArgumentModification(int idx) : d(new ArgumentModificationData(idx))
+{
+}
+
+ArgumentModification::ArgumentModification(const ArgumentModification &) = default;
+ArgumentModification &ArgumentModification::operator=(const ArgumentModification &) = default;
+ArgumentModification::ArgumentModification(ArgumentModification &&) noexcept = default;
+ArgumentModification &ArgumentModification::operator=(ArgumentModification &&) noexcept = default;
+ArgumentModification::~ArgumentModification() = default;
+
+const QString &ArgumentModification::modifiedType() const
+{
+ return d->modified_type;
+}
+
+void ArgumentModification::setModifiedType(const QString &value)
+{
+ if (d->modified_type != value)
+ d->modified_type = value;
+}
+
+bool ArgumentModification::isTypeModified() const
+{
+ return !d->modified_type.isEmpty();
+}
+
+QString ArgumentModification::pyiType() const
+{
+ return d->pyiType;
+}
+
+void ArgumentModification::setPyiType(const QString &value)
+{
+ if (d->pyiType != value)
+ d->pyiType = value;
+}
+
+QString ArgumentModification::replacedDefaultExpression() const
+{
+ return d->replacedDefaultExpression;
+}
+
+void ArgumentModification::setReplacedDefaultExpression(const QString &value)
+{
+ if (d->replacedDefaultExpression != value)
+ d->replacedDefaultExpression = value;
+}
+
+TypeSystem::Ownership ArgumentModification::targetOwnerShip() const
+{
+ return d->m_targetOwnerShip;
+}
+
+void ArgumentModification::setTargetOwnerShip(TypeSystem::Ownership o)
+{
+ if (o != d->m_targetOwnerShip)
+ d->m_targetOwnerShip = o;
+}
+
+TypeSystem::Ownership ArgumentModification::nativeOwnership() const
+{
+ return d->m_nativeOwnerShip;
+}
+
+void ArgumentModification::setNativeOwnership(TypeSystem::Ownership o)
+{
+ if (o != d->m_nativeOwnerShip)
+ d->m_nativeOwnerShip = o;
+}
+
+const CodeSnipList &ArgumentModification::conversionRules() const
+{
+ return d->conversion_rules;
+}
+
+CodeSnipList &ArgumentModification::conversionRules()
+{
+ return d->conversion_rules;
+}
+
+ArgumentOwner ArgumentModification::owner() const
+{
+ return d->owner;
+}
+
+void ArgumentModification::setOwner(const ArgumentOwner &value)
+{
+ d->owner = value;
+}
+
+QString ArgumentModification::renamedToName() const
+{
+ return d->renamed_to;
+}
+
+void ArgumentModification::setRenamedToName(const QString &value)
+{
+ if (d->renamed_to != value)
+ d->renamed_to = value;
+}
+
+int ArgumentModification::index() const
+{
+ return d->index;
+}
+
+void ArgumentModification::setIndex(int value)
+{
+ if (d->index != value)
+ d->index = value;
+}
+
+bool ArgumentModification::removedDefaultExpression() const
+{
+ return d->removedDefaultExpression;
+}
+
+void ArgumentModification::setRemovedDefaultExpression(const uint &value)
+{
+ if (d->removedDefaultExpression != value)
+ d->removedDefaultExpression = value;
+}
+
+bool ArgumentModification::isRemoved() const
+{
+ return d->removed;
+}
+
+void ArgumentModification::setRemoved(bool value)
+{
+ if (d->removed != value)
+ d->removed = value;
+}
+
+bool ArgumentModification::noNullPointers() const
+{
+ return d->noNullPointers;
+}
+
+void ArgumentModification::setNoNullPointers(bool value)
+{
+ if (d->noNullPointers != value)
+ d->noNullPointers = value;
+}
+
+bool ArgumentModification::resetAfterUse() const
+{
+ return d->resetAfterUse;
+}
+
+void ArgumentModification::setResetAfterUse(bool value)
+{
+ if (d->resetAfterUse != value)
+ d->resetAfterUse = value;
+}
+
+bool ArgumentModification::isArray() const
+{
+ return d->array;
+}
+
+void ArgumentModification::setArray(bool value)
+{
+ if (d->array != value)
+ d->array = value;
+}
+
+const QList<ReferenceCount> &ArgumentModification::referenceCounts() const
+{
+ return d->referenceCounts;
+}
+
+void ArgumentModification::addReferenceCount(const ReferenceCount &value)
+{
+ d->referenceCounts.append(value);
+}
+
+class FunctionModificationData : public QSharedData
+{
+public:
+ QString renamedToName;
+ FunctionModification::Modifiers modifiers;
+ CodeSnipList m_snips;
+ QList<ArgumentModification> m_argument_mods;
+ QString m_signature;
+ QString m_originalSignature;
+ QRegularExpression m_signaturePattern;
+ int m_overloadNumber = TypeSystem::OverloadNumberUnset;
+ bool removed = false;
+ TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified;
+ TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified;
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
+};
+
+FunctionModification::FunctionModification() : d(new FunctionModificationData)
+{
+}
+
+FunctionModification::FunctionModification(const FunctionModification &) = default;
+FunctionModification &FunctionModification::operator=(const FunctionModification &) = default;
+FunctionModification::FunctionModification(FunctionModification &&) noexcept = default;
+FunctionModification &FunctionModification::operator=(FunctionModification &&) noexcept = default;
+FunctionModification::~FunctionModification() = default;
+
+void FunctionModification::formatDebug(QDebug &debug) const
+{
+ if (d->m_signature.isEmpty())
+ debug << "pattern=\"" << d->m_signaturePattern.pattern();
+ else
+ debug << "signature=\"" << d->m_signature;
+ debug << "\", modifiers=" << d->modifiers;
+ if (d->removed)
+ debug << ", removed";
+ if (!d->renamedToName.isEmpty())
+ debug << ", renamedToName=\"" << d->renamedToName << '"';
+ if (d->m_allowThread != TypeSystem::AllowThread::Unspecified)
+ debug << ", allowThread=" << int(d->m_allowThread);
+ if (d->m_exceptionHandling != TypeSystem::ExceptionHandling::Unspecified)
+ debug << ", exceptionHandling=" << int(d->m_exceptionHandling);
+ if (!d->m_snips.isEmpty())
+ debug << ", snips=(" << d->m_snips << ')';
+ if (!d->m_argument_mods.isEmpty())
+ debug << ", argument_mods=(" << d->m_argument_mods << ')';
+}
+
+QString FunctionModification::renamedToName() const
+{
+ return d->renamedToName;
+}
+
+void FunctionModification::setRenamedToName(const QString &value)
+{
+ if (d->renamedToName != value)
+ d->renamedToName = value;
+}
+
+FunctionModification::Modifiers FunctionModification::modifiers() const
+{
+ return d->modifiers;
+}
+
+void FunctionModification::setModifiers(Modifiers m)
+{
+ if (d->modifiers != m)
+ d->modifiers = m;
+}
+
+void FunctionModification::setModifierFlag(FunctionModification::ModifierFlag f)
+{
+ auto newMods = d->modifiers | f;
+ if (d->modifiers != newMods)
+ d->modifiers = newMods;
+}
+
+void FunctionModification::clearModifierFlag(ModifierFlag f)
+{
+ auto newMods = d->modifiers & ~f;
+ if (d->modifiers != newMods)
+ d->modifiers = newMods;
+}
+
+bool FunctionModification::isRemoved() const
+{
+ return d->removed;
+}
+
+void FunctionModification::setRemoved(bool r)
+{
+ if (d->removed != r)
+ d->removed = r;
+}
+
+const QList<ArgumentModification> &FunctionModification::argument_mods() const
+{
+ return d->m_argument_mods;
+}
+
+QList<ArgumentModification> &FunctionModification::argument_mods()
+{
+ return d->m_argument_mods;
+}
+
+void FunctionModification::setArgument_mods(const QList<ArgumentModification> &argument_mods)
+{
+ d->m_argument_mods = argument_mods;
+}
+
+TypeSystem::SnakeCase FunctionModification::snakeCase() const
+{
+ return d->snakeCase;
+}
+
+void FunctionModification::setSnakeCase(TypeSystem::SnakeCase s)
+{
+ if (d->snakeCase != s)
+ d->snakeCase = s;
+}
+
+const CodeSnipList &FunctionModification::snips() const
+{
+ return d->m_snips;
+}
+
+CodeSnipList &FunctionModification::snips()
+{
+ return d->m_snips;
+}
+
+void FunctionModification::appendSnip(const CodeSnip &snip)
+{
+ d->m_snips.append(snip);
+}
+
+void FunctionModification::setSnips(const CodeSnipList &snips)
+{
+ d->m_snips = snips;
+}
+
+// ---------------------- FunctionModification
+FunctionModification::AllowThread FunctionModification::allowThread() const
+{
+ return d->m_allowThread;
+}
+
+void FunctionModification::setAllowThread(FunctionModification::AllowThread allow)
+{
+ if (d->m_allowThread != allow)
+ d->m_allowThread = allow;
+}
+
+bool FunctionModification::matches(const QStringList &functionSignatures) const
+{
+ if (!d->m_signature.isEmpty())
+ return functionSignatures.contains(d->m_signature);
+
+ for (const auto &s : functionSignatures) {
+ if (d->m_signaturePattern.match(s).hasMatch())
+ return true;
+ }
+ return false;
+}
+
+bool FunctionModification::setSignature(const QString &s, QString *errorMessage)
+{
+ if (s.startsWith(u'^')) {
+ d->m_signaturePattern.setPattern(s);
+ if (!d->m_signaturePattern.isValid()) {
+ if (errorMessage) {
+ *errorMessage = u"Invalid signature pattern: \""_s
+ + s + u"\": "_s + d->m_signaturePattern.errorString();
+ }
+ return false;
+ }
+ } else {
+ d->m_signature = s;
+ }
+ return true;
+}
+
+QString FunctionModification::signature() const
+{
+ return d->m_signature.isEmpty() ? d->m_signaturePattern.pattern() : d->m_signature;
+}
+
+void FunctionModification::setOriginalSignature(const QString &s)
+{
+ if (d->m_originalSignature != s)
+ d->m_originalSignature = s;
+}
+
+QString FunctionModification::originalSignature() const
+{
+ return d->m_originalSignature;
+}
+
+TypeSystem::ExceptionHandling FunctionModification::exceptionHandling() const
+{
+ return d->m_exceptionHandling;
+}
+
+void FunctionModification::setExceptionHandling(TypeSystem::ExceptionHandling e)
+{
+ if (d->m_exceptionHandling != e)
+ d->m_exceptionHandling = e;
+}
+
+int FunctionModification::overloadNumber() const
+{
+ return d->m_overloadNumber;
+}
+
+void FunctionModification::setOverloadNumber(int overloadNumber)
+{
+ d->m_overloadNumber = overloadNumber;
+}
+
+QDebug operator<<(QDebug d, const ArgumentOwner &a)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "ArgumentOwner(index=" << a.index << ", action=" << a.action << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const ArgumentModification &a)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "ArgumentModification(index=" << a.index();
+ if (a.removedDefaultExpression())
+ d << ", removedDefaultExpression";
+ if (a.isRemoved())
+ d << ", removed";
+ if (a.noNullPointers())
+ d << ", noNullPointers";
+ if (a.isArray())
+ d << ", array";
+ if (!a.referenceCounts().isEmpty())
+ d << ", referenceCounts=" << a.referenceCounts();
+ if (!a.modifiedType().isEmpty())
+ d << ", modified_type=\"" << a.modifiedType() << '"';
+ if (!a.replacedDefaultExpression().isEmpty())
+ d << ", replacedDefaultExpression=\"" << a.replacedDefaultExpression() << '"';
+ if (a.targetOwnerShip() != TypeSystem::UnspecifiedOwnership)
+ d << ", target ownership=" << a.targetOwnerShip();
+ if (a.nativeOwnership() != TypeSystem::UnspecifiedOwnership)
+ d << ", native ownership=" << a.nativeOwnership();
+ if (!a.renamedToName().isEmpty())
+ d << ", renamed_to=\"" << a.renamedToName() << '"';
+ const auto &rules = a.conversionRules();
+ if (!rules.isEmpty())
+ d << ", conversionRules[" << rules.size() << "]=" << rules;
+ d << ", owner=" << a.owner() << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const FunctionModification &fm)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "FunctionModification(";
+ fm.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/modifications.h b/sources/shiboken6/ApiExtractor/modifications.h
new file mode 100644
index 000000000..27a38f1aa
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/modifications.h
@@ -0,0 +1,342 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MODIFICATIONS_H
+#define MODIFICATIONS_H
+
+#include "typesystem_enums.h"
+#include "modifications_typedefs.h"
+
+#include <QtCore/QList>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+
+class ArgumentModificationData;
+class CodeSnip;
+class FunctionModificationData;
+class ModificationData;
+class FieldModificationData;
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+QT_END_NAMESPACE
+
+struct ReferenceCount
+{
+ enum Action { // 0x01 - 0xff
+ Add = 0x01,
+ AddAll = 0x02,
+ Remove = 0x04,
+ Set = 0x08,
+ Ignore = 0x10,
+
+ ActionsMask = 0xff,
+
+ Padding = 0xffffffff
+ };
+
+ QString varName;
+ Action action;
+};
+
+struct ArgumentOwner
+{
+ enum Action {
+ Invalid = 0x00,
+ Add = 0x01,
+ Remove = 0x02
+ };
+ enum {
+ InvalidIndex = -2,
+ ThisIndex = -1,
+ ReturnIndex = 0,
+ FirstArgumentIndex = 1
+ };
+
+ Action action = Invalid;
+ int index = InvalidIndex;
+};
+
+class ArgumentModification
+{
+public:
+ ArgumentModification();
+ explicit ArgumentModification(int idx);
+ ArgumentModification(const ArgumentModification &);
+ ArgumentModification &operator=(const ArgumentModification &);
+ ArgumentModification(ArgumentModification &&) noexcept;
+ ArgumentModification &operator=(ArgumentModification &&) noexcept;
+ ~ArgumentModification();
+
+ // Reference count flags for this argument
+ const QList<ReferenceCount> &referenceCounts() const;
+ void addReferenceCount(const ReferenceCount &value);
+
+ // The text given for the new type of the argument
+ const QString &modifiedType() const;
+ void setModifiedType(const QString &value);
+ bool isTypeModified() const;
+
+ QString pyiType() const;
+ void setPyiType(const QString &value);
+
+ // The text of the new default expression of the argument
+ QString replacedDefaultExpression() const;
+ void setReplacedDefaultExpression(const QString &value);
+
+ // The new definition of ownership for a specific argument
+
+ TypeSystem::Ownership targetOwnerShip() const;
+ void setTargetOwnerShip(TypeSystem::Ownership o);
+ TypeSystem::Ownership nativeOwnership() const;
+ void setNativeOwnership(TypeSystem::Ownership o);
+
+ // Different conversion rules
+ const QList<CodeSnip> &conversionRules() const;
+ QList<CodeSnip> &conversionRules();
+
+ // QObject parent(owner) of this argument
+ ArgumentOwner owner() const;
+ void setOwner(const ArgumentOwner &value);
+
+ // New name
+ QString renamedToName() const;
+ void setRenamedToName(const QString &value);
+
+ int index() const;
+ void setIndex(int value);
+
+ bool removedDefaultExpression() const;
+ void setRemovedDefaultExpression(const uint &value);
+
+ bool isRemoved() const;
+ void setRemoved(bool value);
+
+ bool noNullPointers() const;
+ void setNoNullPointers(bool value);
+
+ bool resetAfterUse() const;
+ void setResetAfterUse(bool value);
+
+ // consider "int*" to be "int[]"
+ bool isArray() const;
+ void setArray(bool value);
+
+private:
+ QSharedDataPointer<ArgumentModificationData> d;
+};
+
+class FunctionModification
+{
+public:
+ using AllowThread = TypeSystem::AllowThread;
+
+ FunctionModification();
+ FunctionModification(const FunctionModification &);
+ FunctionModification &operator=(const FunctionModification &);
+ FunctionModification(FunctionModification &&) noexcept;
+ FunctionModification &operator=(FunctionModification &&) noexcept;
+ ~FunctionModification();
+
+ enum ModifierFlag {
+ Private = 0x0001,
+ Protected = 0x0002,
+ Public = 0x0004,
+ AccessModifierMask = 0x000f,
+
+ Final = 0x0010,
+ NonFinal = 0x0020,
+ FinalMask = Final | NonFinal,
+
+ Readable = 0x0100,
+ Writable = 0x0200,
+
+ CodeInjection = 0x1000,
+ Rename = 0x2000,
+ Deprecated = 0x4000,
+ Undeprecated = 0x8000,
+ ReplaceExpression = 0x10000
+ };
+
+ Q_DECLARE_FLAGS(Modifiers, ModifierFlag);
+
+ QString renamedToName() const;
+ void setRenamedToName(const QString &value);
+
+ Modifiers modifiers() const;
+ void setModifiers(Modifiers m);
+ void setModifierFlag(ModifierFlag f);
+ void clearModifierFlag(ModifierFlag f);
+ bool isRemoved() const;
+ void setRemoved(bool r);
+
+ bool isAccessModifier() const
+ {
+ return (modifiers() & AccessModifierMask) != 0;
+ }
+ Modifiers accessModifier() const
+ {
+ return modifiers() & AccessModifierMask;
+ }
+ bool isPrivate() const
+ {
+ return accessModifier() == Private;
+ }
+ bool isProtected() const
+ {
+ return accessModifier() == Protected;
+ }
+ bool isPublic() const
+ {
+ return accessModifier() == Public;
+ }
+ bool isFinal() const
+ {
+ return modifiers().testFlag(Final);
+ }
+ bool isNonFinal() const
+ {
+ return modifiers().testFlag(NonFinal);
+ }
+
+ bool isDeprecated() const
+ {
+ return modifiers().testFlag(Deprecated);
+ }
+
+ bool isRenameModifier() const
+ {
+ return modifiers().testFlag(Rename);
+ }
+
+ bool isRemoveModifier() const { return isRemoved(); }
+
+
+
+ bool isCodeInjection() const
+ {
+ return modifiers().testFlag(CodeInjection);
+ }
+
+ AllowThread allowThread() const;
+ void setAllowThread(AllowThread allow);
+
+ bool matches(const QStringList &functionSignatures) const;
+
+ bool setSignature(const QString &s, QString *errorMessage = nullptr);
+ QString signature() const;
+
+ void setOriginalSignature(const QString &s);
+ QString originalSignature() const;
+
+ TypeSystem::ExceptionHandling exceptionHandling() const;
+ void setExceptionHandling(TypeSystem::ExceptionHandling e);
+
+ int overloadNumber() const;
+ void setOverloadNumber(int overloadNumber);
+
+ const QList<CodeSnip> &snips() const;
+ QList<CodeSnip> &snips();
+ void appendSnip(const CodeSnip &snip);
+ void setSnips(const QList<CodeSnip> &snips);
+
+ const QList<ArgumentModification> &argument_mods() const;
+ QList<ArgumentModification> &argument_mods();
+ void setArgument_mods(const QList<ArgumentModification> &argument_mods);
+
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase s);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ QSharedDataPointer<FunctionModificationData> d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(FunctionModification::Modifiers)
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const ReferenceCount &);
+QDebug operator<<(QDebug d, const CodeSnip &s);
+QDebug operator<<(QDebug d, const ArgumentOwner &a);
+QDebug operator<<(QDebug d, const ArgumentModification &a);
+QDebug operator<<(QDebug d, const FunctionModification &fm);
+#endif
+
+class FieldModification
+{
+public:
+ FieldModification();
+ FieldModification(const FieldModification &);
+ FieldModification &operator=(const FieldModification &);
+ FieldModification(FieldModification &&) noexcept;
+ FieldModification &operator=(FieldModification &&) noexcept;
+ ~FieldModification();
+
+ QString name() const;
+ void setName(const QString &value);
+
+ bool isRenameModifier() const;
+ QString renamedToName() const;
+ void setRenamedToName(const QString &value);
+
+ bool isReadable() const;
+ void setReadable(bool e);
+
+ bool isWritable() const;
+ void setWritable(bool e);
+
+ bool isRemoved() const;
+ void setRemoved(bool r);
+
+ bool isOpaqueContainer() const;
+ void setOpaqueContainer(bool r);
+
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase s);
+
+private:
+ QSharedDataPointer<FieldModificationData> d;
+};
+
+class DocModification
+{
+public:
+ DocModification() = default;
+ explicit DocModification(const QString& xpath, const QString& signature);
+ explicit DocModification(TypeSystem::DocModificationMode mode, const QString& signature);
+
+ void setCode(const QString& code);
+ void setCode(QStringView code) { setCode(code.toString()); }
+
+ QString code() const
+ {
+ return m_code;
+ }
+ QString xpath() const
+ {
+ return m_xpath;
+ }
+ QString signature() const
+ {
+ return m_signature;
+ }
+ TypeSystem::DocModificationMode mode() const
+ {
+ return m_mode;
+ }
+
+ TypeSystem::Language format() const { return m_format; }
+ void setFormat(TypeSystem::Language f) { m_format = f; }
+
+private:
+ QString m_code;
+ QString m_xpath;
+ QString m_signature;
+ TypeSystem::DocModificationMode m_mode = TypeSystem::DocModificationXPathReplace;
+ TypeSystem::Language m_format = TypeSystem::NativeCode;
+};
+
+#endif // MODIFICATIONS_H
diff --git a/sources/shiboken6/ApiExtractor/modifications_typedefs.h b/sources/shiboken6/ApiExtractor/modifications_typedefs.h
new file mode 100644
index 000000000..3b86c55d3
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/modifications_typedefs.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef MODIFICATIONS_TYPEDEFS_H
+#define MODIFICATIONS_TYPEDEFS_H
+
+#include <QtCore/QList>
+
+#include <memory>
+
+class CodeSnip;
+class DocModification;
+
+struct AddedFunction;
+class FieldModification;
+class FunctionModification;
+
+using AddedFunctionPtr = std::shared_ptr<AddedFunction>;
+using AddedFunctionList = QList<AddedFunctionPtr>;
+using CodeSnipList = QList<CodeSnip>;
+using DocModificationList = QList<DocModification>;
+using FieldModificationList = QList<FieldModification>;
+using FunctionModificationList = QList<FunctionModification>;
+
+#endif // MODIFICATIONS_TYPEDEFS_H
diff --git a/sources/shiboken6/ApiExtractor/namespacetypeentry.h b/sources/shiboken6/ApiExtractor/namespacetypeentry.h
new file mode 100644
index 000000000..6ffd38430
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/namespacetypeentry.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef NAMESPACETYPEENTRY_H
+#define NAMESPACETYPEENTRY_H
+
+#include "complextypeentry.h"
+
+class NamespaceTypeEntryPrivate;
+
+class NamespaceTypeEntry : public ComplexTypeEntry
+{
+public:
+ explicit NamespaceTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeEntry *clone() const override;
+
+ NamespaceTypeEntryCPtr extends() const;
+ void setExtends(const NamespaceTypeEntryCPtr &e);
+
+ const QRegularExpression &filePattern() const; // restrict files
+ void setFilePattern(const QRegularExpression &r);
+
+ bool hasPattern() const;
+
+ bool matchesFile(const QString &needle) const;
+
+ bool isVisible() const;
+ void setVisibility(TypeSystem::Visibility v);
+
+ // C++ 11 inline namespace, from code model
+ bool isInlineNamespace() const;
+ void setInlineNamespace(bool i);
+
+ static bool isVisibleScope(const TypeEntryCPtr &e);
+ static bool isVisibleScope(const TypeEntry *e);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+ // Whether to generate "using namespace" into wrapper
+ bool generateUsing() const;
+ void setGenerateUsing(bool generateUsing);
+
+protected:
+ explicit NamespaceTypeEntry(NamespaceTypeEntryPrivate *d);
+};
+
+#endif // NAMESPACETYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/objecttypeentry.h b/sources/shiboken6/ApiExtractor/objecttypeentry.h
new file mode 100644
index 000000000..da91e8ff4
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/objecttypeentry.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef OBJECTTYPEENTRY_H
+#define OBJECTTYPEENTRY_H
+
+#include "complextypeentry.h"
+
+class ObjectTypeEntry : public ComplexTypeEntry
+{
+public:
+ explicit ObjectTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit ObjectTypeEntry(ComplexTypeEntryPrivate *d);
+};
+
+#endif // OBJECTTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/optionsparser.cpp b/sources/shiboken6/ApiExtractor/optionsparser.cpp
new file mode 100644
index 000000000..f2e64c7e4
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/optionsparser.cpp
@@ -0,0 +1,232 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "optionsparser.h"
+#include "messages.h"
+#include "exception.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+
+using namespace Qt::StringLiterals;
+
+template <class Stream> void formatBoolOption(Stream &s, const BoolOption &bo)
+{
+ switch (bo.source) {
+ case OptionSource::CommandLine:
+ s << "--";
+ break;
+ case OptionSource::CommandLineSingleDash:
+ s << '-';
+ break;
+ default:
+ break;
+ }
+ s << bo.option;
+ if (bo.source == OptionSource::ProjectFile)
+ s << " (project)";
+}
+
+template <class Stream> void formatOptionValue(Stream &s, const OptionValue &ov)
+{
+ switch (ov.source) {
+ case OptionSource::CommandLine:
+ s << "--";
+ break;
+ case OptionSource::CommandLineSingleDash:
+ s << '-';
+ break;
+ default:
+ break;
+ }
+ s << ov.option << '=' << ov.value;
+ if (ov.source == OptionSource::ProjectFile)
+ s << " (project)";
+}
+
+QTextStream &operator<<(QTextStream &s, const BoolOption &bo)
+{
+ formatBoolOption(s, bo);
+ return s;
+}
+
+QTextStream &operator<<(QTextStream &s, const OptionValue &ov)
+{
+ formatOptionValue(s, ov);
+ return s;
+}
+
+QDebug operator<<(QDebug debug, const BoolOption &bo)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ formatBoolOption(debug, bo);
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const OptionValue &v)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ formatOptionValue(debug, v);
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const Options &v)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Options(";
+ if (!v.boolOptions.isEmpty())
+ debug << "bools=" << v.boolOptions;
+ if (!v.valueOptions.isEmpty())
+ debug << ", option values=" << v.valueOptions;
+ if (!v.positionalArguments.isEmpty())
+ debug << ", pos=" << v.positionalArguments;
+ debug << ')';
+ return debug;
+}
+
+QTextStream &operator<<(QTextStream &s, const OptionDescription &od)
+{
+ if (!od.name.startsWith(u'-'))
+ s << "--";
+ s << od.name;
+ if (od.description.isEmpty()) { // For formatting {{"-s", ""}, {"--short", "descr"}}
+ s << ", ";
+ } else {
+ s << '\n';
+ const auto lines = QStringView{od.description}.split(u'\n');
+ for (const auto &line : lines)
+ s << " " << line << '\n';
+ s << '\n';
+ }
+ return s;
+}
+
+QTextStream &operator<<(QTextStream &s, const OptionDescriptions &options)
+{
+ s.setFieldAlignment(QTextStream::AlignLeft);
+ for (const auto &od : options)
+ s << od;
+ return s;
+}
+
+OptionsParser::OptionsParser() noexcept = default;
+OptionsParser::~OptionsParser() = default;
+
+const QString &OptionsParser::pathSyntax()
+{
+ static const QString result =
+ u"<path>["_s + QDir::listSeparator() + u"<path>"_s
+ + QDir::listSeparator() + u"...]"_s;
+ return result;
+}
+
+bool OptionsParser::handleBoolOption(const QString &, OptionSource)
+{
+ return false;
+}
+
+bool OptionsParser::handleOption(const QString &, const QString &, OptionSource)
+{
+ return false;
+}
+
+void OptionsParser::process(Options *o)
+{
+ for (auto i = o->boolOptions.size() - 1; i >= 0; --i) {
+ const auto &opt = o->boolOptions.at(i);
+ if (handleBoolOption(opt.option, opt.source))
+ o->boolOptions.removeAt(i);
+ }
+ for (auto i = o->valueOptions.size() - 1; i >= 0; --i) {
+ const auto &opt = o->valueOptions.at(i);
+ if (handleOption(opt.option, opt.value, opt.source))
+ o->valueOptions.removeAt(i);
+ }
+}
+
+bool OptionsParserList::handleBoolOption(const QString &key, OptionSource source)
+{
+ for (const auto &p : std::as_const(m_parsers)) {
+ if (p->handleBoolOption(key, source))
+ return true;
+ }
+ return false;
+}
+
+bool OptionsParserList::handleOption(const QString &key, const QString &value, OptionSource source)
+{
+ for (const auto &p : std::as_const(m_parsers)) {
+ if (p->handleOption(key, value, source))
+ return true;
+ }
+ return false;
+}
+
+static void processOption(const QString &o, OptionSource source,
+ BoolOptions *bools, OptionValues *values)
+{
+ const auto equals = o.indexOf(u'=');
+ if (equals == -1) {
+ bools->append({o.trimmed(), source});
+ } else {
+ QString key = o.left(equals).trimmed();
+ QString value = o.mid(equals + 1).trimmed();
+ if (!value.isEmpty())
+ values->append({key, value, source});
+ }
+}
+
+static void readProjectFile(const QString &name, Options *o)
+{
+ const auto startMarker = "[generator-project]"_ba;
+
+ QFile file(name);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ throw Exception(msgCannotOpenForReading(file));
+
+ if (file.atEnd() || file.readLine().trimmed() != startMarker)
+ throw Exception(msgMissingProjectFileMarker(name, startMarker));
+
+ while (!file.atEnd()) {
+ const QByteArray lineB = file.readLine().trimmed();
+ if (!lineB.isEmpty() && !lineB.startsWith('#')) {
+ processOption(QString::fromUtf8(lineB), OptionSource::ProjectFile,
+ &o->boolOptions, &o->valueOptions);
+ }
+ }
+}
+
+void Options::setOptions(const QStringList &argv)
+{
+ const auto projectFileOption = "--project-file="_L1;
+ for (const auto &o : argv) {
+ if (o.startsWith(projectFileOption)) {
+ readProjectFile(o.sliced(projectFileOption.size()), this);
+ } else if (o.startsWith(u"--")) {
+ processOption(o.sliced(2), OptionSource::CommandLine,
+ &boolOptions, &valueOptions);
+ } else if (o.startsWith(u'-')) {
+ processOption(o.sliced(1), OptionSource::CommandLineSingleDash,
+ &boolOptions, &valueOptions);
+ } else {
+ positionalArguments.append(o);
+ }
+ }
+}
+
+QString Options::msgUnprocessedOptions() const
+{
+ QString result;
+ QTextStream str(&result);
+ for (const auto &b : boolOptions)
+ str << b << ' ';
+ for (const auto &v : valueOptions)
+ str << v << ' ';
+ return result.trimmed();
+}
diff --git a/sources/shiboken6/ApiExtractor/optionsparser.h b/sources/shiboken6/ApiExtractor/optionsparser.h
new file mode 100644
index 000000000..d5557dc15
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/optionsparser.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef OPTIONSPARSER_H
+#define OPTIONSPARSER_H
+
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+
+enum class OptionSource
+{
+ CommandLine, // "--option"
+ CommandLineSingleDash, // "-o"
+ ProjectFile
+};
+
+struct BoolOption
+{
+ QString option;
+ OptionSource source = OptionSource::CommandLine;
+};
+
+struct OptionValue // --option=value pair
+{
+ QString option;
+ QString value;
+ OptionSource source = OptionSource::CommandLine;
+};
+
+using BoolOptions = QList<BoolOption>;
+using OptionValues = QList<OptionValue>;
+
+struct Options // Options from command line and project file
+{
+ void setOptions(const QStringList &argv);
+ QString msgUnprocessedOptions() const;
+
+ BoolOptions boolOptions;
+ OptionValues valueOptions;
+ QStringList positionalArguments;
+};
+
+struct OptionDescription // For help formatting
+{
+ QString name;
+ QString description;
+};
+
+using OptionDescriptions = QList<OptionDescription>;
+
+QTextStream &operator<<(QTextStream &s, const BoolOption &bo);
+QTextStream &operator<<(QTextStream &s, const OptionValue &ov);
+QTextStream &operator<<(QTextStream &s, const OptionDescription &od);
+QTextStream &operator<<(QTextStream &s, const OptionDescriptions &options);
+
+class OptionsParser
+{
+public:
+ Q_DISABLE_COPY_MOVE(OptionsParser)
+
+ virtual ~OptionsParser();
+
+ // Return true to indicate the option was processed.
+ virtual bool handleBoolOption(const QString &key, OptionSource source);
+ virtual bool handleOption(const QString &key, const QString &value, OptionSource source);
+
+ void process(Options *);
+
+ static const QString &pathSyntax();
+
+protected:
+ OptionsParser() noexcept;
+};
+
+class OptionsParserList : public OptionsParser
+{
+public:
+ using OptionsParserPtr = std::shared_ptr<OptionsParser>;
+
+ void append(const OptionsParserPtr &parser) { m_parsers.append(parser); }
+ void clear() { m_parsers.clear(); }
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value, OptionSource source) override;
+
+private:
+ QList<OptionsParserPtr> m_parsers;
+};
+
+QDebug operator<<(QDebug debug, const BoolOption &bo);
+QDebug operator<<(QDebug debug, const OptionValue &v);
+QDebug operator<<(QDebug debug, const Options &v);
+
+#endif // OPTIONSPARSER_H
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel.cpp b/sources/shiboken6/ApiExtractor/parser/codemodel.cpp
new file mode 100644
index 000000000..259a706dc
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel.cpp
@@ -0,0 +1,1562 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2002-2005 Roberto Raggi <roberto@kdevelop.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+
+#include "codemodel.h"
+
+#include <sourcelocation.h>
+#include <debughelpers_p.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QRegularExpression>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+template <class T>
+static std::shared_ptr<T> findModelItem(const QList<std::shared_ptr<T> > &list,
+ QAnyStringView name)
+{
+ using ItemPtr = std::shared_ptr<T>;
+ auto pred = [name](const ItemPtr &item) { return item->name() == name; };
+ const auto it = std::find_if(list.cbegin(), list.cend(), pred);
+ return it != list.cend() ? *it : ItemPtr{};
+}
+
+// ---------------------------------------------------------------------------
+
+CodeModel::CodeModel() : m_globalNamespace(new _NamespaceModelItem(this))
+{
+}
+
+CodeModel::~CodeModel() = default;
+
+NamespaceModelItem CodeModel::globalNamespace() const
+{
+ return m_globalNamespace;
+}
+
+void CodeModel::addFile(const FileModelItem &item)
+{
+ m_files.append(item);
+}
+
+FileModelItem CodeModel::findFile(QAnyStringView name) const
+{
+ return findModelItem(m_files, name);
+}
+
+static CodeModelItem findRecursion(const ScopeModelItem &scope,
+ const QStringList &qualifiedName, int segment = 0)
+{
+ const QString &nameSegment = qualifiedName.at(segment);
+ if (segment == qualifiedName.size() - 1) { // Leaf item
+ if (ClassModelItem cs = scope->findClass(nameSegment))
+ return cs;
+ if (EnumModelItem es = scope->findEnum(nameSegment))
+ return es;
+ if (TypeDefModelItem tp = scope->findTypeDef(nameSegment))
+ return tp;
+ if (TemplateTypeAliasModelItem tta = scope->findTemplateTypeAlias(nameSegment))
+ return tta;
+ return {};
+ }
+ if (auto nestedClass = scope->findClass(nameSegment))
+ return findRecursion(nestedClass, qualifiedName, segment + 1);
+ if (auto namespaceItem = std::dynamic_pointer_cast<_NamespaceModelItem>(scope)) {
+ for (const auto &nestedNamespace : namespaceItem->namespaces()) {
+ if (nestedNamespace->name() == nameSegment) {
+ if (auto item = findRecursion(nestedNamespace, qualifiedName, segment + 1))
+ return item;
+ }
+ }
+ }
+ return {};
+}
+
+CodeModelItem CodeModel::findItem(const QStringList &qualifiedName, const ScopeModelItem &scope)
+{
+ return findRecursion(scope, qualifiedName);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<(QDebug d, Access a)
+{
+ QDebugStateSaver s(d);
+ d.noquote();
+ d.nospace();
+ switch (a) {
+ case Access::Public:
+ d << "public";
+ break;
+ case Access::Protected:
+ d << "protected";
+ break;
+ case Access::Private:
+ d << "private";
+ break;
+ }
+ return d;
+}
+
+QDebug operator<<(QDebug d, const CodeModel *m)
+{
+ QDebugStateSaver s(d);
+ d.noquote();
+ d.nospace();
+ d << "CodeModel(";
+ if (m) {
+ const NamespaceModelItem globalNamespaceP = m->globalNamespace();
+ if (globalNamespaceP)
+ globalNamespaceP->formatDebug(d);
+ } else {
+ d << '0';
+ }
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_CodeModelItem::_CodeModelItem(CodeModel *model, int kind)
+ : m_model(model),
+ m_kind(kind),
+ m_startLine(0),
+ m_startColumn(0),
+ m_endLine(0),
+ m_endColumn(0)
+{
+}
+
+_CodeModelItem::_CodeModelItem(CodeModel *model, const QString &name, int kind)
+ : m_model(model),
+ m_kind(kind),
+ m_startLine(0),
+ m_startColumn(0),
+ m_endLine(0),
+ m_endColumn(0),
+ m_name(name)
+{
+}
+
+_CodeModelItem::~_CodeModelItem() = default;
+
+int _CodeModelItem::kind() const
+{
+ return m_kind;
+}
+
+QStringList _CodeModelItem::qualifiedName() const
+{
+ QStringList q = scope();
+
+ if (!name().isEmpty())
+ q += name();
+
+ return q;
+}
+
+QString _CodeModelItem::name() const
+{
+ return m_name;
+}
+
+void _CodeModelItem::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QStringList _CodeModelItem::scope() const
+{
+ return m_scope;
+}
+
+void _CodeModelItem::setScope(const QStringList &scope)
+{
+ m_scope = scope;
+}
+
+QString _CodeModelItem::fileName() const
+{
+ return m_fileName;
+}
+
+void _CodeModelItem::setFileName(const QString &fileName)
+{
+ m_fileName = fileName;
+}
+
+FileModelItem _CodeModelItem::file() const
+{
+ return model()->findFile(fileName());
+}
+
+void _CodeModelItem::getStartPosition(int *line, int *column)
+{
+ *line = m_startLine;
+ *column = m_startColumn;
+}
+
+void _CodeModelItem::setStartPosition(int line, int column)
+{
+ m_startLine = line;
+ m_startColumn = column;
+}
+
+void _CodeModelItem::getEndPosition(int *line, int *column)
+{
+ *line = m_endLine;
+ *column = m_endColumn;
+}
+
+void _CodeModelItem::setEndPosition(int line, int column)
+{
+ m_endLine = line;
+ m_endColumn = column;
+}
+
+SourceLocation _CodeModelItem::sourceLocation() const
+{
+ return SourceLocation(m_fileName, m_startLine);
+}
+
+const _ScopeModelItem *_CodeModelItem::enclosingScope() const
+{
+ return m_enclosingScope;
+}
+
+void _CodeModelItem::setEnclosingScope(const _ScopeModelItem *s)
+{
+ m_enclosingScope = s;
+}
+
+_ScopeModelItem::_ScopeModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind)
+{
+}
+
+_ScopeModelItem::_ScopeModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _CodeModelItem::formatKind(QDebug &d, int k)
+{
+ switch (k) {
+ case Kind_Argument:
+ d << "ArgumentModelItem";
+ break;
+ case Kind_Class:
+ d << "ClassModelItem";
+ break;
+ case Kind_Enum:
+ d << "EnumModelItem";
+ break;
+ case Kind_Enumerator:
+ d << "EnumeratorModelItem";
+ break;
+ case Kind_File:
+ d << "FileModelItem";
+ break;
+ case Kind_Function:
+ d << "FunctionModelItem";
+ break;
+ case Kind_Member:
+ d << "MemberModelItem";
+ break;
+ case Kind_Namespace:
+ d << "NamespaceModelItem";
+ break;
+ case Kind_Variable:
+ d << "VariableModelItem";
+ break;
+ case Kind_Scope:
+ d << "ScopeModelItem";
+ break;
+ case Kind_TemplateParameter:
+ d << "TemplateParameter";
+ break;
+ case Kind_TypeDef:
+ d << "TypeDefModelItem";
+ break;
+ case Kind_TemplateTypeAlias:
+ d << "TemplateTypeAliasModelItem";
+ break;
+ default:
+ d << "CodeModelItem";
+ break;
+ }
+}
+
+void _CodeModelItem::formatDebug(QDebug &d) const
+{
+ d << "(\"" << name() << '"';
+ if (!m_scope.isEmpty()) {
+ d << ", scope=";
+ formatSequence(d, m_scope.cbegin(), m_scope.cend(), "::");
+ }
+ if (!m_fileName.isEmpty()) {
+ d << ", file=\"" << QDir::toNativeSeparators(m_fileName);
+ if (m_startLine > 0)
+ d << ':' << m_startLine;
+ d << '"';
+ }
+}
+
+QDebug operator<<(QDebug d, const _CodeModelItem *t)
+{
+ QDebugStateSaver s(d);
+ d.noquote();
+ d.nospace();
+ if (!t) {
+ d << "CodeModelItem(0)";
+ return d;
+ }
+ _CodeModelItem::formatKind(d, t->kind());
+ t->formatDebug(d);
+ switch (t->kind()) {
+ case _CodeModelItem::Kind_Class:
+ d << " /* class " << t->name() << " */";
+ break;
+ case _CodeModelItem::Kind_Namespace:
+ d << " /* namespace " << t->name() << " */";
+ break;
+ }
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_ClassModelItem::~_ClassModelItem() = default;
+
+TemplateParameterList _ClassModelItem::templateParameters() const
+{
+ return m_templateParameters;
+}
+
+void _ClassModelItem::setTemplateParameters(const TemplateParameterList &templateParameters)
+{
+ m_templateParameters = templateParameters;
+}
+
+bool _ClassModelItem::extendsClass(const QString &name) const
+{
+ for (const BaseClass &bc : m_baseClasses) {
+ if (bc.name == name)
+ return true;
+ }
+ return false;
+}
+
+_ClassModelItem::_ClassModelItem(CodeModel *model, int kind)
+ : _ScopeModelItem(model, kind)
+{
+}
+
+_ClassModelItem::_ClassModelItem(CodeModel *model, const QString &name, int kind)
+ : _ScopeModelItem(model, name, kind)
+{
+}
+
+const QList<_ClassModelItem::UsingMember> &_ClassModelItem::usingMembers() const
+{
+ return m_usingMembers;
+}
+
+void _ClassModelItem::addUsingMember(const QString &className,
+ const QString &memberName,
+ Access accessPolicy)
+{
+ m_usingMembers.append({className, memberName, accessPolicy});
+}
+
+void _ClassModelItem::setClassType(CodeModel::ClassType type)
+{
+ m_classType = type;
+}
+
+CodeModel::ClassType _ClassModelItem::classType() const
+{
+ return m_classType;
+}
+
+void _ClassModelItem::addPropertyDeclaration(const QString &propertyDeclaration)
+{
+ m_propertyDeclarations << propertyDeclaration;
+}
+
+bool _ClassModelItem::isEmpty() const
+{
+ return _ScopeModelItem::isEmpty() && m_propertyDeclarations.isEmpty();
+}
+
+bool _ClassModelItem::isTemplate() const
+{
+ return !m_templateParameters.isEmpty();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+template <class List>
+static void formatModelItemList(QDebug &d, const char *prefix, const List &l,
+ const char *separator = ", ")
+{
+ if (const auto size = l.size()) {
+ d << prefix << '[' << size << "](";
+ for (qsizetype i = 0; i < size; ++i) {
+ if (i)
+ d << separator;
+ l.at(i)->formatDebug(d);
+ }
+ d << ')';
+ }
+}
+
+void _ClassModelItem::formatDebug(QDebug &d) const
+{
+ _ScopeModelItem::formatDebug(d);
+ if (!m_baseClasses.isEmpty()) {
+ if (m_final)
+ d << " [final]";
+ d << ", inherits=";
+ d << ", inherits=";
+ for (qsizetype i = 0, size = m_baseClasses.size(); i < size; ++i) {
+ if (i)
+ d << ", ";
+ d << m_baseClasses.at(i).name << " (" << m_baseClasses.at(i).accessPolicy << ')';
+ }
+ }
+ for (const auto &im : m_usingMembers)
+ d << ", using " << im.className << "::" << im.memberName
+ << " (" << im.access << ')';
+ formatModelItemList(d, ", templateParameters=", m_templateParameters);
+ formatScopeItemsDebug(d);
+ if (!m_propertyDeclarations.isEmpty())
+ d << ", Properties=" << m_propertyDeclarations;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+FunctionModelItem _ScopeModelItem::declaredFunction(const FunctionModelItem &item)
+{
+ for (const FunctionModelItem &fun : std::as_const(m_functions)) {
+ if (fun->name() == item->name() && fun->isSimilar(item))
+ return fun;
+
+ }
+ return {};
+}
+
+_ScopeModelItem::~_ScopeModelItem() = default;
+
+void _ScopeModelItem::addEnumsDeclaration(const QString &enumsDeclaration)
+{
+ m_enumsDeclarations << enumsDeclaration;
+}
+
+void _ScopeModelItem::addClass(const ClassModelItem &item)
+{
+ m_classes.append(item);
+ item->setEnclosingScope(this);
+}
+
+void _ScopeModelItem::addFunction(const FunctionModelItem &item)
+{
+ m_functions.append(item);
+ item->setEnclosingScope(this);
+}
+
+void _ScopeModelItem::addVariable(const VariableModelItem &item)
+{
+ m_variables.append(item);
+ item->setEnclosingScope(this);
+}
+
+void _ScopeModelItem::addTypeDef(const TypeDefModelItem &item)
+{
+ m_typeDefs.append(item);
+ item->setEnclosingScope(this);
+}
+
+void _ScopeModelItem::addTemplateTypeAlias(const TemplateTypeAliasModelItem &item)
+{
+ m_templateTypeAliases.append(item);
+ item->setEnclosingScope(this);
+}
+
+qsizetype _ScopeModelItem::indexOfEnum(const QString &name) const
+{
+ for (qsizetype i = 0, size = m_enums.size(); i < size; ++i) {
+ if (m_enums.at(i)->name() == name)
+ return i;
+ }
+ return -1;
+}
+
+void _ScopeModelItem::addEnum(const EnumModelItem &item)
+{
+ item->setEnclosingScope(this);
+ // A forward declaration of an enum ("enum class Foo;") is undistinguishable
+ // from an enum without values ("enum class QCborTag {}"), so, add all
+ // enums and replace existing ones without values by ones with values.
+ const int index = indexOfEnum(item->name());
+ if (index >= 0) {
+ if (item->hasValues() && !m_enums.at(index)->hasValues())
+ m_enums[index] = item;
+ return;
+ }
+ m_enums.append(item);
+}
+
+void _ScopeModelItem::appendScope(const _ScopeModelItem &other)
+{
+ m_classes += other.m_classes;
+ m_enums += other.m_enums;
+ m_typeDefs += other.m_typeDefs;
+ m_templateTypeAliases += other.m_templateTypeAliases;
+ m_variables += other.m_variables;
+ m_functions += other.m_functions;
+ m_enumsDeclarations += other.m_enumsDeclarations;
+}
+
+bool _ScopeModelItem::isEmpty() const
+{
+ return m_classes.isEmpty() && m_enums.isEmpty()
+ && m_typeDefs.isEmpty() && m_templateTypeAliases.isEmpty()
+ && m_variables.isEmpty() && m_functions.isEmpty()
+ && m_enumsDeclarations.isEmpty();
+}
+
+/* This function removes MSVC export declarations of non-type template
+ * specializations (see below code from photon.h) for which
+ * clang_isCursorDefinition() returns true, causing them to be added as
+ * definitions of empty classes shadowing the template definition depending
+ * on QHash seed values.
+
+template <int N> class Tpl
+{
+public:
+...
+};
+
+#ifdef WIN32
+template class LIBSAMPLE_EXPORT Tpl<54>;
+*/
+void _ScopeModelItem::purgeClassDeclarations()
+{
+ for (auto i = m_classes.size() - 1; i >= 0; --i) {
+ auto klass = m_classes.at(i);
+ // For an empty class, check if there is a matching template
+ // definition, and remove it if this is the case.
+ if (!klass->isTemplate() && klass->isEmpty()) {
+ const QString definitionPrefix = klass->name() + u'<';
+ const bool definitionFound =
+ std::any_of(m_classes.cbegin(), m_classes.cend(),
+ [definitionPrefix] (const ClassModelItem &c) {
+ return c->isTemplate() && !c->isEmpty()
+ && c->name().startsWith(definitionPrefix);
+ });
+ if (definitionFound)
+ m_classes.removeAt(i);
+ }
+ }
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+template <class Hash>
+static void formatScopeHash(QDebug &d, const char *prefix, const Hash &h,
+ const char *separator = ", ",
+ bool trailingNewLine = false)
+{
+ if (!h.isEmpty()) {
+ d << prefix << '[' << h.size() << "](";
+ const auto begin = h.cbegin();
+ for (auto it = begin, end = h.cend(); it != end; ++it) { // Omit the names as they are repeated
+ if (it != begin)
+ d << separator;
+ d << it.value().data();
+ }
+ d << ')';
+ if (trailingNewLine)
+ d << '\n';
+ }
+}
+
+template <class List>
+static void formatScopeList(QDebug &d, const char *prefix, const List &l,
+ const char *separator = ", ",
+ bool trailingNewLine = false)
+{
+ if (!l.isEmpty()) {
+ d << prefix << '[' << l.size() << "](";
+ formatPtrSequence(d, l.begin(), l.end(), separator);
+ d << ')';
+ if (trailingNewLine)
+ d << '\n';
+ }
+}
+
+void _ScopeModelItem::formatScopeItemsDebug(QDebug &d) const
+{
+ formatScopeList(d, ", classes=", m_classes, "\n", true);
+ formatScopeList(d, ", enums=", m_enums, "\n", true);
+ formatScopeList(d, ", aliases=", m_typeDefs, "\n", true);
+ formatScopeList(d, ", template type aliases=", m_templateTypeAliases, "\n", true);
+ formatScopeList(d, ", functions=", m_functions, "\n", true);
+ formatScopeList(d, ", variables=", m_variables);
+}
+
+void _ScopeModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ formatScopeItemsDebug(d);
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// Predicate to match a non-template class name against the class list.
+// "Vector" should match "Vector" as well as "Vector<T>" (as seen for methods
+// from within the class "Vector").
+static bool matchClassNameNonTemplatePart(const ClassModelItem &item, const QString &name)
+{
+ const QString &itemName = item->name();
+ if (!itemName.startsWith(name))
+ return false;
+ return itemName.size() == name.size() || itemName.at(name.size()) == u'<';
+}
+
+ClassModelItem _ScopeModelItem::findClass(const QString &name) const
+{
+ // A fully qualified template is matched by name only
+ const ClassList::const_iterator it = name.contains(u'<')
+ ? std::find_if(m_classes.begin(), m_classes.end(),
+ [&name](const ClassModelItem &item) {
+ return item->name() == name; })
+ : std::find_if(m_classes.begin(), m_classes.end(),
+ [&name](const ClassModelItem &item) {
+ return matchClassNameNonTemplatePart(item, name); });
+ return it != m_classes.end() ? *it : ClassModelItem();
+}
+
+VariableModelItem _ScopeModelItem::findVariable(QAnyStringView name) const
+{
+ return findModelItem(m_variables, name);
+}
+
+TypeDefModelItem _ScopeModelItem::findTypeDef(QAnyStringView name) const
+{
+ return findModelItem(m_typeDefs, name);
+}
+
+TemplateTypeAliasModelItem _ScopeModelItem::findTemplateTypeAlias(QAnyStringView name) const
+{
+ return findModelItem(m_templateTypeAliases, name);
+}
+
+EnumModelItem _ScopeModelItem::findEnum(QAnyStringView name) const
+{
+ return findModelItem(m_enums, name);
+}
+
+_ScopeModelItem::FindEnumByValueReturn
+ _ScopeModelItem::findEnumByValueHelper(QStringView fullValue,
+ QStringView enumValue) const
+{
+ const bool unqualified = fullValue.size() == enumValue.size();
+ QString scopePrefix = scope().join(u"::");
+ if (!scopePrefix.isEmpty())
+ scopePrefix += u"::"_s;
+ scopePrefix += name() + u"::"_s;
+
+ for (const auto &e : m_enums) {
+ const auto index = e->indexOfValue(enumValue);
+ if (index != -1) {
+ QString fullyQualifiedName = scopePrefix;
+ if (e->enumKind() != AnonymousEnum)
+ fullyQualifiedName += e->name() + u"::"_s;
+ fullyQualifiedName += e->enumerators().at(index)->name();
+ if (unqualified || fullyQualifiedName.endsWith(fullValue))
+ return {e, fullyQualifiedName};
+ // For standard enums, check the name without enum name
+ if (e->enumKind() == CEnum) {
+ const QString qualifiedName =
+ scopePrefix + e->enumerators().at(index)->name();
+ if (qualifiedName.endsWith(fullValue))
+ return {e, fullyQualifiedName};
+ }
+ }
+ }
+
+ return {};
+}
+
+// Helper to recursively find the scope of an enum value
+_ScopeModelItem::FindEnumByValueReturn
+ _ScopeModelItem::findEnumByValueRecursion(const _ScopeModelItem *scope,
+ QStringView fullValue,
+ QStringView enumValue,
+ bool searchSiblingNamespaces)
+{
+ if (const auto e = scope->findEnumByValueHelper(fullValue, enumValue))
+ return e;
+
+ if (auto *enclosingScope = scope->enclosingScope()) {
+ // The enclosing scope may have several sibling namespaces of that name.
+ if (searchSiblingNamespaces && scope->kind() == Kind_Namespace) {
+ if (auto *enclosingNamespace = dynamic_cast<const _NamespaceModelItem *>(enclosingScope)) {
+ for (const auto &sibling : enclosingNamespace->namespaces()) {
+ if (sibling.get() != scope && sibling->name() == scope->name()) {
+ if (const auto e = findEnumByValueRecursion(sibling.get(),
+ fullValue, enumValue, false)) {
+ return e;
+ }
+ }
+ }
+ }
+ }
+
+ if (const auto e = findEnumByValueRecursion(enclosingScope, fullValue, enumValue))
+ return e;
+ }
+
+ // PYSIDE-331: We need to also search the base classes.
+ if (auto *classItem = dynamic_cast<const _ClassModelItem *>(scope)) {
+ for (const auto &base : classItem->baseClasses()) {
+ if (base.klass) {
+ auto *c = base.klass.get();
+ if (const auto e = findEnumByValueRecursion(c, fullValue, enumValue))
+ return e;
+ }
+ }
+ }
+
+ return {};
+}
+
+_ScopeModelItem::FindEnumByValueReturn
+ _ScopeModelItem::findEnumByValue(QStringView value) const
+{
+ const auto lastQualifier = value.lastIndexOf(u"::");
+ const auto enumValue = lastQualifier == -1
+ ? value : value.mid(lastQualifier + 2);
+ return findEnumByValueRecursion(this, value, enumValue);
+}
+
+FunctionList _ScopeModelItem::findFunctions(QAnyStringView name) const
+{
+ FunctionList result;
+ for (const FunctionModelItem &func : m_functions) {
+ if (func->name() == name)
+ result.append(func);
+ }
+ return result;
+}
+
+// ---------------------------------------------------------------------------
+_NamespaceModelItem::_NamespaceModelItem(CodeModel *model, int kind)
+ : _ScopeModelItem(model, kind)
+{
+}
+
+_NamespaceModelItem::_NamespaceModelItem(CodeModel *model, const QString &name, int kind)
+ : _ScopeModelItem(model, name, kind)
+{
+}
+
+_NamespaceModelItem::~_NamespaceModelItem() = default;
+
+void _NamespaceModelItem::addNamespace(NamespaceModelItem item)
+{
+ item->setEnclosingScope(this);
+ m_namespaces.append(item);
+}
+
+NamespaceModelItem _NamespaceModelItem::findNamespace(QAnyStringView name) const
+{
+ return findModelItem(m_namespaces, name);
+}
+
+_FileModelItem::~_FileModelItem() = default;
+
+void _NamespaceModelItem::appendNamespace(const _NamespaceModelItem &other)
+{
+ appendScope(other);
+ m_namespaces += other.m_namespaces;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _NamespaceModelItem::formatDebug(QDebug &d) const
+{
+ _ScopeModelItem::formatDebug(d);
+ switch (m_type) {
+ case NamespaceType::Default:
+ break;
+ case NamespaceType::Anonymous:
+ d << ", anonymous";
+ break;
+ case NamespaceType::Inline:
+ d << ", inline";
+ break;
+ }
+ formatScopeList(d, ", namespaces=", m_namespaces);
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_ArgumentModelItem::_ArgumentModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind)
+{
+}
+
+_ArgumentModelItem::_ArgumentModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind)
+{
+}
+
+_ArgumentModelItem::~_ArgumentModelItem() = default;
+
+TypeInfo _ArgumentModelItem::type() const
+{
+ return m_type;
+}
+
+void _ArgumentModelItem::setType(const TypeInfo &type)
+{
+ m_type = type;
+}
+
+bool _ArgumentModelItem::defaultValue() const
+{
+ return m_defaultValue;
+}
+
+void _ArgumentModelItem::setDefaultValue(bool defaultValue)
+{
+ m_defaultValue = defaultValue;
+}
+
+bool _ArgumentModelItem::scopeResolution() const
+{
+ return m_scopeResolution;
+}
+
+void _ArgumentModelItem::setScopeResolution(bool v)
+{
+ m_scopeResolution = v;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _ArgumentModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ d << ", type=" << m_type;
+ if (m_scopeResolution)
+ d << ", [m_scope resolution]";
+ if (m_defaultValue)
+ d << ", defaultValue=\"" << m_defaultValueExpression << '"';
+}
+#endif // !QT_NO_DEBUG_STREAM
+// ---------------------------------------------------------------------------
+_FunctionModelItem::~_FunctionModelItem() = default;
+
+bool _FunctionModelItem::isSimilar(const FunctionModelItem &other) const
+{
+ if (name() != other->name())
+ return false;
+
+ if (isConstant() != other->isConstant())
+ return false;
+
+ if (isVariadics() != other->isVariadics())
+ return false;
+
+ if (arguments().size() != other->arguments().size())
+ return false;
+
+ // ### check the template parameters
+
+ for (qsizetype i = 0; i < arguments().size(); ++i) {
+ ArgumentModelItem arg1 = arguments().at(i);
+ ArgumentModelItem arg2 = other->arguments().at(i);
+
+ if (arg1->type() != arg2->type())
+ return false;
+ }
+
+ return true;
+}
+
+_FunctionModelItem::_FunctionModelItem(CodeModel *model, int kind)
+ : _MemberModelItem(model, kind), m_flags(0)
+{
+}
+
+_FunctionModelItem::_FunctionModelItem(CodeModel *model, const QString &name, int kind)
+ : _MemberModelItem(model, name, kind), m_flags(0)
+{
+}
+
+ArgumentList _FunctionModelItem::arguments() const
+{
+ return m_arguments;
+}
+
+void _FunctionModelItem::addArgument(const ArgumentModelItem& item)
+{
+ m_arguments.append(item);
+}
+
+CodeModel::FunctionType _FunctionModelItem::functionType() const
+{
+ return m_functionType;
+}
+
+void _FunctionModelItem::setFunctionType(CodeModel::FunctionType functionType)
+{
+ m_functionType = functionType;
+}
+
+bool _FunctionModelItem::isVariadics() const
+{
+ return m_isVariadics;
+}
+
+void _FunctionModelItem::setVariadics(bool isVariadics)
+{
+ m_isVariadics = isVariadics;
+}
+
+bool _FunctionModelItem::scopeResolution() const
+{
+ return m_scopeResolution;
+}
+
+void _FunctionModelItem::setScopeResolution(bool v)
+{
+ m_scopeResolution = v;
+}
+
+bool _FunctionModelItem::isDefaultConstructor() const
+{
+ return m_functionType == CodeModel::Constructor
+ && (m_arguments.isEmpty() || m_arguments.constFirst()->defaultValue());
+}
+
+bool _FunctionModelItem::isSpaceshipOperator() const
+{
+ return m_functionType == CodeModel::ComparisonOperator
+ && name() == u"operator<=>";
+}
+
+bool _FunctionModelItem::isNoExcept() const
+{
+ return m_exceptionSpecification == ExceptionSpecification::NoExcept;
+}
+
+bool _FunctionModelItem::isOperator() const
+{
+ bool result = false;
+ switch (m_functionType) {
+ case CodeModel::CallOperator:
+ case CodeModel::ConversionOperator:
+ case CodeModel::DereferenceOperator:
+ case CodeModel::ReferenceOperator:
+ case CodeModel::ArrowOperator:
+ case CodeModel::ArithmeticOperator:
+ case CodeModel::IncrementOperator:
+ case CodeModel::DecrementOperator:
+ case CodeModel::BitwiseOperator:
+ case CodeModel::LogicalOperator:
+ case CodeModel::ShiftOperator:
+ case CodeModel::SubscriptOperator:
+ case CodeModel::ComparisonOperator:
+ result = true;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+ExceptionSpecification _FunctionModelItem::exceptionSpecification() const
+{
+ return m_exceptionSpecification;
+}
+
+void _FunctionModelItem::setExceptionSpecification(ExceptionSpecification e)
+{
+ m_exceptionSpecification = e;
+}
+
+bool _FunctionModelItem::isDeleted() const
+{
+ return m_isDeleted;
+}
+
+void _FunctionModelItem::setDeleted(bool d)
+{
+ m_isDeleted = d;
+}
+
+bool _FunctionModelItem::isInline() const
+{
+ return m_isInline;
+}
+
+void _FunctionModelItem::setInline(bool isInline)
+{
+ m_isInline = isInline;
+}
+
+bool _FunctionModelItem::isHiddenFriend() const
+{
+ return m_isHiddenFriend;
+}
+
+void _FunctionModelItem::setHiddenFriend(bool f)
+{
+ m_isHiddenFriend = f;
+}
+
+QString _FunctionModelItem::typeSystemSignature() const // For dumping out type system files
+{
+ QString result;
+ QTextStream str(&result);
+ str << name() << '(';
+ for (qsizetype a = 0, size = m_arguments.size(); a < size; ++a) {
+ if (a)
+ str << ',';
+ m_arguments.at(a)->type().formatTypeSystemSignature(str);
+ }
+ str << ')';
+ return result;
+}
+
+using NameFunctionTypeHash = QHash<QStringView, CodeModel::FunctionType>;
+
+static const NameFunctionTypeHash &nameToOperatorFunction()
+{
+ static const NameFunctionTypeHash result = {
+ {u"operator=", CodeModel::AssignmentOperator},
+ {u"operator+", CodeModel::ArithmeticOperator},
+ {u"operator+=", CodeModel::ArithmeticOperator},
+ {u"operator-", CodeModel::ArithmeticOperator},
+ {u"operator-=", CodeModel::ArithmeticOperator},
+ {u"operator*", CodeModel::ArithmeticOperator},
+ {u"operator*=", CodeModel::ArithmeticOperator},
+ {u"operator/", CodeModel::ArithmeticOperator},
+ {u"operator/=", CodeModel::ArithmeticOperator},
+ {u"operator%", CodeModel::ArithmeticOperator},
+ {u"operator%=", CodeModel::ArithmeticOperator},
+ {u"operator++", CodeModel::IncrementOperator},
+ {u"operator--", CodeModel::DecrementOperator},
+ {u"operator&", CodeModel::BitwiseOperator},
+ {u"operator&=", CodeModel::BitwiseOperator},
+ {u"operator|", CodeModel::BitwiseOperator},
+ {u"operator|=", CodeModel::BitwiseOperator},
+ {u"operator^", CodeModel::BitwiseOperator},
+ {u"operator^=", CodeModel::BitwiseOperator},
+ {u"operator~", CodeModel::BitwiseOperator},
+ {u"operator<<", CodeModel::ShiftOperator},
+ {u"operator<<=", CodeModel::ShiftOperator},
+ {u"operator>>", CodeModel::ShiftOperator},
+ {u"operator>>=", CodeModel::ShiftOperator},
+ {u"operator<", CodeModel::ComparisonOperator},
+ {u"operator<=", CodeModel::ComparisonOperator},
+ {u"operator>", CodeModel::ComparisonOperator},
+ {u"operator>=", CodeModel::ComparisonOperator},
+ {u"operator==", CodeModel::ComparisonOperator},
+ {u"operator!=", CodeModel::ComparisonOperator},
+ {u"operator<=>", CodeModel::ComparisonOperator},
+ {u"operator!", CodeModel::LogicalOperator},
+ {u"operator&&", CodeModel::LogicalOperator},
+ {u"operator||", CodeModel::LogicalOperator},
+ {u"operator[]", CodeModel::SubscriptOperator},
+ {u"operator()", CodeModel::CallOperator}, // Can be void
+ {u"operator->", CodeModel::ArrowOperator}
+ };
+ return result;
+}
+
+std::optional<CodeModel::FunctionType> _FunctionModelItem::functionTypeFromName(QStringView name)
+{
+ const auto it = nameToOperatorFunction().constFind(name);
+ if (it != nameToOperatorFunction().constEnd())
+ return it.value();
+ // This check is only for added functions. Clang detects this
+ // by cursor type CXCursor_ConversionFunction.
+ if (name.startsWith(u"operator "))
+ return CodeModel::ConversionOperator;
+ return {};
+}
+
+// Check for operators, etc. unless it is a specific type like a constructor
+CodeModel::FunctionType _FunctionModelItem::_determineTypeHelper() const
+{
+ switch (m_functionType) {
+ case CodeModel::Constructor:
+ case CodeModel::CopyConstructor:
+ case CodeModel::MoveConstructor:
+ case CodeModel::Destructor:
+ case CodeModel::Signal:
+ case CodeModel::Slot:
+ return m_functionType; // nothing to do here
+ default:
+ break;
+ }
+ const QString &functionName = name();
+ const auto newTypeOpt = _FunctionModelItem::functionTypeFromName(functionName);
+ if (!newTypeOpt.has_value())
+ return m_functionType;
+
+ auto newType = newTypeOpt.value();
+ // It's some sort of dereference operator?!
+ if (m_arguments.isEmpty()) {
+ switch (newType) {
+ case CodeModel::ArithmeticOperator:
+ if (functionName == u"operator*")
+ return CodeModel::DereferenceOperator;
+ break;
+ case CodeModel::BitwiseOperator:
+ if (functionName == u"operator&")
+ return CodeModel::ReferenceOperator;
+ break;
+ default:
+ break;
+ }
+ }
+ return newType;
+}
+
+void _FunctionModelItem::_determineType()
+{
+ m_functionType = _determineTypeHelper();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _FunctionModelItem::formatDebug(QDebug &d) const
+{
+ _MemberModelItem::formatDebug(d);
+ d << ", type=" << m_functionType << ", exspec=" << int(m_exceptionSpecification);
+ if (m_isDeleted)
+ d << " [deleted!]";
+ if (m_isInline)
+ d << " [inline]";
+ if (m_attributes.testFlag(FunctionAttribute::Virtual))
+ d << " [virtual]";
+ if (m_attributes.testFlag(FunctionAttribute::Override))
+ d << " [override]";
+ if (m_attributes.testFlag(FunctionAttribute::Deprecated))
+ d << " [deprecated]";
+ if (m_attributes.testFlag(FunctionAttribute::Final))
+ d << " [final]";
+ if (m_attributes.testFlag(FunctionAttribute::Abstract))
+ d << " [abstract]";
+ if (m_attributes.testFlag(FunctionAttribute::Explicit))
+ d << " [explicit]";
+ if (m_isInvokable)
+ d << " [invokable]";
+ if (m_scopeResolution)
+ d << " [scope resolution]";
+ formatModelItemList(d, ", arguments=", m_arguments);
+ if (m_isVariadics)
+ d << ",...";
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_TypeDefModelItem::_TypeDefModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind)
+{
+}
+
+_TypeDefModelItem::_TypeDefModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind)
+{
+}
+
+TypeInfo _TypeDefModelItem::type() const
+{
+ return m_type;
+}
+
+void _TypeDefModelItem::setType(const TypeInfo &type)
+{
+ m_type = type;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _TypeDefModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ d << ", type=" << m_type;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+
+_TemplateTypeAliasModelItem::_TemplateTypeAliasModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind) {}
+
+_TemplateTypeAliasModelItem::_TemplateTypeAliasModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind) {}
+
+TemplateParameterList _TemplateTypeAliasModelItem::templateParameters() const
+{
+ return m_templateParameters;
+}
+
+void _TemplateTypeAliasModelItem::addTemplateParameter(const TemplateParameterModelItem &templateParameter)
+{
+ m_templateParameters.append(templateParameter);
+}
+
+TypeInfo _TemplateTypeAliasModelItem::type() const
+{
+ return m_type;
+}
+
+void _TemplateTypeAliasModelItem::setType(const TypeInfo &type)
+{
+ m_type = type;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _TemplateTypeAliasModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ d << ", <";
+ for (qsizetype i = 0, count = m_templateParameters.size(); i < count; ++i) {
+ if (i)
+ d << ", ";
+ d << m_templateParameters.at(i)->name();
+ }
+ d << ">, type=" << m_type;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_EnumModelItem::_EnumModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind)
+{
+}
+
+_EnumModelItem::_EnumModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind)
+{
+}
+
+Access _EnumModelItem::accessPolicy() const
+{
+ return m_accessPolicy;
+}
+
+_EnumModelItem::~_EnumModelItem() = default;
+
+void _EnumModelItem::setAccessPolicy(Access accessPolicy)
+{
+ m_accessPolicy = accessPolicy;
+}
+
+EnumeratorList _EnumModelItem::enumerators() const
+{
+ return m_enumerators;
+}
+
+void _EnumModelItem::addEnumerator(const EnumeratorModelItem &item)
+{
+ m_enumerators.append(item);
+}
+
+qsizetype _EnumModelItem::indexOfValue(QStringView value) const
+{
+ for (qsizetype i = 0, size = m_enumerators.size(); i < size; ++i) {
+ if (m_enumerators.at(i)->name() == value)
+ return i;
+ }
+ return -1;
+}
+
+bool _EnumModelItem::isSigned() const
+{
+ return m_signed;
+}
+
+void _EnumModelItem::setSigned(bool s)
+{
+ m_signed = s;
+}
+
+QString _EnumModelItem::underlyingType() const
+{
+ return m_underlyingType;
+}
+
+void _EnumModelItem::setUnderlyingType(const QString &underlyingType)
+{
+ m_underlyingType = underlyingType;
+}
+
+bool _EnumModelItem::isDeprecated() const
+{
+ return m_deprecated;
+}
+
+void _EnumModelItem::setDeprecated(bool d)
+{
+ m_deprecated = d;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _EnumModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ switch (m_enumKind) {
+ case CEnum:
+ break;
+ case AnonymousEnum:
+ d << " (anonymous)";
+ break;
+ case EnumClass:
+ d << " (class)";
+ break;
+ }
+ if (m_deprecated)
+ d << " (deprecated)";
+ if (!m_signed)
+ d << " (unsigned)";
+ formatModelItemList(d, ", enumerators=", m_enumerators);
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_EnumeratorModelItem::~_EnumeratorModelItem() = default;
+
+_EnumeratorModelItem::_EnumeratorModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind)
+{
+}
+
+_EnumeratorModelItem::_EnumeratorModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind)
+{
+}
+
+QString _EnumeratorModelItem::stringValue() const
+{
+ return m_stringValue;
+}
+
+void _EnumeratorModelItem::setStringValue(const QString &value)
+{
+ m_stringValue = value;
+}
+
+bool _EnumeratorModelItem::isDeprecated() const
+{
+ return m_deprecated;
+}
+
+void _EnumeratorModelItem::setDeprecated(bool d)
+{
+ m_deprecated = d;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _EnumeratorModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ d << ", value=" << m_value << ", stringValue=\"" << m_stringValue << '"';
+ if (m_deprecated)
+ d << " (deprecated)";
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+_TemplateParameterModelItem::~_TemplateParameterModelItem() = default;
+
+_TemplateParameterModelItem::_TemplateParameterModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind)
+{
+}
+
+_TemplateParameterModelItem::_TemplateParameterModelItem(CodeModel *model,
+ const QString &name, int kind)
+ : _CodeModelItem(model, name, kind)
+{
+}
+
+TypeInfo _TemplateParameterModelItem::type() const
+{
+ return m_type;
+}
+
+void _TemplateParameterModelItem::setType(const TypeInfo &type)
+{
+ m_type = type;
+}
+
+bool _TemplateParameterModelItem::defaultValue() const
+{
+ return m_defaultValue;
+}
+
+void _TemplateParameterModelItem::setDefaultValue(bool defaultValue)
+{
+ m_defaultValue = defaultValue;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _TemplateParameterModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ d << ", type=" << m_type;
+ if (m_defaultValue)
+ d << " [defaultValue]";
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// ---------------------------------------------------------------------------
+TypeInfo _MemberModelItem::type() const
+{
+ return m_type;
+}
+
+void _MemberModelItem::setType(const TypeInfo &type)
+{
+ m_type = type;
+}
+
+Access _MemberModelItem::accessPolicy() const
+{
+ return m_accessPolicy;
+}
+
+_MemberModelItem::~_MemberModelItem() = default;
+
+void _MemberModelItem::setAccessPolicy(Access accessPolicy)
+{
+ m_accessPolicy = accessPolicy;
+}
+
+bool _MemberModelItem::isStatic() const
+{
+ return m_isStatic;
+}
+
+void _MemberModelItem::setStatic(bool isStatic)
+{
+ m_isStatic = isStatic;
+}
+
+_MemberModelItem::_MemberModelItem(CodeModel *model, int kind)
+ : _CodeModelItem(model, kind), m_flags(0)
+{
+}
+
+_MemberModelItem::_MemberModelItem(CodeModel *model, const QString &name, int kind)
+ : _CodeModelItem(model, name, kind), m_flags(0)
+{
+}
+
+bool _MemberModelItem::isConstant() const
+{
+ return m_isConstant;
+}
+
+void _MemberModelItem::setConstant(bool isConstant)
+{
+ m_isConstant = isConstant;
+}
+
+bool _MemberModelItem::isVolatile() const
+{
+ return m_isVolatile;
+}
+
+void _MemberModelItem::setVolatile(bool isVolatile)
+{
+ m_isVolatile = isVolatile;
+}
+
+bool _MemberModelItem::isAuto() const
+{
+ return m_isAuto;
+}
+
+void _MemberModelItem::setAuto(bool isAuto)
+{
+ m_isAuto = isAuto;
+}
+
+bool _MemberModelItem::isFriend() const
+{
+ return m_isFriend;
+}
+
+void _MemberModelItem::setFriend(bool isFriend)
+{
+ m_isFriend = isFriend;
+}
+
+bool _MemberModelItem::isRegister() const
+{
+ return m_isRegister;
+}
+
+void _MemberModelItem::setRegister(bool isRegister)
+{
+ m_isRegister = isRegister;
+}
+
+bool _MemberModelItem::isExtern() const
+{
+ return m_isExtern;
+}
+
+void _MemberModelItem::setExtern(bool isExtern)
+{
+ m_isExtern = isExtern;
+}
+
+bool _MemberModelItem::isMutable() const
+{
+ return m_isMutable;
+}
+
+void _MemberModelItem::setMutable(bool isMutable)
+{
+ m_isMutable = isMutable;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void _MemberModelItem::formatDebug(QDebug &d) const
+{
+ _CodeModelItem::formatDebug(d);
+ d << ", " << m_accessPolicy << ", type=";
+ if (m_isConstant)
+ d << "const ";
+ if (m_isVolatile)
+ d << "volatile ";
+ if (m_isStatic)
+ d << "static ";
+ if (m_isAuto)
+ d << "auto ";
+ if (m_isFriend)
+ d << "friend ";
+ if (m_isRegister)
+ d << "register ";
+ if (m_isExtern)
+ d << "extern ";
+ if (m_isMutable)
+ d << "mutable ";
+ d << m_type;
+ formatScopeList(d, ", templateParameters", m_templateParameters);
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel.h b/sources/shiboken6/ApiExtractor/parser/codemodel.h
new file mode 100644
index 000000000..b31c09163
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel.h
@@ -0,0 +1,700 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2002-2005 Roberto Raggi <roberto@kdevelop.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+
+#ifndef CODEMODEL_H
+#define CODEMODEL_H
+
+#include "codemodel_fwd.h"
+#include "codemodel_enums.h"
+#include "enumvalue.h"
+#include "typeinfo.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QSet>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QList>
+#include <QtCore/QWeakPointer>
+
+#include <optional>
+#include <utility>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+#define DECLARE_MODEL_NODE(k) \
+ enum { __node_kind = Kind_##k };
+
+class SourceLocation;
+
+class CodeModel
+{
+ Q_GADGET
+public:
+ Q_DISABLE_COPY_MOVE(CodeModel)
+
+ enum FunctionType {
+ Normal,
+ Constructor,
+ CopyConstructor,
+ MoveConstructor,
+ Destructor,
+ Signal,
+ Slot,
+ AssignmentOperator,
+ CallOperator,
+ ConversionOperator,
+ DereferenceOperator, // Iterator's operator *
+ ReferenceOperator, // operator &
+ ArrowOperator,
+ ArithmeticOperator,
+ IncrementOperator,
+ DecrementOperator,
+ BitwiseOperator,
+ LogicalOperator,
+ ShiftOperator,
+ SubscriptOperator,
+ ComparisonOperator
+ };
+ Q_ENUM(FunctionType)
+
+ enum ClassType {
+ Class,
+ Struct,
+ Union
+ };
+ Q_ENUM(ClassType)
+
+public:
+ CodeModel();
+ virtual ~CodeModel();
+
+ FileList files() const { return m_files; }
+ NamespaceModelItem globalNamespace() const;
+
+ void addFile(const FileModelItem &item);
+ FileModelItem findFile(QAnyStringView name) const;
+
+ static CodeModelItem findItem(const QStringList &qualifiedName,
+ const ScopeModelItem &scope);
+
+private:
+ FileList m_files;
+ NamespaceModelItem m_globalNamespace;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, Access a);
+QDebug operator<<(QDebug d, const CodeModel *m);
+#endif
+
+class _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_CodeModelItem)
+
+ enum Kind {
+ /* These are bit-flags resembling inheritance */
+ Kind_Scope = 0x1,
+ Kind_Namespace = 0x2 | Kind_Scope,
+ Kind_Member = 0x4,
+ Kind_Function = 0x8 | Kind_Member,
+ KindMask = 0xf,
+
+ /* These are for classes that are not inherited from */
+ FirstKind = 0x8,
+ Kind_Argument = 1 << FirstKind,
+ Kind_Class = 2 << FirstKind | Kind_Scope,
+ Kind_Enum = 3 << FirstKind,
+ Kind_Enumerator = 4 << FirstKind,
+ Kind_File = 5 << FirstKind | Kind_Namespace,
+ Kind_TemplateParameter = 7 << FirstKind,
+ Kind_TypeDef = 8 << FirstKind,
+ Kind_TemplateTypeAlias = 9 << FirstKind,
+ Kind_Variable = 10 << FirstKind | Kind_Member
+ };
+
+public:
+ virtual ~_CodeModelItem();
+
+ int kind() const;
+
+ QStringList qualifiedName() const;
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QStringList scope() const;
+ void setScope(const QStringList &scope);
+
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+ FileModelItem file() const;
+
+ void getStartPosition(int *line, int *column);
+ int startLine() const { return m_startLine; }
+ void setStartPosition(int line, int column);
+
+ void getEndPosition(int *line, int *column);
+ void setEndPosition(int line, int column);
+
+ SourceLocation sourceLocation() const;
+
+ inline CodeModel *model() const { return m_model; }
+
+ const _ScopeModelItem *enclosingScope() const;
+ void setEnclosingScope(const _ScopeModelItem *s);
+
+#ifndef QT_NO_DEBUG_STREAM
+ static void formatKind(QDebug &d, int k);
+ virtual void formatDebug(QDebug &d) const;
+#endif
+
+protected:
+ explicit _CodeModelItem(CodeModel *model, int kind);
+ explicit _CodeModelItem(CodeModel *model, const QString &name, int kind);
+
+private:
+ CodeModel *m_model;
+ const _ScopeModelItem *m_enclosingScope = nullptr;
+ int m_kind;
+ int m_startLine;
+ int m_startColumn;
+ int m_endLine;
+ int m_endColumn;
+ QString m_name;
+ QString m_fileName;
+ QStringList m_scope;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const _CodeModelItem *t);
+#endif
+
+class _ScopeModelItem: public _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_ScopeModelItem)
+ DECLARE_MODEL_NODE(Scope)
+
+ ~_ScopeModelItem();
+
+ ClassList classes() const { return m_classes; }
+ const EnumList &enums() const { return m_enums; }
+ inline const FunctionList &functions() const { return m_functions; }
+ TypeDefList typeDefs() const { return m_typeDefs; }
+ TemplateTypeAliasList templateTypeAliases() const { return m_templateTypeAliases; }
+ VariableList variables() const { return m_variables; }
+
+ void addClass(const ClassModelItem &item);
+ void addEnum(const EnumModelItem &item);
+ void addFunction(const FunctionModelItem &item);
+ void addTypeDef(const TypeDefModelItem &item);
+ void addTemplateTypeAlias(const TemplateTypeAliasModelItem &item);
+ void addVariable(const VariableModelItem &item);
+
+ ClassModelItem findClass(const QString &name) const;
+ EnumModelItem findEnum(QAnyStringView name) const;
+
+ struct FindEnumByValueReturn
+ {
+ operator bool() const { return bool(item); }
+
+ EnumModelItem item;
+ QString qualifiedName;
+ };
+ FindEnumByValueReturn findEnumByValue(QStringView value) const;
+
+ FunctionList findFunctions(QAnyStringView name) const;
+ TypeDefModelItem findTypeDef(QAnyStringView name) const;
+ TemplateTypeAliasModelItem findTemplateTypeAlias(QAnyStringView name) const;
+ VariableModelItem findVariable(QAnyStringView name) const;
+
+ void addEnumsDeclaration(const QString &enumsDeclaration);
+ QStringList enumsDeclarations() const { return m_enumsDeclarations; }
+
+ FunctionModelItem declaredFunction(const FunctionModelItem &item);
+
+ bool isEmpty() const;
+ void purgeClassDeclarations();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+protected:
+ explicit _ScopeModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _ScopeModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+
+ void appendScope(const _ScopeModelItem &other);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatScopeItemsDebug(QDebug &d) const;
+#endif
+
+private:
+ qsizetype indexOfEnum(const QString &name) const;
+
+ FindEnumByValueReturn findEnumByValueHelper(QStringView fullValue,
+ QStringView value) const;
+ static FindEnumByValueReturn
+ findEnumByValueRecursion(const _ScopeModelItem *scope,
+ QStringView fullValue, QStringView value,
+ bool searchSiblingNamespaces = true);
+
+ ClassList m_classes;
+ EnumList m_enums;
+ TypeDefList m_typeDefs;
+ TemplateTypeAliasList m_templateTypeAliases;
+ VariableList m_variables;
+ FunctionList m_functions;
+
+private:
+ QStringList m_enumsDeclarations;
+};
+
+class _ClassModelItem: public _ScopeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_ClassModelItem)
+ DECLARE_MODEL_NODE(Class)
+
+ struct BaseClass
+ {
+ QString name;
+ ClassModelItem klass; // Might be null in case of templates
+ Access accessPolicy = Access::Public;
+ };
+
+ struct UsingMember // Introducing a base class member via 'using' directive
+ {
+ QString className;
+ QString memberName;
+ Access access = Access::Public;
+ };
+
+ explicit _ClassModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _ClassModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_ClassModelItem();
+
+ const QList<BaseClass> &baseClasses() const { return m_baseClasses; }
+
+ const QList<UsingMember> &usingMembers() const;
+ void addUsingMember(const QString &className, const QString &memberName,
+ Access accessPolicy);
+
+ void addBaseClass(const BaseClass &b) { m_baseClasses.append(b); }
+
+ TemplateParameterList templateParameters() const;
+ void setTemplateParameters(const TemplateParameterList &templateParameters);
+
+ bool extendsClass(const QString &name) const;
+
+ void setClassType(CodeModel::ClassType type);
+ CodeModel::ClassType classType() const;
+
+ void addPropertyDeclaration(const QString &propertyDeclaration);
+ QStringList propertyDeclarations() const { return m_propertyDeclarations; }
+
+ bool isFinal() const { return m_final; }
+ void setFinal(bool f) { m_final = f; }
+
+ bool isEmpty() const;
+ bool isTemplate() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ QList<BaseClass> m_baseClasses;
+ QList<UsingMember> m_usingMembers;
+ TemplateParameterList m_templateParameters;
+ CodeModel::ClassType m_classType = CodeModel::Class;
+
+ QStringList m_propertyDeclarations;
+ bool m_final = false;
+};
+
+class _NamespaceModelItem: public _ScopeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_NamespaceModelItem)
+ DECLARE_MODEL_NODE(Namespace)
+
+ explicit _NamespaceModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _NamespaceModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_NamespaceModelItem();
+
+ const NamespaceList &namespaces() const { return m_namespaces; }
+
+ NamespaceType type() const { return m_type; }
+ void setType(NamespaceType t) { m_type = t; }
+
+ void addNamespace(NamespaceModelItem item);
+
+ NamespaceModelItem findNamespace(QAnyStringView name) const;
+
+ void appendNamespace(const _NamespaceModelItem &other);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ NamespaceList m_namespaces;
+ NamespaceType m_type = NamespaceType::Default;
+};
+
+class _FileModelItem: public _NamespaceModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_FileModelItem)
+ DECLARE_MODEL_NODE(File)
+
+ using _NamespaceModelItem::_NamespaceModelItem;
+
+ ~_FileModelItem();
+};
+
+class _ArgumentModelItem: public _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_ArgumentModelItem)
+ DECLARE_MODEL_NODE(Argument)
+
+ explicit _ArgumentModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _ArgumentModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_ArgumentModelItem();
+
+ TypeInfo type() const;
+ void setType(const TypeInfo &type);
+
+ bool defaultValue() const;
+ void setDefaultValue(bool defaultValue);
+
+ QString defaultValueExpression() const { return m_defaultValueExpression; }
+ void setDefaultValueExpression(const QString &expr) { m_defaultValueExpression = expr; }
+
+ // Argument type has scope resolution "::ArgumentType"
+ bool scopeResolution() const;
+ void setScopeResolution(bool v);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ TypeInfo m_type;
+ QString m_defaultValueExpression;
+ bool m_defaultValue = false;
+ bool m_scopeResolution = false;
+};
+
+class _MemberModelItem: public _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_MemberModelItem)
+ DECLARE_MODEL_NODE(Member)
+
+ explicit _MemberModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _MemberModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_MemberModelItem();
+
+ bool isConstant() const;
+ void setConstant(bool isConstant);
+
+ bool isVolatile() const;
+ void setVolatile(bool isVolatile);
+
+ bool isStatic() const;
+ void setStatic(bool isStatic);
+
+ bool isAuto() const;
+ void setAuto(bool isAuto);
+
+ bool isFriend() const;
+ void setFriend(bool isFriend);
+
+ bool isRegister() const;
+ void setRegister(bool isRegister);
+
+ bool isExtern() const;
+ void setExtern(bool isExtern);
+
+ bool isMutable() const;
+ void setMutable(bool isMutable);
+
+ Access accessPolicy() const;
+ void setAccessPolicy(Access accessPolicy);
+
+ TemplateParameterList templateParameters() const { return m_templateParameters; }
+ void setTemplateParameters(const TemplateParameterList &templateParameters) { m_templateParameters = templateParameters; }
+
+ TypeInfo type() const;
+ void setType(const TypeInfo &type);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ TemplateParameterList m_templateParameters;
+ TypeInfo m_type;
+ Access m_accessPolicy = Access::Public;
+ union {
+ struct {
+ uint m_isConstant: 1;
+ uint m_isVolatile: 1;
+ uint m_isStatic: 1;
+ uint m_isAuto: 1;
+ uint m_isFriend: 1;
+ uint m_isRegister: 1;
+ uint m_isExtern: 1;
+ uint m_isMutable: 1;
+ };
+ uint m_flags;
+ };
+
+};
+
+class _FunctionModelItem: public _MemberModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_FunctionModelItem)
+ DECLARE_MODEL_NODE(Function)
+
+ explicit _FunctionModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _FunctionModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_FunctionModelItem();
+
+ ArgumentList arguments() const;
+
+ void addArgument(const ArgumentModelItem& item);
+
+ CodeModel::FunctionType functionType() const;
+ void setFunctionType(CodeModel::FunctionType functionType);
+
+ static std::optional<CodeModel::FunctionType> functionTypeFromName(QStringView name);
+
+ FunctionAttributes attributes() const { return m_attributes; }
+ void setAttributes(FunctionAttributes a) { m_attributes = a; }
+ void setAttribute(FunctionAttribute a, bool on = true) { m_attributes.setFlag(a, on); }
+
+ bool isDeleted() const;
+ void setDeleted(bool d);
+
+ bool isInline() const;
+ void setInline(bool isInline);
+
+ bool isHiddenFriend() const;
+ void setHiddenFriend(bool f);
+
+ bool isVariadics() const;
+ void setVariadics(bool isVariadics);
+
+ bool scopeResolution() const; // Return type has scope resolution "::ReturnType"
+ void setScopeResolution(bool v);
+
+ bool isDefaultConstructor() const;
+ bool isSpaceshipOperator() const;
+
+ bool isSimilar(const FunctionModelItem &other) const;
+
+ bool isNoExcept() const;
+
+ bool isOperator() const;
+
+ ExceptionSpecification exceptionSpecification() const;
+ void setExceptionSpecification(ExceptionSpecification e);
+
+ QString typeSystemSignature() const; // For dumping out type system files
+
+ // Private, for usage by the clang builder.
+ void _determineType();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ CodeModel::FunctionType _determineTypeHelper() const;
+
+ ArgumentList m_arguments;
+ FunctionAttributes m_attributes;
+ CodeModel::FunctionType m_functionType = CodeModel::Normal;
+ union {
+ struct {
+ uint m_isDeleted: 1;
+ uint m_isInline: 1;
+ uint m_isVariadics: 1;
+ uint m_isHiddenFriend: 1;
+ uint m_isInvokable : 1; // Qt
+ uint m_scopeResolution: 1;
+ };
+ uint m_flags;
+ };
+ ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown;
+};
+
+class _VariableModelItem: public _MemberModelItem
+{
+public:
+ DECLARE_MODEL_NODE(Variable)
+
+ using _MemberModelItem::_MemberModelItem;
+};
+
+class _TypeDefModelItem: public _CodeModelItem
+{
+public:
+ DECLARE_MODEL_NODE(TypeDef)
+
+ explicit _TypeDefModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _TypeDefModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+
+ TypeInfo type() const;
+ void setType(const TypeInfo &type);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ TypeInfo m_type;
+};
+
+class _TemplateTypeAliasModelItem : public _CodeModelItem
+{
+public:
+ DECLARE_MODEL_NODE(TemplateTypeAlias)
+
+ explicit _TemplateTypeAliasModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _TemplateTypeAliasModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+
+ TemplateParameterList templateParameters() const;
+ void addTemplateParameter(const TemplateParameterModelItem &templateParameter);
+
+ TypeInfo type() const;
+ void setType(const TypeInfo &type);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ TemplateParameterList m_templateParameters;
+ TypeInfo m_type;
+};
+
+class _EnumModelItem: public _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_EnumModelItem)
+ DECLARE_MODEL_NODE(Enum)
+
+ explicit _EnumModelItem(CodeModel *model, const QString &name, int kind = __node_kind);
+ explicit _EnumModelItem(CodeModel *model, int kind = __node_kind);
+ ~_EnumModelItem();
+
+ Access accessPolicy() const;
+ void setAccessPolicy(Access accessPolicy);
+
+ bool hasValues() const { return !m_enumerators.isEmpty(); }
+ EnumeratorList enumerators() const;
+ void addEnumerator(const EnumeratorModelItem &item);
+
+ EnumKind enumKind() const { return m_enumKind; }
+ void setEnumKind(EnumKind kind) { m_enumKind = kind; }
+
+ qsizetype indexOfValue(QStringView value) const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+ bool isDeprecated() const;
+ void setDeprecated(bool d);
+
+ bool isSigned() const;
+ void setSigned(bool s);
+
+ QString underlyingType() const;
+ void setUnderlyingType(const QString &underlyingType);
+
+private:
+ QString m_underlyingType;
+ Access m_accessPolicy = Access::Public;
+ EnumeratorList m_enumerators;
+ EnumKind m_enumKind = CEnum;
+ bool m_deprecated = false;
+ bool m_signed = true;
+};
+
+class _EnumeratorModelItem: public _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_EnumeratorModelItem)
+ DECLARE_MODEL_NODE(Enumerator)
+
+ explicit _EnumeratorModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _EnumeratorModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_EnumeratorModelItem();
+
+ QString stringValue() const;
+ void setStringValue(const QString &stringValue);
+
+ EnumValue value() const { return m_value; }
+ void setValue(EnumValue v) { m_value = v; }
+
+ bool isDeprecated() const;
+ void setDeprecated(bool d);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ QString m_stringValue;
+ EnumValue m_value;
+ bool m_deprecated = false;
+};
+
+class _TemplateParameterModelItem: public _CodeModelItem
+{
+public:
+ Q_DISABLE_COPY_MOVE(_TemplateParameterModelItem)
+ DECLARE_MODEL_NODE(TemplateParameter)
+
+ explicit _TemplateParameterModelItem(CodeModel *model, int kind = __node_kind);
+ explicit _TemplateParameterModelItem(CodeModel *model, const QString &name,
+ int kind = __node_kind);
+ ~_TemplateParameterModelItem();
+
+ TypeInfo type() const;
+ void setType(const TypeInfo &type);
+
+ bool defaultValue() const;
+ void setDefaultValue(bool defaultValue);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+private:
+ TypeInfo m_type;
+ bool m_defaultValue = false;
+};
+
+#endif // CODEMODEL_H
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h b/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h
new file mode 100644
index 000000000..e5c429bd0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel_enums.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CODEMODEL_ENUMS_H
+#define CODEMODEL_ENUMS_H
+
+#include <QtCore/qflags.h>
+
+enum ReferenceType {
+ NoReference,
+ LValueReference,
+ RValueReference
+};
+
+enum EnumKind {
+ CEnum, // Standard C: enum Foo { value1, value2 }
+ AnonymousEnum, // enum { value1, value2 }
+ EnumClass // C++ 11 : enum class Foo { value1, value2 }
+};
+
+enum class Indirection
+{
+ Pointer, // int *
+ ConstPointer // int *const
+};
+
+enum class ExceptionSpecification
+{
+ Unknown,
+ NoExcept,
+ Throws
+};
+
+enum class NamespaceType
+{
+ Default,
+ Anonymous,
+ Inline
+};
+
+enum class Access
+{
+ Private,
+ Protected,
+ Public
+};
+
+enum class FunctionAttribute {
+ Abstract = 0x00000001,
+ Static = 0x00000002,
+ Virtual = 0x00000004,
+ Override = 0x00000008,
+ Final = 0x00000010,
+ Deprecated = 0x00000020, // Code annotation
+ Explicit = 0x00000040, // Constructor
+};
+
+Q_DECLARE_FLAGS(FunctionAttributes, FunctionAttribute)
+Q_DECLARE_OPERATORS_FOR_FLAGS(FunctionAttributes)
+
+#endif // CODEMODEL_ENUMS_H
diff --git a/sources/shiboken6/ApiExtractor/parser/codemodel_fwd.h b/sources/shiboken6/ApiExtractor/parser/codemodel_fwd.h
new file mode 100644
index 000000000..f0a25c9db
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/codemodel_fwd.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2002-2005 Roberto Raggi <roberto@kdevelop.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+
+#ifndef CODEMODEL_FWD_H
+#define CODEMODEL_FWD_H
+
+#include <QtCore/QList>
+
+#include <memory>
+
+// forward declarations
+class CodeModel;
+class _ArgumentModelItem;
+class _ClassModelItem;
+class _CodeModelItem;
+class _EnumModelItem;
+class _EnumeratorModelItem;
+class _FileModelItem;
+class _FunctionModelItem;
+class _NamespaceModelItem;
+class _ScopeModelItem;
+class _TemplateParameterModelItem;
+class _TypeDefModelItem;
+class _TemplateTypeAliasModelItem;
+class _VariableModelItem;
+class _MemberModelItem;
+class TypeInfo;
+
+using ArgumentModelItem = std::shared_ptr<_ArgumentModelItem>;
+using ClassModelItem = std::shared_ptr<_ClassModelItem>;
+using CodeModelItem = std::shared_ptr<_CodeModelItem>;
+using EnumModelItem = std::shared_ptr<_EnumModelItem>;
+using EnumeratorModelItem = std::shared_ptr<_EnumeratorModelItem>;
+using FileModelItem = std::shared_ptr<_FileModelItem>;
+using FunctionModelItem = std::shared_ptr<_FunctionModelItem>;
+using NamespaceModelItem = std::shared_ptr<_NamespaceModelItem>;
+using ScopeModelItem = std::shared_ptr<_ScopeModelItem>;
+using TemplateParameterModelItem = std::shared_ptr<_TemplateParameterModelItem>;
+using TypeDefModelItem = std::shared_ptr<_TypeDefModelItem>;
+using TemplateTypeAliasModelItem = std::shared_ptr<_TemplateTypeAliasModelItem>;
+using VariableModelItem = std::shared_ptr<_VariableModelItem>;
+using MemberModelItem = std::shared_ptr<_MemberModelItem>;
+
+using ArgumentList = QList<ArgumentModelItem>;
+using ClassList = QList<ClassModelItem>;
+using ItemList = QList<CodeModelItem>;
+using EnumList = QList<EnumModelItem>;
+using EnumeratorList = QList<EnumeratorModelItem>;
+using FileList = QList<FileModelItem>;
+using FunctionList = QList<FunctionModelItem>;
+using NamespaceList = QList<NamespaceModelItem>;
+using ScopeList = QList<ScopeModelItem>;
+using TemplateParameterList = QList<TemplateParameterModelItem>;
+using TypeDefList = QList<TypeDefModelItem>;
+using TemplateTypeAliasList = QList<TemplateTypeAliasModelItem>;
+using VariableList = QList<VariableModelItem>;
+using MemberList = QList<MemberModelItem>;
+
+#endif // CODEMODEL_FWD_H
diff --git a/sources/shiboken6/ApiExtractor/parser/enumvalue.cpp b/sources/shiboken6/ApiExtractor/parser/enumvalue.cpp
new file mode 100644
index 000000000..3749e16a8
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/enumvalue.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "enumvalue.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+using namespace Qt::StringLiterals;
+
+QString EnumValue::toString() const
+{
+ return m_type == EnumValue::Signed
+ ? QString::number(m_value) : QString::number(m_unsignedValue);
+}
+
+QString EnumValue::toHex(int fieldWidth) const
+{
+ QString result;
+ QTextStream str(&result);
+ // Note: Qt goofes up formatting of negative padded hex numbers, it ends up
+ // with "0x00-1". Write '-' before.
+ if (isNegative())
+ str << '-';
+ str << "0x" << Qt::hex;
+ if (fieldWidth) {
+ str.setFieldWidth(fieldWidth);
+ str.setPadChar(u'0');
+ }
+ if (m_type == EnumValue::Signed)
+ str << qAbs(m_value);
+ else
+ str << m_unsignedValue;
+ return result;
+}
+
+void EnumValue::setValue(qint64 v)
+{
+ m_value = v;
+ m_type = Signed;
+}
+
+void EnumValue::setUnsignedValue(quint64 v)
+{
+ m_unsignedValue = v;
+ m_type = Unsigned;
+}
+
+EnumValue EnumValue::toUnsigned() const
+{
+ if (m_type == Unsigned)
+ return *this;
+ EnumValue result;
+ result.setUnsignedValue(m_value < 0 ? quint64(-m_value) : quint64(m_value));
+ return result;
+}
+
+bool comparesEqual(const EnumValue &lhs, const EnumValue &rhs) noexcept
+{
+ if (lhs.m_type != rhs.m_type)
+ return false;
+ return lhs.m_type == EnumValue::Signed
+ ? lhs.m_value == rhs.m_value : lhs.m_unsignedValue == rhs.m_unsignedValue;
+}
+
+void EnumValue::formatDebugHex(QDebug &d) const
+{
+ d << "0x" << Qt::hex;
+ formatDebug(d);
+ d << Qt::dec;
+}
+
+void EnumValue::formatDebug(QDebug &d) const
+{
+
+ if (m_type == EnumValue::Signed)
+ d << m_value;
+ else
+ d << m_unsignedValue << 'u';
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d,const EnumValue &v)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "EnumValue(";
+ v.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+QTextStream &operator<<(QTextStream &s, const EnumValue &v)
+{
+ if (v.m_type == EnumValue::Signed)
+ s << v.m_value;
+ else
+ s << v.m_unsignedValue;
+ return s;
+}
diff --git a/sources/shiboken6/ApiExtractor/parser/enumvalue.h b/sources/shiboken6/ApiExtractor/parser/enumvalue.h
new file mode 100644
index 000000000..bbd5a712d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/enumvalue.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ENUMVALUE_H
+#define ENUMVALUE_H
+
+#include <QtCore/qtypes.h>
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/QtCompare>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QString)
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+
+class EnumValue
+{
+public:
+ enum Type
+ {
+ Signed,
+ Unsigned
+ };
+
+ QString toString() const;
+ QString toHex(int fieldWidth = 0) const;
+
+ Type type() { return m_type; }
+ qint64 value() const { return m_value; }
+ quint64 unsignedValue() const { return m_unsignedValue; }
+ bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0u; }
+ bool isNegative() const { return m_type == Signed && m_value < 0; }
+
+ void setValue(qint64 v);
+ void setUnsignedValue(quint64 v);
+
+ EnumValue toUnsigned() const;
+
+ bool equals(const EnumValue &rhs) const;
+
+ void formatDebug(QDebug &d) const;
+ void formatDebugHex(QDebug &d) const;
+
+private:
+ friend bool comparesEqual(const EnumValue &lhs,
+ const EnumValue &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(EnumValue)
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend QDebug operator<<(QDebug, const EnumValue &);
+#endif
+ friend QTextStream &operator<<(QTextStream &, const EnumValue &);
+
+ union
+ {
+ qint64 m_value = 0;
+ quint64 m_unsignedValue;
+ };
+ Type m_type = Signed;
+};
+
+#endif // ENUMVALUE_H
diff --git a/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp b/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp
new file mode 100644
index 000000000..f8c5c31d8
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp
@@ -0,0 +1,624 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2002-2005 Roberto Raggi <roberto@kdevelop.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+
+#include "typeinfo.h"
+#include "codemodel.h"
+
+#include <clangparser/clangutils.h>
+#include <debughelpers_p.h>
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QStack>
+#include <QtCore/QTextStream>
+
+#include <iostream>
+
+using namespace Qt::StringLiterals;
+
+class TypeInfoData : public QSharedData
+{
+
+public:
+ TypeInfoData();
+
+ bool isVoid() const;
+ bool equals(const TypeInfoData &other) const;
+ bool isStdType() const;
+ void simplifyStdType();
+
+ QStringList m_qualifiedName;
+ QStringList m_arrayElements;
+ TypeInfo::TypeInfoList m_arguments;
+ TypeInfo::TypeInfoList m_instantiations;
+ TypeInfo::Indirections m_indirections;
+
+ union {
+ uint flags;
+
+ struct {
+ uint m_constant: 1;
+ uint m_volatile: 1;
+ uint m_functionPointer: 1;
+ uint m_padding: 29;
+ };
+ };
+
+ ReferenceType m_referenceType = NoReference;
+};
+
+TypeInfoData::TypeInfoData() : flags(0)
+{
+}
+
+TypeInfo::TypeInfo() : d(new TypeInfoData)
+{
+}
+
+TypeInfo::~TypeInfo() = default;
+TypeInfo::TypeInfo(const TypeInfo &) = default;
+TypeInfo& TypeInfo::operator=(const TypeInfo &) = default;
+TypeInfo::TypeInfo(TypeInfo &&) noexcept = default;
+TypeInfo &TypeInfo::operator=(TypeInfo &&) noexcept = default;
+
+static inline TypeInfo createType(const QString &name)
+{
+ TypeInfo result;
+ result.addName(name);
+ return result;
+}
+
+TypeInfo TypeInfo::voidType()
+{
+ static const TypeInfo result = createType(u"void"_s);
+ return result;
+}
+
+TypeInfo TypeInfo::varArgsType()
+{
+ static const TypeInfo result = createType(u"..."_s);
+ return result;
+}
+
+TypeInfo TypeInfo::combine(const TypeInfo &__lhs, const TypeInfo &__rhs)
+{
+ TypeInfo __result = __lhs;
+
+ __result.setConstant(__result.isConstant() || __rhs.isConstant());
+ __result.setVolatile(__result.isVolatile() || __rhs.isVolatile());
+ if (__rhs.referenceType() > __result.referenceType())
+ __result.setReferenceType(__rhs.referenceType());
+
+ const auto indirections = __rhs.indirectionsV();
+ for (auto i : indirections)
+ __result.addIndirection(i);
+
+ __result.setArrayElements(__result.arrayElements() + __rhs.arrayElements());
+
+ const auto &instantiations = __rhs.instantiations();
+ for (const auto &i : instantiations)
+ __result.addInstantiation(i);
+
+ return __result;
+}
+
+QStringList TypeInfo::qualifiedName() const
+{
+ return d->m_qualifiedName;
+}
+
+void TypeInfo::setQualifiedName(const QStringList &qualified_name)
+{
+ if (d->m_qualifiedName != qualified_name)
+ d->m_qualifiedName = qualified_name;
+}
+
+void TypeInfo::addName(const QString &n)
+{
+ d->m_qualifiedName.append(n);
+}
+
+bool TypeInfoData::isVoid() const
+{
+ return m_indirections.isEmpty() && m_referenceType == NoReference
+ && m_arguments.isEmpty() && m_arrayElements.isEmpty()
+ && m_instantiations.isEmpty()
+ && m_qualifiedName.size() == 1
+ && m_qualifiedName.constFirst() == u"void";
+}
+
+bool TypeInfo::isVoid() const
+{
+ return d->isVoid();
+}
+
+bool TypeInfo::isConstant() const
+{
+ return d->m_constant;
+}
+
+void TypeInfo::setConstant(bool is)
+{
+ if (d->m_constant != is)
+ d->m_constant = is;
+}
+
+bool TypeInfo::isVolatile() const
+{
+ return d->m_volatile;
+}
+
+void TypeInfo::setVolatile(bool is)
+{
+ if (d->m_volatile != is)
+ d->m_volatile = is;
+}
+
+ReferenceType TypeInfo::referenceType() const
+{
+ return d->m_referenceType;
+}
+
+void TypeInfo::setReferenceType(ReferenceType r)
+{
+ if (d->m_referenceType != r)
+ d->m_referenceType = r;
+}
+
+const TypeInfo::Indirections &TypeInfo::indirectionsV() const
+{
+ return d->m_indirections;
+}
+
+void TypeInfo::setIndirectionsV(const TypeInfo::Indirections &i)
+{
+ if (d->m_indirections != i)
+ d->m_indirections = i;
+}
+
+int TypeInfo::indirections() const
+{
+ return d->m_indirections.size();
+}
+
+void TypeInfo::setIndirections(int indirections)
+{
+ const Indirections newValue(indirections, Indirection::Pointer);
+ if (d->m_indirections != newValue)
+ d->m_indirections = newValue;
+}
+
+void TypeInfo::addIndirection(Indirection i)
+{
+ d->m_indirections.append(i);
+}
+
+bool TypeInfo::isFunctionPointer() const
+{
+ return d->m_functionPointer;
+}
+
+void TypeInfo::setFunctionPointer(bool is)
+{
+ if (d->m_functionPointer != is)
+ d->m_functionPointer = is;
+}
+
+const QStringList &TypeInfo::arrayElements() const
+{
+ return d->m_arrayElements;
+}
+
+void TypeInfo::setArrayElements(const QStringList &arrayElements)
+{
+ if (d->m_arrayElements != arrayElements)
+ d->m_arrayElements = arrayElements;
+}
+
+void TypeInfo::addArrayElement(const QString &a)
+{
+ d->m_arrayElements.append(a);
+}
+
+const QList<TypeInfo> &TypeInfo::arguments() const
+{
+ return d->m_arguments;
+}
+
+void TypeInfo::setArguments(const QList<TypeInfo> &arguments)
+{
+ if (d->m_arguments != arguments)
+ d->m_arguments = arguments;
+}
+
+void TypeInfo::addArgument(const TypeInfo &arg)
+{
+ d->m_arguments.append(arg);
+}
+
+const TypeInfo::TypeInfoList &TypeInfo::instantiations() const
+{
+ return d->m_instantiations;
+}
+
+TypeInfo::TypeInfoList &TypeInfo::instantiations()
+{
+ return d->m_instantiations;
+}
+
+void TypeInfo::setInstantiations(const TypeInfoList &i)
+{
+ if (d->m_instantiations != i)
+ d->m_instantiations = i;
+}
+
+void TypeInfo::addInstantiation(const TypeInfo &i)
+{
+ d->m_instantiations.append(i);
+}
+
+void TypeInfo::clearInstantiations()
+{
+ if (!d->m_instantiations.isEmpty())
+ d->m_instantiations.clear();
+}
+
+bool TypeInfo::isPlain() const
+{
+ return d->m_constant == 0 && d->m_volatile == 0 && d->m_referenceType == NoReference
+ && d->m_indirections.isEmpty() && d->m_arrayElements.isEmpty();
+}
+
+TypeInfo TypeInfo::resolveType(TypeInfo const &__type, const ScopeModelItem &__scope)
+{
+ CodeModel *__model = __scope->model();
+ Q_ASSERT(__model != nullptr);
+
+ return TypeInfo::resolveType(__model->findItem(__type.qualifiedName(), __scope), __type, __scope);
+}
+
+TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, const ScopeModelItem &__scope)
+{
+ // Copy the type and replace with the proper qualified name. This
+ // only makes sence to do if we're actually getting a resolved
+ // type with a namespace. We only get this if the returned type
+ // has more than 2 entries in the qualified name... This test
+ // could be improved by returning if the type was found or not.
+ TypeInfo otherType(__type);
+ if (__item && __item->qualifiedName().size() > 1) {
+ otherType.setQualifiedName(__item->qualifiedName());
+ }
+
+ if (TypeDefModelItem __typedef = std::dynamic_pointer_cast<_TypeDefModelItem>(__item)) {
+ const TypeInfo combined = TypeInfo::combine(__typedef->type(), otherType);
+ const CodeModelItem nextItem = __scope->model()->findItem(combined.qualifiedName(), __scope);
+ if (!nextItem)
+ return combined;
+ // PYSIDE-362, prevent recursion on opaque structs like
+ // typedef struct xcb_connection_t xcb_connection_t;
+ if (nextItem.get() ==__item.get()) {
+ std::cerr << "** WARNING Bailing out recursion of " << __FUNCTION__
+ << "() on " << qPrintable(__type.qualifiedName().join(u"::"_s))
+ << std::endl;
+ return otherType;
+ }
+ return resolveType(nextItem, combined, __scope);
+ }
+
+ if (TemplateTypeAliasModelItem templateTypeAlias = std::dynamic_pointer_cast<_TemplateTypeAliasModelItem>(__item)) {
+
+ TypeInfo combined = TypeInfo::combine(templateTypeAlias->type(), otherType);
+ // For the alias "template<typename T> using QList = QVector<T>" with
+ // other="QList<int>", replace the instantiations to obtain "QVector<int>".
+ auto aliasInstantiations = templateTypeAlias->type().instantiations();
+ const auto &concreteInstantiations = otherType.instantiations();
+ const auto count = qMin(aliasInstantiations.size(), concreteInstantiations.size());
+ for (qsizetype i = 0; i < count; ++i)
+ aliasInstantiations[i] = concreteInstantiations.at(i);
+ combined.setInstantiations(aliasInstantiations);
+ const CodeModelItem nextItem = CodeModel::findItem(combined.qualifiedName(), __scope);
+ if (!nextItem)
+ return combined;
+ return resolveType(nextItem, combined, __scope);
+ }
+
+ return otherType;
+}
+
+// Handler for clang::parseTemplateArgumentList() that populates
+// TypeInfo::m_instantiations
+class TypeInfoTemplateArgumentHandler
+{
+public:
+ explicit TypeInfoTemplateArgumentHandler(TypeInfo *t)
+ {
+ m_parseStack.append(t);
+ }
+
+ void operator()(int level, QStringView name)
+ {
+ if (level > m_parseStack.size()) {
+ Q_ASSERT(!top()->instantiations().isEmpty());
+ m_parseStack.push(&top()->instantiations().back());
+ }
+ while (level < m_parseStack.size())
+ m_parseStack.pop();
+ TypeInfo instantiation;
+ if (name.startsWith(u"const ")) {
+ instantiation.setConstant(true);
+ name = name.mid(6);
+ }
+ instantiation.setQualifiedName(qualifiedName(name));
+ top()->addInstantiation(instantiation);
+ }
+
+private:
+ TypeInfo *top() const { return m_parseStack.back(); }
+
+ static QStringList qualifiedName(QStringView name)
+ {
+ QStringList result;
+ const auto nameParts = name.split(u"::");
+ result.reserve(nameParts.size());
+ for (const auto &p : nameParts)
+ result.append(p.toString());
+ return result;
+ }
+
+ QStack<TypeInfo *> m_parseStack;
+};
+
+std::pair<qsizetype, qsizetype>
+ TypeInfo::parseTemplateArgumentList(const QString &l, qsizetype from)
+{
+ return clang::parseTemplateArgumentList(l, clang::TemplateArgumentHandler(TypeInfoTemplateArgumentHandler(this)), from);
+}
+
+QString TypeInfo::toString() const
+{
+ QString tmp;
+ if (isConstant())
+ tmp += u"const "_s;
+
+ if (isVolatile())
+ tmp += u"volatile "_s;
+
+ tmp += d->m_qualifiedName.join(u"::"_s);
+
+ if (const auto instantiationCount = d->m_instantiations.size()) {
+ tmp += u'<';
+ for (qsizetype i = 0; i < instantiationCount; ++i) {
+ if (i)
+ tmp += u", "_s;
+ tmp += d->m_instantiations.at(i).toString();
+ }
+ if (tmp.endsWith(u'>'))
+ tmp += u' ';
+ tmp += u'>';
+ }
+
+ for (Indirection i : d->m_indirections)
+ tmp.append(indirectionKeyword(i));
+
+ switch (referenceType()) {
+ case NoReference:
+ break;
+ case LValueReference:
+ tmp += u'&';
+ break;
+ case RValueReference:
+ tmp += u"&&"_s;
+ break;
+ }
+
+ if (isFunctionPointer()) {
+ tmp += u" (*)("_s;
+ for (qsizetype i = 0; i < d->m_arguments.size(); ++i) {
+ if (i != 0)
+ tmp += u", "_s;
+
+ tmp += d->m_arguments.at(i).toString();
+ }
+ tmp += u')';
+ }
+
+ for (const QString &elt : d->m_arrayElements)
+ tmp += u'[' + elt + u']';
+
+ return tmp;
+}
+
+bool TypeInfoData::equals(const TypeInfoData &other) const
+{
+ if (m_arrayElements.size() != other.m_arrayElements.size())
+ return false;
+
+#if defined (RXX_CHECK_ARRAY_ELEMENTS) // ### it'll break
+ for (qsizetype i = 0; i < arrayElements().size(); ++i) {
+ QString elt1 = arrayElements().at(i).trimmed();
+ QString elt2 = other.arrayElements().at(i).trimmed();
+
+ if (elt1 != elt2)
+ return false;
+ }
+#endif
+
+ return flags == other.flags
+ && m_qualifiedName == other.m_qualifiedName
+ && (!m_functionPointer || m_arguments == other.m_arguments)
+ && m_instantiations == other.m_instantiations;
+}
+
+
+bool comparesEqual(const TypeInfo &lhs, const TypeInfo &rhs) noexcept
+{
+ return lhs.d.data() == rhs.d.data() || lhs.d->equals(*rhs.d);
+}
+
+QString TypeInfo::indirectionKeyword(Indirection i)
+{
+ return i == Indirection::Pointer ? "*"_L1 : "*const"_L1;
+}
+
+bool TypeInfo::stripLeadingConst(QString *s)
+{
+ return stripLeadingQualifier("const"_L1, s);
+}
+
+bool TypeInfo::stripLeadingVolatile(QString *s)
+{
+ return stripLeadingQualifier("volatile"_L1, s);
+}
+
+bool TypeInfo::stripLeadingQualifier(QLatin1StringView qualifier, QString *s)
+{
+ // "const int x"
+ const auto qualifierSize = qualifier.size();
+ if (s->size() < qualifierSize + 1 || !s->startsWith(qualifier)
+ || !s->at(qualifierSize).isSpace()) {
+ return false;
+ }
+ s->remove(0, qualifierSize + 1);
+ while (!s->isEmpty() && s->at(0).isSpace())
+ s->remove(0, 1);
+ return true;
+}
+
+// Strip all const/volatile/*/&
+void TypeInfo::stripQualifiers(QString *s)
+{
+ stripLeadingConst(s);
+ stripLeadingVolatile(s);
+ while (s->endsWith(u'&') || s->endsWith(u'*') || s->endsWith(u' '))
+ s->chop(1);
+}
+
+// Helper functionality to simplify a raw standard type as returned by
+// clang_getCanonicalType() for g++ standard containers from
+// "std::__cxx11::list<int, std::allocator<int> >" or
+// "std::__1::list<int, std::allocator<int> >" -> "std::list<int>".
+
+bool TypeInfoData::isStdType() const
+{
+ return m_qualifiedName.size() > 1
+ && m_qualifiedName.constFirst() == u"std";
+}
+
+bool TypeInfo::isStdType() const
+{
+ return d->isStdType();
+}
+
+static inline bool discardStdType(const QString &name)
+{
+ return name == u"allocator" || name == u"less";
+}
+
+void TypeInfoData::simplifyStdType()
+{
+ Q_ASSERT(isStdType());
+ if (m_qualifiedName.at(1).startsWith(u"__"))
+ m_qualifiedName.removeAt(1);
+ for (auto t = m_instantiations.size() - 1; t >= 0; --t) {
+ if (m_instantiations.at(t).isStdType()) {
+ if (discardStdType(m_instantiations.at(t).qualifiedName().constLast()))
+ m_instantiations.removeAt(t);
+ else
+ m_instantiations[t].simplifyStdType();
+ }
+ }
+}
+
+void TypeInfo::simplifyStdType()
+{
+ if (isStdType())
+ d->simplifyStdType();
+}
+
+void TypeInfo::formatTypeSystemSignature(QTextStream &str) const
+{
+ if (d->m_constant)
+ str << "const ";
+ str << d->m_qualifiedName.join(u"::"_s);
+ switch (d->m_referenceType) {
+ case NoReference:
+ break;
+ case LValueReference:
+ str << '&';
+ break;
+ case RValueReference:
+ str << "&&";
+ break;
+ }
+ for (auto i : d->m_indirections) {
+ switch (i) {
+ case Indirection::Pointer:
+ str << '*';
+ break;
+ case Indirection::ConstPointer:
+ str << "* const";
+ break;
+ }
+ }
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void TypeInfo::formatDebug(QDebug &debug) const
+{
+ debug << '"';
+ formatSequence(debug, d->m_qualifiedName.begin(), d->m_qualifiedName.end(), "\", \"");
+ debug << '"';
+ if (d->m_constant)
+ debug << ", [const]";
+ if (d->m_volatile)
+ debug << ", [volatile]";
+ if (!d->m_indirections.isEmpty()) {
+ debug << ", indirections=";
+ for (auto i : d->m_indirections)
+ debug << ' ' << TypeInfo::indirectionKeyword(i);
+ }
+ switch (d->m_referenceType) {
+ case NoReference:
+ break;
+ case LValueReference:
+ debug << ", [ref]";
+ break;
+ case RValueReference:
+ debug << ", [rvalref]";
+ break;
+ }
+ if (!d->m_instantiations.isEmpty()) {
+ debug << ", template<";
+ formatSequence(debug, d->m_instantiations.begin(), d->m_instantiations.end());
+ debug << '>';
+ }
+ if (d->m_functionPointer) {
+ debug << ", function ptr(";
+ formatSequence(debug, d->m_arguments.begin(), d->m_arguments.end());
+ debug << ')';
+ }
+ if (!d->m_arrayElements.isEmpty()) {
+ debug << ", array[" << d->m_arrayElements.size() << "][";
+ formatSequence(debug, d->m_arrayElements.begin(), d->m_arrayElements.end());
+ debug << ']';
+ }
+}
+
+QDebug operator<<(QDebug d, const TypeInfo &t)
+{
+ QDebugStateSaver s(d);
+ const int verbosity = d.verbosity();
+ d.noquote();
+ d.nospace();
+ d << "TypeInfo(";
+ if (verbosity > 2)
+ t.formatDebug(d);
+ else
+ d << t.toString();
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/parser/typeinfo.h b/sources/shiboken6/ApiExtractor/parser/typeinfo.h
new file mode 100644
index 000000000..e4f363b67
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/parser/typeinfo.h
@@ -0,0 +1,128 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2002-2005 Roberto Raggi <roberto@kdevelop.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEINFO_H
+#define TYPEINFO_H
+
+#include "codemodel_enums.h"
+#include "codemodel_fwd.h"
+
+#include <QtCore/QString>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QtCompare>
+#include <QtCore/QStringList>
+
+#include <utility>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+
+class TypeInfoData;
+
+class TypeInfo
+{
+ friend class TypeParser;
+public:
+ using Indirections = QList<Indirection>;
+ using TypeInfoList = QList<TypeInfo>;
+
+ TypeInfo();
+ ~TypeInfo();
+ TypeInfo(const TypeInfo &);
+ TypeInfo& operator=(const TypeInfo &);
+ TypeInfo(TypeInfo &&) noexcept;
+ TypeInfo &operator=(TypeInfo &&) noexcept;
+
+ static TypeInfo voidType();
+ static TypeInfo varArgsType();
+
+ QStringList qualifiedName() const;
+ void setQualifiedName(const QStringList &qualified_name);
+ void addName(const QString &);
+
+ bool isVoid() const;
+
+ bool isConstant() const;
+ void setConstant(bool is);
+
+ bool isVolatile() const;
+
+ void setVolatile(bool is);
+
+ ReferenceType referenceType() const;
+ void setReferenceType(ReferenceType r);
+
+ const Indirections &indirectionsV() const;
+ void setIndirectionsV(const Indirections &i);
+ void addIndirection(Indirection i);
+
+ // "Legacy", rename?
+ int indirections() const;
+
+ void setIndirections(int indirections);
+
+ bool isFunctionPointer() const;
+ void setFunctionPointer(bool is);
+
+ const QStringList &arrayElements() const;
+ void setArrayElements(const QStringList &arrayElements);
+
+ void addArrayElement(const QString &a);
+
+ const TypeInfoList &arguments() const;
+ void setArguments(const TypeInfoList &arguments);
+ void addArgument(const TypeInfo &arg);
+
+ const TypeInfoList &instantiations() const;
+ TypeInfoList &instantiations(); // for parsing only
+ void setInstantiations(const TypeInfoList &i);
+ void addInstantiation(const TypeInfo &i);
+ void clearInstantiations();
+
+ bool isPlain() const; // neither const,volatile, no indirections/references, array
+
+ bool isStdType() const;
+
+ std::pair<qsizetype, qsizetype>
+ parseTemplateArgumentList(const QString &l, qsizetype from = 0);
+
+ // ### arrays and templates??
+
+ QString toString() const;
+
+ static TypeInfo combine(const TypeInfo &__lhs, const TypeInfo &__rhs);
+ static TypeInfo resolveType(TypeInfo const &__type, const ScopeModelItem &__scope);
+
+ void formatTypeSystemSignature(QTextStream &str) const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+ static QString indirectionKeyword(Indirection i);
+
+ static bool stripLeadingConst(QString *s);
+ static bool stripLeadingVolatile(QString *s);
+ static bool stripLeadingQualifier(QLatin1StringView qualifier, QString *s);
+ static void stripQualifiers(QString *s);
+
+ void simplifyStdType();
+
+private:
+ friend bool comparesEqual(const TypeInfo &lhs,
+ const TypeInfo &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(TypeInfo)
+
+ QSharedDataPointer<TypeInfoData> d;
+
+ friend class TypeInfoTemplateArgumentHandler;
+
+ static TypeInfo resolveType(CodeModelItem item, TypeInfo const &__type, const ScopeModelItem &__scope);
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const TypeInfo &t);
+#endif
+
+#endif // TYPEINFO_H
diff --git a/sources/shiboken6/ApiExtractor/predefined_templates.cpp b/sources/shiboken6/ApiExtractor/predefined_templates.cpp
new file mode 100644
index 000000000..992f735ac
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/predefined_templates.cpp
@@ -0,0 +1,276 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "predefined_templates.h"
+
+#include "qtcompat.h"
+
+using namespace Qt::StringLiterals;
+
+static QString pySequenceToCppContainer(const QString &insertFunc,
+ bool reserve)
+{
+ QString result = u"(%out).clear();\n"_s;
+ if (reserve) {
+ result += uR"(if (PyList_Check(%in)) {
+ const Py_ssize_t size = PySequence_Size(%in);
+ if (size > 10)
+ (%out).reserve(size);
+}
+
+)"_s;
+ }
+
+ result += uR"(Shiboken::AutoDecRef it(PyObject_GetIter(%in));
+while (true) {
+ Shiboken::AutoDecRef pyItem(PyIter_Next(it.object()));
+ if (pyItem.isNull()) {
+ if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration))
+ PyErr_Clear();
+ break;
+ }
+ %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem);
+ (%out).)"_s;
+
+ result += insertFunc;
+ result += uR"((cppItem);
+}
+)"_s;
+ return result;
+}
+
+// Convert a sequence to a limited/fixed array
+static QString pySequenceToCppArray()
+{
+ return uR"(Shiboken::AutoDecRef it(PyObject_GetIter(%in));
+for (auto oit = std::begin(%out), oend = std::end(%out); oit != oend; ++oit) {
+ Shiboken::AutoDecRef pyItem(PyIter_Next(it.object()));
+ if (pyItem.isNull()) {
+ if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration))
+ PyErr_Clear();
+ break;
+ }
+ %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem);
+ *oit = cppItem;
+}
+)"_s;
+}
+
+static constexpr auto stlMapKeyAccessor = "->first"_L1;
+static constexpr auto stlMapValueAccessor = "->second"_L1;
+static constexpr auto qtMapKeyAccessor = ".key()"_L1;
+static constexpr auto qtMapValueAccessor = ".value()"_L1;
+
+static QString cppMapToPyDict(bool isQMap)
+{
+ return uR"(PyObject *%out = PyDict_New();
+for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ++it) {
+ const auto &key = it)"_s
+ + (isQMap ? qtMapKeyAccessor : stlMapKeyAccessor)
+ + uR"(;
+ const auto &value = it)"_s
+ + (isQMap ? qtMapValueAccessor : stlMapValueAccessor)
+ + uR"(;
+ PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key);
+ PyObject *pyValue = %CONVERTTOPYTHON[%INTYPE_1](value);
+ PyDict_SetItem(%out, pyKey, pyValue);
+ Py_DECREF(pyKey);
+ Py_DECREF(pyValue);
+}
+return %out;
+)"_s;
+}
+
+static QString pyDictToCppMap(bool isQMap)
+{
+ return uR"(PyObject *key;
+PyObject *value;
+%out.clear();
+Py_ssize_t pos = 0;
+while (PyDict_Next(%in, &pos, &key, &value)) {
+ %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key);
+ %OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value);
+ %out.insert()"_s
+ // STL needs a pair
+ + (isQMap ? u"cppKey, cppValue"_s : u"{cppKey, cppValue}"_s) + uR"();
+}
+)"_s;
+}
+
+// Convert a STL or Qt multi map to Dict of Lists using upperBound()
+static QString cppMultiMapToPyDict(bool isQMultiMap)
+{
+ return uR"(PyObject *%out = PyDict_New();
+ for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ) {
+ const auto &key = it)"_s
+ + (isQMultiMap ? qtMapKeyAccessor : stlMapKeyAccessor)
+ + uR"(;
+ PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key);
+ auto upper = %in.)"_s
+ + (isQMultiMap ? u"upperBound"_s : u"upper_bound"_s)
+ + uR"((key);
+ const auto count = Py_ssize_t(std::distance(it, upper));
+ PyObject *pyValues = PyList_New(count);
+ Py_ssize_t idx = 0;
+ for (; it != upper; ++it, ++idx) {
+ const auto &cppItem = it.value();
+ PyList_SET_ITEM(pyValues, idx, %CONVERTTOPYTHON[%INTYPE_1](cppItem));
+ }
+ PyDict_SetItem(%out, pyKey, pyValues);
+ Py_DECREF(pyKey);
+ }
+ return %out;
+)"_s;
+}
+
+// Convert a STL or Qt multi hash to Dict of Lists using equalRange()
+static QString cppMultiHashToPyDict(bool isQMultiHash)
+{
+ return uR"(PyObject *%out = PyDict_New();
+ for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ) {
+ const auto &key = it)"_s
+ + (isQMultiHash ? qtMapKeyAccessor : stlMapKeyAccessor)
+ + uR"(;
+ PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key);
+ auto range = %in.equal_range(key);
+ const auto count = Py_ssize_t(std::distance(range.first, range.second));
+ PyObject *pyValues = PyList_New(count);
+ Py_ssize_t idx = 0;
+ for (; it != range.second; ++it, ++idx) {
+ const auto &cppItem = it.value();
+ PyList_SET_ITEM(pyValues, idx, %CONVERTTOPYTHON[%INTYPE_1](cppItem));
+ }
+ PyDict_SetItem(%out, pyKey, pyValues);
+ Py_DECREF(pyKey);
+ }
+ return %out;
+)"_s;
+}
+
+// Convert Dict of Lists to a STL or Qt multi hash/map
+static QString pyDictToCppMultiHash(bool isQMultiHash)
+{
+ return uR"(PyObject *key;
+ PyObject *values;
+ %out.clear();
+ Py_ssize_t pos = 0;
+ while (PyDict_Next(%in, &pos, &key, &values)) {
+ %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key);
+ const Py_ssize_t size = PySequence_Size(values);
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ Shiboken::AutoDecRef value(PySequence_GetItem(values, i));
+ %OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value);
+ %out.insert()"_s
+ + (isQMultiHash ? u"cppKey, cppValue"_s : u"{cppKey, cppValue}"_s)
+ + uR"();
+ }
+ }
+)"_s;
+}
+
+const PredefinedTemplates &predefinedTemplates()
+{
+ static const PredefinedTemplates result{
+ {u"shiboken_conversion_pylong_to_cpp"_s,
+ u"%out = %OUTTYPE(PyLong_AsLong(%in));\n"_s},
+
+ // QPair/std::pair
+ {u"shiboken_conversion_pysequence_to_cpppair"_s,
+ uR"(%out.first = %CONVERTTOCPP[%OUTTYPE_0](PySequence_Fast_GET_ITEM(%in, 0));
+%out.second = %CONVERTTOCPP[%OUTTYPE_1](PySequence_Fast_GET_ITEM(%in, 1));
+)"_s},
+
+ {u"shiboken_conversion_cpppair_to_pytuple"_s,
+ uR"(PyObject *%out = PyTuple_New(2);
+PyTuple_SET_ITEM(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first));
+PyTuple_SET_ITEM(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second));
+return %out;
+)"_s},
+
+ // Sequential containers
+ {u"shiboken_conversion_cppsequence_to_pylist"_s,
+ uR"(PyObject *%out = PyList_New(Py_ssize_t(%in.size()));
+Py_ssize_t idx = 0;
+for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ++it, ++idx) {
+ const auto &cppItem = *it;
+ PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem));
+}
+return %out;)"_s},
+
+ // PySet
+ {u"shiboken_conversion_cppsequence_to_pyset"_s,
+ uR"(PyObject *%out = PySet_New(nullptr);
+for (const auto &cppItem : %in) {
+ PySet_Add(%out, %CONVERTTOPYTHON[%INTYPE_0](cppItem));
+}
+return %out;)"_s},
+
+ {u"shiboken_conversion_pyiterable_to_cppsequentialcontainer"_s,
+ pySequenceToCppContainer(u"push_back"_s, false)},
+ {u"shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"_s,
+ pySequenceToCppContainer(u"push_back"_s, true)},
+ {u"shiboken_conversion_pyiterable_to_cpparray"_s,
+ pySequenceToCppArray()},
+ {u"shiboken_conversion_pyiterable_to_cppsetcontainer"_s,
+ pySequenceToCppContainer(u"insert"_s, false)},
+
+ // Maps
+ {u"shiboken_conversion_stdmap_to_pydict"_s,
+ cppMapToPyDict(false)},
+ {u"shiboken_conversion_qmap_to_pydict"_s,
+ cppMapToPyDict(true)},
+ {u"shiboken_conversion_pydict_to_stdmap"_s,
+ pyDictToCppMap(false)},
+ {u"shiboken_conversion_pydict_to_qmap"_s,
+ pyDictToCppMap(true)},
+
+ // Multi maps
+ {u"shiboken_conversion_stdmultimap_to_pydict"_s,
+ cppMultiMapToPyDict(false)},
+ {u"shiboken_conversion_qmultimap_to_pydict"_s,
+ cppMultiMapToPyDict(true)},
+
+ // Multi hashes
+ {u"shiboken_conversion_stdunorderedmultimap_to_pydict"_s,
+ cppMultiHashToPyDict(false)},
+ {u"shiboken_conversion_qmultihash_to_pydict"_s,
+ cppMultiHashToPyDict(true)},
+
+ // STL multi hash/map
+ {u"shiboken_conversion_pydict_to_stdmultimap"_s,
+ pyDictToCppMultiHash(false)},
+ {u"shiboken_conversion_pydict_to_qmultihash"_s,
+ pyDictToCppMultiHash(true)}
+ };
+
+ return result;
+}
+
+QByteArray containerTypeSystemSnippet(const char *name, const char *type,
+ const char *include,
+ const char *nativeToTarget,
+ const char *targetToNativeType,
+ const char *targetToNative)
+{
+ QByteArray result = QByteArrayLiteral("<container-type name=\"")
+ + name + QByteArrayLiteral("\" type=\"") + type + R"(">
+ <include file-name=")" + include + R"(" location="global"/>
+ <conversion-rule>
+ <native-to-target>
+ <insert-template name=")" + nativeToTarget + R"("/>
+ </native-to-target>
+)";
+ if (targetToNativeType != nullptr) {
+ result += QByteArrayLiteral(R"( <target-to-native>
+ <add-conversion type=")") + targetToNativeType
+ + QByteArrayLiteral(R"(">
+ <insert-template name=")") + targetToNative + QByteArrayLiteral(R"("/>
+ </add-conversion>
+ </target-to-native>
+)");
+ }
+result += QByteArrayLiteral(R"( </conversion-rule>
+</container-type>
+)");
+ return result;
+}
diff --git a/sources/shiboken6/ApiExtractor/predefined_templates.h b/sources/shiboken6/ApiExtractor/predefined_templates.h
new file mode 100644
index 000000000..0cc2c7f32
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/predefined_templates.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PREDEFINED_TEMPLATES_H
+#define PREDEFINED_TEMPLATES_H
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+struct PredefinedTemplate
+{
+ QString name;
+ QString content;
+};
+
+using PredefinedTemplates = QList<PredefinedTemplate>;
+
+const PredefinedTemplates &predefinedTemplates();
+
+// Create an XML snippet for a container type.
+QByteArray containerTypeSystemSnippet(const char *name, const char *type,
+ const char *include,
+ const char *nativeToTarget,
+ const char *targetToNativeType = nullptr,
+ const char *targetToNative = nullptr);
+
+#endif // PREDEFINED_TEMPLATES_H
diff --git a/sources/shiboken6/ApiExtractor/primitivetypeentry.h b/sources/shiboken6/ApiExtractor/primitivetypeentry.h
new file mode 100644
index 000000000..6faaf7a61
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/primitivetypeentry.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PRIMITIVETYPEENTRY_H
+#define PRIMITIVETYPEENTRY_H
+
+#include "typesystem.h"
+#include "customconversion_typedefs.h"
+
+class PrimitiveTypeEntryPrivate;
+
+/// A PrimitiveTypeEntry is user-defined type with conversion rules, a C++
+/// primitive type for which a PrimitiveTypeConverter exists in libshiboken
+/// or a typedef to a C++ primitive type as determined by AbstractMetaBuilder.
+class PrimitiveTypeEntry : public TypeEntry
+{
+public:
+ explicit PrimitiveTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ QString defaultConstructor() const;
+ void setDefaultConstructor(const QString& defaultConstructor);
+ bool hasDefaultConstructor() const;
+
+ /**
+ * The PrimitiveTypeEntry pointed by this type entry if it
+ * represents a typedef).
+ * \return the type referenced by the typedef, or a null pointer
+ * if the current object is not an typedef
+ */
+ PrimitiveTypeEntryPtr referencedTypeEntry() const;
+
+ /**
+ * Defines type referenced by this entry.
+ * \param referencedTypeEntry type referenced by this entry
+ */
+ void setReferencedTypeEntry(PrimitiveTypeEntryPtr referencedTypeEntry);
+
+ /// Returns whether this entry references another entry.
+ bool referencesType() const;
+
+ bool preferredTargetLangType() const;
+ void setPreferredTargetLangType(bool b);
+
+ bool hasCustomConversion() const;
+ void setCustomConversion(const CustomConversionPtr &customConversion);
+ CustomConversionPtr customConversion() const;
+
+ TypeEntry *clone() const override;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+protected:
+ explicit PrimitiveTypeEntry(PrimitiveTypeEntryPrivate *d);
+};
+
+/// Finds the most basic primitive type that the typedef represents,
+/// i.e. a type that is not an typedef'ed.
+/// \return the most basic non-typedef'ed primitive type represented
+/// by this typedef or self in case it is not a reference.
+PrimitiveTypeEntryCPtr basicReferencedTypeEntry(const PrimitiveTypeEntryCPtr &e);
+PrimitiveTypeEntryCPtr basicReferencedTypeEntry(const TypeEntryCPtr &e);
+
+/// Finds the basic primitive type that the typedef represents
+/// and was explicitly specified in the type system.
+/// \return the basic primitive type that was explicitly specified in
+/// the type system.
+PrimitiveTypeEntryCPtr basicReferencedNonBuiltinTypeEntry(const PrimitiveTypeEntryCPtr &e);
+
+#endif // PRIMITIVETYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/propertyspec.cpp b/sources/shiboken6/ApiExtractor/propertyspec.cpp
new file mode 100644
index 000000000..32b756fad
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/propertyspec.cpp
@@ -0,0 +1,347 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "propertyspec.h"
+#include "abstractmetalang.h"
+#include "abstractmetabuilder_p.h"
+#include "abstractmetatype.h"
+#include "documentation.h"
+#include "messages.h"
+#include "complextypeentry.h"
+#include "typeinfo.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QHash>
+
+#ifndef QT_NO_DEBUG_STREAM
+# include <QtCore/QDebug>
+#endif
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+class QPropertySpecData : public QSharedData
+{
+public:
+ QPropertySpecData(const TypeSystemProperty &ts,
+ const AbstractMetaType &type) :
+ m_name(ts.name),
+ m_read(ts.read),
+ m_write(ts.write),
+ m_designable(ts.designable),
+ m_reset(ts.reset),
+ m_notify(ts.notify),
+ m_type(type),
+ m_generateGetSetDef(ts.generateGetSetDef)
+ {
+ }
+
+ QString m_name;
+ QString m_read;
+ QString m_write;
+ QString m_designable;
+ QString m_reset;
+ QString m_notify;
+ Documentation m_documentation;
+ AbstractMetaType m_type;
+ int m_index = -1;
+ // Indicates whether actual code is generated instead of relying on libpyside.
+ bool m_generateGetSetDef = false;
+};
+
+QPropertySpec::QPropertySpec(const TypeSystemProperty &ts,
+ const AbstractMetaType &type) :
+ d(new QPropertySpecData(ts, type))
+{
+}
+
+QPropertySpec::QPropertySpec(const QPropertySpec &) = default;
+QPropertySpec &QPropertySpec::operator=(const QPropertySpec &) = default;
+QPropertySpec::QPropertySpec(QPropertySpec &&) noexcept = default;
+QPropertySpec &QPropertySpec::operator=(QPropertySpec &&) noexcept = default;
+QPropertySpec::~QPropertySpec() = default;
+
+const AbstractMetaType &QPropertySpec::type() const
+{
+ return d->m_type;
+}
+
+void QPropertySpec::setType(const AbstractMetaType &t)
+{
+ if (d->m_type != t)
+ d->m_type = t;
+}
+
+TypeEntryCPtr QPropertySpec::typeEntry() const
+{
+ return d->m_type.typeEntry();
+}
+
+QString QPropertySpec::name() const
+{
+ return d->m_name;
+}
+
+void QPropertySpec::setName(const QString &name)
+{
+ if (d->m_name != name)
+ d->m_name = name;
+}
+
+Documentation QPropertySpec::documentation() const
+{
+ return d->m_documentation;
+}
+
+void QPropertySpec::setDocumentation(const Documentation &doc)
+{
+ if (d->m_documentation != doc)
+ d->m_documentation = doc;
+}
+
+QString QPropertySpec::read() const
+{
+ return d->m_read;
+}
+
+void QPropertySpec::setRead(const QString &read)
+{
+ if (d->m_read != read)
+ d->m_read = read;
+}
+
+QString QPropertySpec::write() const
+{
+ return d->m_write;
+}
+
+void QPropertySpec::setWrite(const QString &write)
+{
+ if (d->m_write != write)
+ d->m_write = write;
+}
+
+bool QPropertySpec::hasWrite() const
+{
+ return !d->m_write.isEmpty();
+}
+
+QString QPropertySpec::designable() const
+{
+ return d->m_designable;
+}
+
+void QPropertySpec::setDesignable(const QString &designable)
+{
+ if (d->m_designable != designable)
+ d->m_designable = designable;
+}
+
+QString QPropertySpec::reset() const
+{
+ return d->m_reset;
+}
+
+void QPropertySpec::setReset(const QString &reset)
+{
+ if (d->m_reset != reset)
+ d->m_reset = reset;
+}
+
+QString QPropertySpec::notify() const
+{
+ return d->m_notify;
+}
+
+void QPropertySpec::setNotify(const QString &notify)
+{
+ if (d->m_notify != notify)
+ d->m_notify = notify;
+}
+
+int QPropertySpec::index() const
+{
+ return d->m_index;
+}
+
+void QPropertySpec::setIndex(int index)
+{
+ if (d->m_index != index)
+ d->m_index = index;
+}
+
+bool QPropertySpec::generateGetSetDef() const
+{
+ return d->m_generateGetSetDef;
+}
+
+void QPropertySpec::setGenerateGetSetDef(bool generateGetSetDef)
+{
+ if (d->m_generateGetSetDef != generateGetSetDef)
+ d->m_generateGetSetDef = generateGetSetDef;
+}
+
+// Parse a Q_PROPERTY macro
+// Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)
+// into a TypeSystemProperty.
+TypeSystemProperty QPropertySpec::typeSystemPropertyFromQ_Property(const QString &declarationIn,
+ QString *errorMessage)
+{
+ enum class PropertyToken { None, Read, Write, Designable, Reset, Notify, Member };
+
+ static const QHash<QString, PropertyToken> tokenLookup = {
+ {"READ"_L1, PropertyToken::Read},
+ {"WRITE"_L1, PropertyToken::Write},
+ {"DESIGNABLE"_L1, PropertyToken::Designable},
+ {"RESET"_L1, PropertyToken::Reset},
+ {"NOTIFY"_L1, PropertyToken::Notify},
+ {"MEMBER"_L1, PropertyToken::Member}
+ };
+
+ errorMessage->clear();
+
+ TypeSystemProperty result;
+
+ // Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)
+
+ const QString declaration = declarationIn.simplified();
+ auto propertyTokens = declaration.split(u' ', Qt::SkipEmptyParts);
+
+ // To properly parse complicated type declarations like
+ // "Q_PROPERTY(const QList<QString > *objectName READ objectName ..."
+ // we first search the first "READ" token, parse the subsequent tokens and
+ // extract type and name from the tokens before "READ".
+ const auto it = std::find_if(propertyTokens.cbegin(), propertyTokens.cend(),
+ [](const QString &t) { return tokenLookup.contains(t); });
+ if (it == propertyTokens.cend()) {
+ *errorMessage = u"Invalid property specification, READ missing"_s;
+ return result;
+ }
+
+ const auto firstToken = qsizetype(it - propertyTokens.cbegin());
+ if (firstToken < 2) {
+ *errorMessage = u"Insufficient number of tokens in property specification"_s;
+ return result;
+ }
+
+ for (qsizetype pos = firstToken; pos + 1 < propertyTokens.size(); pos += 2) {
+ switch (tokenLookup.value(propertyTokens.at(pos))) {
+ case PropertyToken::Read:
+ result.read = propertyTokens.at(pos + 1);
+ break;
+ case PropertyToken::Write:
+ result.write = propertyTokens.at(pos + 1);
+ break;
+ case PropertyToken::Reset:
+ result.reset = propertyTokens.at(pos + 1);
+ break;
+ case PropertyToken::Designable:
+ result.designable = propertyTokens.at(pos + 1);
+ break;
+ case PropertyToken::Notify:
+ result.notify = propertyTokens.at(pos + 1);
+ break;
+ case PropertyToken::Member:
+ // Ignore MEMBER tokens introduced by QTBUG-16852 as Python
+ // properties are anyways generated for fields.
+ return {};
+
+ case PropertyToken::None:
+ break;
+ }
+ }
+
+ const auto namePos = firstToken - 1;
+ result.name = propertyTokens.at(namePos);
+
+ result.type = propertyTokens.constFirst();
+ for (qsizetype pos = 1; pos < namePos; ++pos)
+ result.type += u' ' + propertyTokens.at(pos);
+
+ // Fix errors like "Q_PROPERTY(QXYSeries *series .." to be of type "QXYSeries*"
+ while (!result.name.isEmpty() && !result.name.at(0).isLetter()) {
+ result.type += result.name.at(0);
+ result.name.remove(0, 1);
+ }
+ if (!result.isValid())
+ *errorMessage = u"Incomplete property specification"_s;
+ return result;
+}
+
+// Create a QPropertySpec from a TypeSystemProperty, determining
+// the AbstractMetaType from the type string.
+std::optional<QPropertySpec>
+ QPropertySpec::fromTypeSystemProperty(AbstractMetaBuilderPrivate *b,
+ const AbstractMetaClassPtr &metaClass,
+ const TypeSystemProperty &ts,
+ const QStringList &scopes,
+ QString *errorMessage)
+ {
+ Q_ASSERT(ts.isValid());
+ QString typeError;
+ TypeInfo info = TypeParser::parse(ts.type, &typeError);
+ if (info.qualifiedName().isEmpty()) {
+ *errorMessage = msgPropertyTypeParsingFailed(ts.name, ts.type, typeError);
+ return {};
+ }
+
+ auto type = b->translateType(info, metaClass, {}, &typeError);
+ if (!type.has_value()) {
+ const QStringList qualifiedName = info.qualifiedName();
+ for (auto j = scopes.size(); j >= 0 && !type; --j) {
+ info.setQualifiedName(scopes.mid(0, j) + qualifiedName);
+ type = b->translateType(info, metaClass, {}, &typeError);
+ }
+ }
+
+ if (!type.has_value()) {
+ *errorMessage = msgPropertyTypeParsingFailed(ts.name, ts.type, typeError);
+ return {};
+ }
+ return QPropertySpec(ts, type.value());
+ }
+
+// Convenience to create a QPropertySpec from a Q_PROPERTY macro
+// via TypeSystemProperty.
+std::optional<QPropertySpec>
+ QPropertySpec::parseQ_Property(AbstractMetaBuilderPrivate *b,
+ const AbstractMetaClassPtr &metaClass,
+ const QString &declarationIn,
+ const QStringList &scopes,
+ QString *errorMessage)
+{
+ const TypeSystemProperty ts =
+ typeSystemPropertyFromQ_Property(declarationIn, errorMessage);
+ if (!ts.isValid())
+ return {};
+ return fromTypeSystemProperty(b, metaClass, ts, scopes, errorMessage);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void QPropertySpec::formatDebug(QDebug &debug) const
+{
+ debug << '#' << d->m_index << " \"" << d->m_name << "\" (" << d->m_type.cppSignature();
+ debug << "), read=" << d->m_read;
+ if (!d->m_write.isEmpty())
+ debug << ", write=" << d->m_write;
+ if (!d->m_reset.isEmpty())
+ debug << ", reset=" << d->m_reset;
+ if (!d->m_designable.isEmpty())
+ debug << ", designable=" << d->m_designable;
+ if (!d->m_documentation.isEmpty())
+ debug << ", doc=\"" << d->m_documentation << '"';
+}
+
+QDebug operator<<(QDebug d, const QPropertySpec &p)
+{
+ QDebugStateSaver s(d);
+ d.noquote();
+ d.nospace();
+ d << "QPropertySpec(";
+ p.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/propertyspec.h b/sources/shiboken6/ApiExtractor/propertyspec.h
new file mode 100644
index 000000000..9e2e0f3d4
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/propertyspec.h
@@ -0,0 +1,104 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PROPERTYSPEC_H
+#define PROPERTYSPEC_H
+
+class AbstractMetaType;
+
+#include "abstractmetalang_typedefs.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QSharedDataPointer>
+
+#include <optional>
+
+class AbstractMetaClass;
+class AbstractMetaBuilderPrivate;
+class AbstractMetaType;
+class Documentation;
+class TypeEntry;
+
+struct TypeSystemProperty;
+
+class QPropertySpecData;
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class QPropertySpec
+{
+public:
+ explicit QPropertySpec(const TypeSystemProperty &ts,
+ const AbstractMetaType &type);
+ QPropertySpec(const QPropertySpec &);
+ QPropertySpec &operator=(const QPropertySpec &);
+ QPropertySpec(QPropertySpec &&) noexcept;
+ QPropertySpec &operator=(QPropertySpec &&) noexcept;
+ ~QPropertySpec();
+
+ static TypeSystemProperty typeSystemPropertyFromQ_Property(const QString &declarationIn,
+ QString *errorMessage);
+
+
+ static std::optional<QPropertySpec>
+ fromTypeSystemProperty(AbstractMetaBuilderPrivate *b,
+ const AbstractMetaClassPtr &metaClass,
+ const TypeSystemProperty &ts,
+ const QStringList &scopes,
+ QString *errorMessage);
+
+ static std::optional<QPropertySpec>
+ parseQ_Property(AbstractMetaBuilderPrivate *b,
+ const AbstractMetaClassPtr &metaClass,
+ const QString &declarationIn,
+ const QStringList &scopes,
+ QString *errorMessage);
+
+ const AbstractMetaType &type() const;
+ void setType(const AbstractMetaType &t);
+
+ TypeEntryCPtr typeEntry() const;
+
+ QString name() const;
+ void setName(const QString &name);
+
+ Documentation documentation() const;
+ void setDocumentation(const Documentation &doc);
+
+ QString read() const;
+ void setRead(const QString &read);
+
+ QString write() const;
+ void setWrite(const QString &write);
+ bool hasWrite() const;
+
+ QString designable() const;
+ void setDesignable(const QString &designable);
+
+ QString reset() const;
+ void setReset(const QString &reset);
+
+ QString notify() const; // Q_PROPERTY/C++ only
+ void setNotify(const QString &notify);
+
+ int index() const;
+ void setIndex(int index);
+
+ // Indicates whether actual code is generated instead of relying on libpyside.
+ bool generateGetSetDef() const;
+ void setGenerateGetSetDef(bool generateGetSetDef);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ QSharedDataPointer<QPropertySpecData> d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QPropertySpec &p);
+#endif
+
+#endif // PROPERTYSPEC_H
diff --git a/sources/shiboken6/ApiExtractor/pymethoddefentry.cpp b/sources/shiboken6/ApiExtractor/pymethoddefentry.cpp
new file mode 100644
index 000000000..64d44378b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/pymethoddefentry.cpp
@@ -0,0 +1,53 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "pymethoddefentry.h"
+#include "textstream.h"
+
+#include <QtCore/QDebug>
+
+TextStream &operator<<(TextStream &str, const castToPyCFunction &c)
+{
+ str << "reinterpret_cast<PyCFunction>(" << c.m_function << ')';
+ return str;
+}
+
+TextStream &operator<<(TextStream &s, const PyMethodDefEntry &e)
+{
+ s << "{\"" << e.name << "\", " << castToPyCFunction(e.function) <<", ";
+ if (e.methFlags.isEmpty()) {
+ s << '0';
+ } else {
+ for (qsizetype i = 0, size = e.methFlags.size(); i < size; ++i) {
+ if (i)
+ s << '|';
+ s << e.methFlags.at(i);
+ }
+ }
+ if (e.doc.isEmpty())
+ s << ", nullptr";
+ else
+ s << ", R\"(" << e.doc << ")\"";
+ s << '}';
+ return s;
+}
+
+TextStream &operator<<(TextStream &s, const PyMethodDefEntries &entries)
+{
+ for (const auto &e : entries)
+ s << e << ",\n";
+ return s;
+}
+
+QDebug operator<<(QDebug debug, const PyMethodDefEntry &e)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "PyMethodDefEntry(\"" << e.name << "\", " << e.function
+ << ", " << e.methFlags;
+ if (!e.doc.isEmpty())
+ debug << ", \"" << e.doc << '"';
+ debug << ')';
+ return debug;
+}
diff --git a/sources/shiboken6/ApiExtractor/pymethoddefentry.h b/sources/shiboken6/ApiExtractor/pymethoddefentry.h
new file mode 100644
index 000000000..a8694eb30
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/pymethoddefentry.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PYMETHODDEFENTRY_H
+#define PYMETHODDEFENTRY_H
+
+#include <QtCore/QByteArrayList>
+#include <QtCore/QString>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+class TextStream;
+
+struct castToPyCFunction
+{
+ explicit castToPyCFunction(QAnyStringView function) noexcept :
+ m_function(function) {}
+
+ QAnyStringView m_function;
+};
+
+struct PyMethodDefEntry
+{
+ QString name;
+ QString function;
+ QByteArrayList methFlags; // "METH_O" etc.
+ QString doc;
+};
+
+using PyMethodDefEntries = QList<PyMethodDefEntry>;
+
+TextStream &operator<<(TextStream &str, const castToPyCFunction &e);
+TextStream &operator<<(TextStream &s, const PyMethodDefEntry &e);
+TextStream &operator<<(TextStream &s, const PyMethodDefEntries &e);
+
+QDebug operator<<(QDebug debug, const PyMethodDefEntry &e);
+
+#endif // PYMETHODDEFENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/pythontypeentry.h b/sources/shiboken6/ApiExtractor/pythontypeentry.h
new file mode 100644
index 000000000..2e0fbda97
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/pythontypeentry.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef PYTHONTYPEENTRY_H
+#define PYTHONTYPEENTRY_H
+
+#include "customtypenentry.h"
+#include "typesystem_enums.h"
+
+class PythonTypeEntry : public CustomTypeEntry
+{
+public:
+ explicit PythonTypeEntry(const QString &entryName,
+ const QString &checkFunction,
+ TypeSystem::CPythonType type);
+
+ TypeEntry *clone() const override;
+
+ TypeSystem::CPythonType cPythonType() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
+protected:
+ explicit PythonTypeEntry(TypeEntryPrivate *d);
+};
+
+#endif // PYTHONTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/qtcompat.h b/sources/shiboken6/ApiExtractor/qtcompat.h
new file mode 100644
index 000000000..3837dcfd2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/qtcompat.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTCOMPAT_H
+#define QTCOMPAT_H
+
+#include <QtCore/qtconfigmacros.h>
+
+#if QT_VERSION < 0x060400
+
+// QTBUG-98434, provide literals of Qt 6.4 for compatibility.
+
+# include <QtCore/QString>
+
+# define QLatin1StringView QLatin1String
+
+namespace Qt {
+inline namespace Literals {
+inline namespace StringLiterals {
+
+constexpr inline QLatin1String operator"" _L1(const char *str, size_t size) noexcept
+{
+ return QLatin1String(str, qsizetype(size));
+}
+
+inline QString operator"" _s(const char16_t *str, size_t size) noexcept
+{
+ return QString(QStringPrivate(nullptr, const_cast<char16_t *>(str), qsizetype(size)));
+}
+
+} // StringLiterals
+} // Literals
+} // Qt
+
+#endif // < 6.4
+
+#endif // QTCOMPAT_H
diff --git a/sources/shiboken6/ApiExtractor/qtdocparser.cpp b/sources/shiboken6/ApiExtractor/qtdocparser.cpp
new file mode 100644
index 000000000..5bd99bbd8
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/qtdocparser.cpp
@@ -0,0 +1,445 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qtdocparser.h"
+#include "classdocumentation.h"
+#include "abstractmetaargument.h"
+#include "abstractmetaenum.h"
+#include "abstractmetafunction.h"
+#include "abstractmetalang.h"
+#include "abstractmetatype.h"
+#include "documentation.h"
+#include "modifications.h"
+#include "messages.h"
+#include "propertyspec.h"
+#include "reporthandler.h"
+#include "flagstypeentry.h"
+#include "complextypeentry.h"
+#include "functiontypeentry.h"
+#include "enumtypeentry.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QUrl>
+
+using namespace Qt::StringLiterals;
+
+enum { debugFunctionSearch = 0 };
+
+constexpr auto briefStartElement = "<brief>"_L1;
+constexpr auto briefEndElement = "</brief>"_L1;
+constexpr auto webxmlSuffix = ".webxml"_L1;
+
+Documentation QtDocParser::retrieveModuleDocumentation()
+{
+ return retrieveModuleDocumentation(packageName());
+}
+
+static void formatPreQualifications(QTextStream &str, const AbstractMetaType &type)
+{
+ if (type.isConstant())
+ str << "const " ;
+}
+
+static void formatPostQualifications(QTextStream &str, const AbstractMetaType &type)
+{
+ if (type.referenceType() == LValueReference)
+ str << " &";
+ else if (type.referenceType() == RValueReference)
+ str << " &&";
+ else if (type.indirections())
+ str << ' ' << QByteArray(type.indirections(), '*');
+}
+
+static void formatFunctionUnqualifiedArgTypeQuery(QTextStream &str,
+ const AbstractMetaType &metaType)
+{
+ switch (metaType.typeUsagePattern()) {
+ case AbstractMetaType::FlagsPattern: {
+ // Modify qualified name "QFlags<Qt::AlignmentFlag>" with name "Alignment"
+ // to "Qt::Alignment" as seen by qdoc.
+ const auto flagsEntry = std::static_pointer_cast<const FlagsTypeEntry>(metaType.typeEntry());
+ QString name = flagsEntry->qualifiedCppName();
+ if (name.endsWith(u'>') && name.startsWith(u"QFlags<")) {
+ const int lastColon = name.lastIndexOf(u':');
+ if (lastColon != -1) {
+ name.replace(lastColon + 1, name.size() - lastColon - 1, metaType.name());
+ name.remove(0, 7);
+ } else {
+ name = metaType.name(); // QFlags<> of enum in global namespace
+ }
+ }
+ str << name;
+ }
+ break;
+ case AbstractMetaType::ContainerPattern: { // QVector<int>
+ str << metaType.typeEntry()->qualifiedCppName() << '<';
+ const auto instantiations = metaType.instantiations();
+ for (qsizetype i = 0, size = instantiations.size(); i < size; ++i) {
+ if (i)
+ str << ", ";
+ const auto &instantiation = instantiations.at(i);
+ formatPreQualifications(str, instantiation);
+ str << instantiation.typeEntry()->qualifiedCppName();
+ formatPostQualifications(str, instantiation);
+ }
+ str << '>';
+ }
+ break;
+ default: // Fully qualify enums (Qt::AlignmentFlag), nested classes, etc.
+ str << metaType.typeEntry()->qualifiedCppName();
+ break;
+ }
+}
+
+static QString formatFunctionArgTypeQuery(const AbstractMetaType &metaType)
+{
+ QString result;
+ QTextStream str(&result);formatPreQualifications(str, metaType);
+ formatFunctionUnqualifiedArgTypeQuery(str, metaType);
+ formatPostQualifications(str, metaType);
+ return result;
+}
+
+QString QtDocParser::functionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func,
+ QString *errorMessage)
+{
+ errorMessage->clear();
+
+ const QString docString =
+ queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass,
+ func, errorMessage);
+
+ const auto funcModifs = DocParser::getXpathDocModifications(func, metaClass);
+ return docString.isEmpty() || funcModifs.isEmpty()
+ ? docString : applyDocModifications(funcModifs, docString);
+}
+
+QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func,
+ QString *errorMessage)
+{
+ // Search candidates by name and const-ness
+ FunctionDocumentationList candidates =
+ classDocumentation.findFunctionCandidates(func->name(), func->isConstant());
+ if (candidates.isEmpty()) {
+ *errorMessage = msgCannotFindDocumentation(sourceFileName, func.get())
+ + u" (no matches)"_s;
+ return {};
+ }
+
+ // Try an exact query
+ FunctionDocumentationQuery fq;
+ fq.name = func->name();
+ fq.constant = func->isConstant();
+ for (const auto &arg : func->arguments())
+ fq.parameters.append(formatFunctionArgTypeQuery(arg.type()));
+
+ const auto funcFlags = func->flags();
+ // Re-add arguments removed by the metabuilder to binary operator functions
+ if (funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorLeadingClassArgumentRemoved)
+ || funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorTrailingClassArgumentRemoved)) {
+ QString classType = metaClass->qualifiedCppName();
+ if (!funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorClassArgumentByValue)) {
+ classType.prepend(u"const "_s);
+ classType.append(u" &"_s);
+ }
+ if (funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorLeadingClassArgumentRemoved))
+ fq.parameters.prepend(classType);
+ else
+ fq.parameters.append(classType);
+ }
+
+ const qsizetype index = ClassDocumentation::indexOfFunction(candidates, fq);
+
+ if (debugFunctionSearch) {
+ qDebug() << __FUNCTION__ << metaClass->name() << fq << funcFlags << "returns"
+ << index << "\n " << candidates.value(index) << "\n " << candidates;
+ }
+
+ if (index != -1)
+ return candidates.at(index).description;
+
+ // Fallback: Try matching by argument count
+ const auto parameterCount = func->arguments().size();
+ auto pend = std::remove_if(candidates.begin(), candidates.end(),
+ [parameterCount](const FunctionDocumentation &fd) {
+ return fd.parameters.size() != parameterCount; });
+ candidates.erase(pend, candidates.end());
+ if (candidates.size() == 1) {
+ const auto &match = candidates.constFirst();
+ QTextStream(errorMessage) << msgFallbackForDocumentation(sourceFileName, func.get())
+ << "\n Falling back to \"" << match.signature
+ << "\" obtained by matching the argument count only.";
+ return candidates.constFirst().description;
+ }
+
+ QTextStream(errorMessage) << msgCannotFindDocumentation(sourceFileName, func.get())
+ << " (" << candidates.size() << " candidates matching the argument count)";
+ return {};
+}
+
+// Extract the <brief> section from a WebXML (class) documentation and remove it
+// from the source.
+static QString extractBrief(QString *value)
+{
+ const auto briefStart = value->indexOf(briefStartElement);
+ if (briefStart < 0)
+ return {};
+ const auto briefEnd = value->indexOf(briefEndElement,
+ briefStart + briefStartElement.size());
+ if (briefEnd < briefStart)
+ return {};
+ const auto briefLength = briefEnd + briefEndElement.size() - briefStart;
+ QString briefValue = value->mid(briefStart, briefLength);
+ briefValue.insert(briefValue.size() - briefEndElement.size(),
+ u"<rst> More_...</rst>"_s);
+ value->remove(briefStart, briefLength);
+ return briefValue;
+}
+
+// Find the webxml file for global functions/enums
+// by the doc-file typesystem attribute or via include file.
+static QString findGlobalWebXmLFile(const QString &documentationDataDirectory,
+ const QString &docFile,
+ const Include &include)
+{
+ QString result;
+ if (!docFile.isEmpty()) {
+ result = documentationDataDirectory + u'/' + docFile;
+ if (!result.endsWith(webxmlSuffix))
+ result += webxmlSuffix;
+ return QFileInfo::exists(result) ? result : QString{};
+ }
+ if (include.name().isEmpty())
+ return {};
+ // qdoc "\headerfile <QtLogging>" directive produces "qtlogging.webxml"
+ result = documentationDataDirectory + u'/' +
+ QFileInfo(include.name()).baseName() + webxmlSuffix;
+ if (QFileInfo::exists(result))
+ return result;
+ // qdoc "\headerfile <qdrawutil.h>" produces "qdrawutil-h.webxml"
+ result.insert(result.size() - webxmlSuffix.size(), "-h"_L1);
+ return QFileInfo::exists(result) ? result : QString{};
+}
+
+void QtDocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f)
+{
+ auto te = f->typeEntry();
+ if (te == nullptr)
+ return;
+
+ const QString sourceFileName =
+ findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+ if (sourceFileName.isEmpty())
+ return;
+
+ QString errorMessage;
+ auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ if (!classDocumentationO.has_value()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return;
+ }
+ const QString detailed =
+ functionDocumentation(sourceFileName, classDocumentationO.value(),
+ {}, f, &errorMessage);
+ if (!errorMessage.isEmpty())
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ const Documentation documentation(detailed, {});
+ f->setDocumentation(documentation);
+}
+
+void QtDocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &e)
+{
+ auto te = e.typeEntry();
+ const QString sourceFileName =
+ findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+ if (sourceFileName.isEmpty())
+ return;
+
+ QString errorMessage;
+ auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ if (!classDocumentationO.has_value()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return;
+ }
+ if (!extractEnumDocumentation(classDocumentationO.value(), e)) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(sourceFileName, {}, e, {})));
+ }
+}
+
+void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
+{
+ if (!metaClass)
+ return;
+
+ auto context = metaClass->enclosingClass();
+ while (context) {
+ if (!context->enclosingClass())
+ break;
+ context = context->enclosingClass();
+ }
+
+ QString sourceFileRoot = documentationDataDirectory() + u'/'
+ + metaClass->qualifiedCppName().toLower();
+ sourceFileRoot.replace(u"::"_s, u"-"_s);
+
+ QFileInfo sourceFile(sourceFileRoot + webxmlSuffix);
+ if (!sourceFile.exists())
+ sourceFile.setFile(sourceFileRoot + ".xml"_L1);
+ if (!sourceFile.exists()) {
+ qCWarning(lcShibokenDoc).noquote().nospace()
+ << "Can't find qdoc file for class " << metaClass->name() << ", tried: "
+ << QDir::toNativeSeparators(sourceFile.absoluteFilePath());
+ return;
+ }
+
+ const QString sourceFileName = sourceFile.absoluteFilePath();
+ QString errorMessage;
+
+ const auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ if (!classDocumentationO.has_value()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return;
+ }
+
+ const auto &classDocumentation = classDocumentationO.value();
+ for (const auto &p : classDocumentation.properties) {
+ Documentation doc(p.description, p.brief);
+ metaClass->setPropertyDocumentation(p.name, doc);
+ }
+
+ QString docString = applyDocModifications(DocParser::getXpathDocModifications(metaClass),
+ classDocumentation.description);
+
+ if (docString.isEmpty()) {
+ QString className = metaClass->name();
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(sourceFileName, "class", className, {})));
+ }
+ const QString brief = extractBrief(&docString);
+
+ Documentation doc;
+ if (!brief.isEmpty())
+ doc.setValue(brief, Documentation::Brief);
+ doc.setValue(docString);
+ metaClass->setDocumentation(doc);
+
+ //Functions Documentation
+ const auto &funcs = DocParser::documentableFunctions(metaClass);
+ for (const auto &func : funcs) {
+ const QString detailed =
+ functionDocumentation(sourceFileName, classDocumentation,
+ metaClass, func, &errorMessage);
+ if (!errorMessage.isEmpty())
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ const Documentation documentation(detailed, {});
+ std::const_pointer_cast<AbstractMetaFunction>(func)->setDocumentation(documentation);
+ }
+#if 0
+ // Fields
+ const AbstractMetaFieldList &fields = metaClass->fields();
+ for (AbstractMetaField *field : fields) {
+ if (field->isPrivate())
+ return;
+
+ QString query = "/doxygen/compounddef/sectiondef/memberdef/name[text()=\"" + field->name() + "\"]/..";
+ Documentation doc = getDocumentation(DocModificationList(), xquery, query);
+ field->setDocumentation(doc);
+ }
+#endif
+ // Enums
+ for (AbstractMetaEnum &meta_enum : metaClass->enums()) {
+ if (!extractEnumDocumentation(classDocumentation, meta_enum)) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(sourceFileName, metaClass, meta_enum, {})));
+ }
+ }
+}
+
+bool QtDocParser::extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+ AbstractMetaEnum &meta_enum)
+{
+ Documentation enumDoc;
+ const auto index = classDocumentation.indexOfEnum(meta_enum.name());
+ if (index == -1)
+ return false;
+ QString doc = classDocumentation.enums.at(index).description;
+ const auto firstPara = doc.indexOf(u"<para>");
+ if (firstPara != -1) {
+ const QString baseClass = QtDocParser::enumBaseClass(meta_enum);
+ if (baseClass != "Enum"_L1) {
+ const QString note = "(inherits <teletype>enum."_L1 + baseClass
+ + "</teletype>) "_L1;
+ doc.insert(firstPara + 6, note);
+ }
+ }
+ enumDoc.setValue(doc);
+ meta_enum.setDocumentation(enumDoc);
+ return true;
+}
+
+static QString qmlReferenceLink(const QFileInfo &qmlModuleFi)
+{
+ QString result;
+ QTextStream(&result) << "<para>The module also provides <link"
+ << R"( type="page" page="https://doc.qt.io/qt-)" << QT_VERSION_MAJOR
+ << '/' << qmlModuleFi.baseName() << R"(.html")"
+ << ">QML types</link>.</para>";
+ return result;
+}
+
+Documentation QtDocParser::retrieveModuleDocumentation(const QString& name)
+{
+ // TODO: This method of acquiring the module name supposes that the target language uses
+ // dots as module separators in package names. Improve this.
+ QString moduleName = name;
+ moduleName.remove(0, name.lastIndexOf(u'.') + 1);
+ if (moduleName == u"QtQuickControls2")
+ moduleName.chop(1);
+ const QString prefix = documentationDataDirectory() + u'/'
+ + moduleName.toLower();
+
+ const QString sourceFile = prefix + u"-index.webxml"_s;
+ if (!QFile::exists(sourceFile)) {
+ qCWarning(lcShibokenDoc).noquote().nospace()
+ << "Can't find qdoc file for module " << name << ", tried: "
+ << QDir::toNativeSeparators(sourceFile);
+ return Documentation();
+ }
+
+ QString errorMessage;
+ QString docString = webXmlModuleDescription(sourceFile, &errorMessage);
+ if (!errorMessage.isEmpty()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return {};
+ }
+
+ Documentation doc(docString, {});
+ if (doc.isEmpty()) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(sourceFile, "module", name)));
+ return doc;
+ }
+
+ // If a QML module info file exists, insert a link to the Qt docs.
+ const QFileInfo qmlModuleFi(prefix + u"-qmlmodule.webxml"_s);
+ if (qmlModuleFi.isFile()) {
+ QString docString = doc.detailed();
+ const int pos = docString.lastIndexOf(u"</description>");
+ if (pos != -1) {
+ docString.insert(pos, qmlReferenceLink(qmlModuleFi));
+ doc.setDetailed(docString);
+ }
+ }
+
+ return doc;
+}
diff --git a/sources/shiboken6/ApiExtractor/qtdocparser.h b/sources/shiboken6/ApiExtractor/qtdocparser.h
new file mode 100644
index 000000000..f6ba5e47a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/qtdocparser.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QTDOCPARSER_H
+#define QTDOCPARSER_H
+
+#include "docparser.h"
+
+struct ClassDocumentation;
+
+class QtDocParser : public DocParser
+{
+public:
+ QtDocParser() = default;
+ void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+ void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f) override;
+ void fillGlobalEnumDocumentation(AbstractMetaEnum &e) override;
+
+ Documentation retrieveModuleDocumentation() override;
+ Documentation retrieveModuleDocumentation(const QString& name) override;
+
+private:
+ static QString functionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func,
+ QString *errorMessage);
+
+ static QString queryFunctionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func,
+ QString *errorMessage);
+ static bool extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+ AbstractMetaEnum &meta_enum);
+
+};
+
+#endif // QTDOCPARSER_H
+
diff --git a/sources/shiboken6/ApiExtractor/reporthandler.cpp b/sources/shiboken6/ApiExtractor/reporthandler.cpp
new file mode 100644
index 000000000..23066ba21
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/reporthandler.cpp
@@ -0,0 +1,194 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "reporthandler.h"
+#include "typedatabase.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QSet>
+#include <cstring>
+#include <cstdarg>
+#include <cstdio>
+
+using namespace Qt::StringLiterals;
+
+#if defined(_WINDOWS) || defined(NOCOLOR)
+ #define COLOR_END ""
+ #define COLOR_WHITE ""
+ #define COLOR_YELLOW ""
+ #define COLOR_GREEN ""
+#else
+ #define COLOR_END "\033[0m"
+ #define COLOR_WHITE "\033[1;37m"
+ #define COLOR_YELLOW "\033[1;33m"
+ #define COLOR_GREEN "\033[0;32m"
+#endif
+
+static bool m_silent = false;
+static int m_warningCount = 0;
+static int m_suppressedCount = 0;
+static ReportHandler::DebugLevel m_debugLevel = ReportHandler::NoDebug;
+static QSet<QString> m_reportedWarnings;
+static QString m_prefix;
+static bool m_withinProgress = false;
+static QByteArray m_progressMessage;
+static int m_step_warning = 0;
+static QElapsedTimer m_timer;
+
+Q_LOGGING_CATEGORY(lcShiboken, "qt.shiboken")
+Q_LOGGING_CATEGORY(lcShibokenDoc, "qt.shiboken.doc")
+
+void ReportHandler::install()
+{
+ qInstallMessageHandler(ReportHandler::messageOutput);
+ startTimer();
+}
+
+void ReportHandler::startTimer()
+{
+ m_timer.start();
+}
+
+ReportHandler::DebugLevel ReportHandler::debugLevel()
+{
+ return m_debugLevel;
+}
+
+void ReportHandler::setDebugLevel(ReportHandler::DebugLevel level)
+{
+ m_debugLevel = level;
+}
+
+bool ReportHandler::setDebugLevelFromArg(const QString &level)
+{
+ bool result = true;
+ if (level == u"sparse")
+ ReportHandler::setDebugLevel(ReportHandler::SparseDebug);
+ else if (level == u"medium")
+ ReportHandler::setDebugLevel(ReportHandler::MediumDebug);
+ else if (level == u"full")
+ ReportHandler::setDebugLevel(ReportHandler::FullDebug);
+ else
+ result = false;
+ return result;
+}
+
+int ReportHandler::suppressedCount()
+{
+ return m_suppressedCount;
+}
+
+int ReportHandler::warningCount()
+{
+ return m_warningCount;
+}
+
+bool ReportHandler::isSilent()
+{
+ return m_silent;
+}
+
+void ReportHandler::setSilent(bool silent)
+{
+ m_silent = silent;
+}
+
+void ReportHandler::setPrefix(const QString &p)
+{
+ m_prefix = p;
+}
+
+void ReportHandler::messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &text)
+{
+ // Check for file location separator added by SourceLocation
+ int fileLocationPos = text.indexOf(u":\t");
+ if (type == QtWarningMsg) {
+ if (m_silent || m_reportedWarnings.contains(text))
+ return;
+ if (auto db = TypeDatabase::instance()) {
+ const bool suppressed = fileLocationPos >= 0
+ ? db->isSuppressedWarning(QStringView{text}.mid(fileLocationPos + 2))
+ : db->isSuppressedWarning(text);
+ if (suppressed) {
+ ++m_suppressedCount;
+ return;
+ }
+ }
+ ++m_warningCount;
+ ++m_step_warning;
+ m_reportedWarnings.insert(text);
+ }
+ QString message = m_prefix;
+ if (!message.isEmpty())
+ message.append(u' ');
+ const auto prefixLength = message.size();
+ message.append(text);
+ // Replace file location tab by space
+ if (fileLocationPos >= 0)
+ message[prefixLength + fileLocationPos + 1] = u' ';
+ fprintf(stderr, "%s\n", qPrintable(qFormatLogMessage(type, context, message)));
+}
+
+static QByteArray timeStamp()
+{
+ const qint64 elapsed = m_timer.elapsed();
+ return elapsed > 5000
+ ? QByteArray::number(elapsed / 1000) + 's'
+ : QByteArray::number(elapsed) + "ms";
+}
+
+void ReportHandler::startProgress(const QByteArray& str)
+{
+ if (m_silent)
+ return;
+
+ if (m_withinProgress)
+ endProgress();
+
+ m_withinProgress = true;
+ m_progressMessage = str;
+}
+
+static void indentStdout(qsizetype n)
+{
+ for (qsizetype i = 0; i < n; ++i)
+ fputc(' ', stdout);
+}
+
+void ReportHandler::endProgress()
+{
+ if (m_silent)
+ return;
+
+ m_withinProgress = false;
+
+ std::fputs(m_prefix.toUtf8().constData(), stdout);
+ const auto ts = timeStamp();
+ if (ts.size() < 6)
+ indentStdout(6 - ts.size());
+ std::fputs(" [", stdout);
+ std::fputs(ts.constData(), stdout);
+ std::fputs("] ", stdout);
+ std::fputs(m_progressMessage.constData(), stdout);
+ if (m_progressMessage.size() < 60)
+ indentStdout(60 - m_progressMessage.size());
+ const char *endMessage = m_step_warning == 0
+ ? "[" COLOR_GREEN "OK" COLOR_END "]\n"
+ : "[" COLOR_YELLOW "WARNING" COLOR_END "]\n";
+ std::fputs(endMessage, stdout);
+ std::fflush(stdout);
+ m_progressMessage.clear();
+ m_step_warning = 0;
+}
+
+QByteArray ReportHandler::doneMessage()
+{
+ QByteArray result = "Done, " + m_prefix.toUtf8() + ' ' + timeStamp();
+ if (m_warningCount)
+ result += ", " + QByteArray::number(m_warningCount) + " warnings";
+ if (m_suppressedCount)
+ result += " (" + QByteArray::number(m_suppressedCount) + " known issues)";
+ return result;
+}
diff --git a/sources/shiboken6/ApiExtractor/reporthandler.h b/sources/shiboken6/ApiExtractor/reporthandler.h
new file mode 100644
index 000000000..b79adfa73
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/reporthandler.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef REPORTHANDLER_H
+#define REPORTHANDLER_H
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QString>
+
+Q_DECLARE_LOGGING_CATEGORY(lcShiboken)
+Q_DECLARE_LOGGING_CATEGORY(lcShibokenDoc)
+
+class ReportHandler
+{
+public:
+ enum DebugLevel { NoDebug, SparseDebug, MediumDebug, FullDebug };
+
+ static void install();
+ static void startTimer();
+
+ static DebugLevel debugLevel();
+ static void setDebugLevel(DebugLevel level);
+ static bool setDebugLevelFromArg(const QString &);
+
+ static int warningCount();
+
+ static int suppressedCount();
+
+ static void startProgress(const QByteArray &str);
+ static void endProgress();
+
+ static bool isDebug(DebugLevel level)
+ { return debugLevel() >= level; }
+
+ static bool isSilent();
+ static void setSilent(bool silent);
+
+ static void setPrefix(const QString &p);
+
+ static QByteArray doneMessage();
+
+private:
+ static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+};
+
+#endif // REPORTHANDLER_H
diff --git a/sources/shiboken6/ApiExtractor/smartpointertypeentry.h b/sources/shiboken6/ApiExtractor/smartpointertypeentry.h
new file mode 100644
index 000000000..7b712fe35
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/smartpointertypeentry.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef SMARTPOINTERTYPEENTRY_H
+#define SMARTPOINTERTYPEENTRY_H
+
+#include "complextypeentry.h"
+
+class SmartPointerTypeEntryPrivate;
+
+struct SmartPointerInstantiation
+{
+ QString name; // user defined name
+ TypeEntryCPtr typeEntry;
+};
+
+class SmartPointerTypeEntry : public ComplexTypeEntry
+{
+public:
+ using Instantiations = QList<SmartPointerInstantiation>;
+
+ explicit SmartPointerTypeEntry(const QString &entryName,
+ const QString &getterName,
+ TypeSystem::SmartPointerType type,
+ const QString &refCountMethodName,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeSystem::SmartPointerType smartPointerType() const;
+
+ QString getter() const;
+
+ QString refCountMethodName() const;
+
+ QString valueCheckMethod() const;
+ void setValueCheckMethod(const QString &);
+ QString nullCheckMethod() const;
+ void setNullCheckMethod(const QString &);
+ QString resetMethod() const;
+ void setResetMethod(const QString &);
+
+ TypeEntry *clone() const override;
+
+ const Instantiations &instantiations() const;
+ void setInstantiations(const Instantiations &i);
+ bool matchesInstantiation(const TypeEntryCPtr &e) const;
+
+ QString getTargetName(const AbstractMetaType &metaType) const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+protected:
+ SmartPointerTypeEntry(SmartPointerTypeEntryPrivate *d);
+};
+
+#endif // SMARTPOINTERTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/sourcelocation.cpp b/sources/shiboken6/ApiExtractor/sourcelocation.cpp
new file mode 100644
index 000000000..003f201ac
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/sourcelocation.cpp
@@ -0,0 +1,75 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sourcelocation.h"
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+
+SourceLocation::SourceLocation() = default;
+
+SourceLocation::SourceLocation(const QString &file, int l)
+ : m_fileName(file), m_lineNumber(l)
+{
+}
+
+bool SourceLocation::isValid() const
+{
+ return m_lineNumber >= 0 && !m_fileName.isEmpty();
+}
+
+QString SourceLocation::fileName() const
+{
+ return m_fileName;
+}
+
+void SourceLocation::setFileName(const QString &fileName)
+{
+ m_fileName = fileName;
+}
+
+int SourceLocation::lineNumber() const
+{
+ return m_lineNumber;
+}
+
+void SourceLocation::setLineNumber(int lineNumber)
+{
+ m_lineNumber = lineNumber;
+}
+
+QString SourceLocation::toString() const
+{
+ QString result;
+ QTextStream s(&result);
+ format(s);
+ return result;
+}
+
+template<class Stream>
+void SourceLocation::format(Stream &s) const
+{
+ if (isValid())
+ s << QDir::toNativeSeparators(m_fileName) << ':' << m_lineNumber << ':';
+ else
+ s << "<unknown>";
+}
+
+QTextStream &operator<<(QTextStream &s, const SourceLocation &l)
+{
+ if (l.isValid()) {
+ l.format(s);
+ s << '\t'; // ":\t" is used by ReportHandler for filtering suppressions
+ }
+ return s;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const SourceLocation &l)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ l.format(d);
+ return d;
+}
+#endif
diff --git a/sources/shiboken6/ApiExtractor/sourcelocation.h b/sources/shiboken6/ApiExtractor/sourcelocation.h
new file mode 100644
index 000000000..0b188dca3
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/sourcelocation.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef SOURCE_LOCATION_H
+#define SOURCE_LOCATION_H
+
+#include <QtCore/QString>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+
+class SourceLocation
+{
+public:
+ explicit SourceLocation(const QString &file, int l);
+ SourceLocation();
+
+ bool isValid() const;
+
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+ int lineNumber() const;
+ void setLineNumber(int lineNumber);
+
+ QString toString() const;
+
+ template<class Stream>
+ void format(Stream &s) const;
+
+private:
+ QString m_fileName;
+ int m_lineNumber = 0;
+};
+
+QTextStream &operator<<(QTextStream &s, const SourceLocation &l);
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const SourceLocation &l);
+#endif
+
+#endif // SOURCE_LOCATION_H
diff --git a/sources/shiboken6/ApiExtractor/symbols.filter b/sources/shiboken6/ApiExtractor/symbols.filter
new file mode 100644
index 000000000..af6c744dd
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/symbols.filter
@@ -0,0 +1,7 @@
+{
+local:
+_ZSt*;
+_ZNSt*;
+_ZNSs*;
+_ZNKSt*;
+};
diff --git a/sources/shiboken6/ApiExtractor/templateargumententry.h b/sources/shiboken6/ApiExtractor/templateargumententry.h
new file mode 100644
index 000000000..9f2338022
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/templateargumententry.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TEMPLATEARGUMENTENTRY_H
+#define TEMPLATEARGUMENTENTRY_H
+
+#include "typesystem.h"
+
+class TemplateArgumentEntryPrivate;
+
+class TemplateArgumentEntry : public TypeEntry
+{
+public:
+ explicit TemplateArgumentEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ int ordinal() const;
+ void setOrdinal(int o);
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit TemplateArgumentEntry(TemplateArgumentEntryPrivate *d);
+};
+
+#endif // TEMPLATEARGUMENTENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/tests/CMakeLists.txt b/sources/shiboken6/ApiExtractor/tests/CMakeLists.txt
new file mode 100644
index 000000000..76c014fbb
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/CMakeLists.txt
@@ -0,0 +1,66 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(CMAKE_AUTORCC ON)
+
+macro(declare_test testname)
+ # gone: qt4_automoc("${testname}.cpp")
+ set(SOURCES "${testname}.cpp")
+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.h")
+ list(APPEND SOURCES "${testname}.h")
+ endif ()
+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.qrc")
+ list(APPEND SOURCES "${testname}.qrc")
+ endif ()
+
+ add_executable(${testname} ${SOURCES})
+ target_include_directories(${testname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${apiextractor_SOURCE_DIR}
+ )
+ target_link_libraries(${testname} PRIVATE apiextractor Qt::Test)
+ add_test(${testname} ${testname})
+ if (INSTALL_TESTS)
+ install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${testname}
+ DESTINATION share/apiextractor${apiextractor_SUFFIX}/tests)
+ endif()
+endmacro(declare_test testname)
+
+declare_test(testabstractmetaclass)
+declare_test(testabstractmetatype)
+declare_test(testaddfunction)
+declare_test(testarrayargument)
+declare_test(testcodeinjection)
+declare_test(testcontainer)
+declare_test(testconversionoperator)
+declare_test(testconversionruletag)
+declare_test(testctorinformation)
+declare_test(testdroptypeentries)
+declare_test(testdtorinformation)
+declare_test(testenum)
+declare_test(testextrainclude)
+declare_test(testfunctiontag)
+declare_test(testimplicitconversions)
+declare_test(testinserttemplate)
+declare_test(testmodifyfunction)
+declare_test(testmultipleinheritance)
+declare_test(testnamespace)
+declare_test(testnestedtypes)
+declare_test(testnumericaltypedef)
+declare_test(testprimitivetypetag)
+declare_test(testrefcounttag)
+declare_test(testreferencetopointer)
+declare_test(testremovefield)
+declare_test(testremoveimplconv)
+declare_test(testremoveoperatormethod)
+declare_test(testresolvetype)
+declare_test(testreverseoperators)
+declare_test(testtemplates)
+declare_test(testtoposort)
+declare_test(testvaluetypedefaultctortag)
+declare_test(testvoidarg)
+declare_test(testtyperevision)
+if (NOT DISABLE_DOCSTRINGS)
+ declare_test(testmodifydocumentation)
+endif()
+
diff --git a/sources/shiboken6/ApiExtractor/tests/a.xml b/sources/shiboken6/ApiExtractor/tests/a.xml
new file mode 100644
index 000000000..3c09d3800
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/a.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" ?>
+<!-- Sample for testModifyDocumentation -->
+<WebXML>
+ <document>
+ <class name="A">
+ <description>oi
+ <brief>Brief description</brief>
+ <para>Paragraph number 1</para>
+ <para>Paragraph number 2</para>
+ <para>Paragraph number 3</para>
+ </description>
+ </class>
+ </document>
+</WebXML>
diff --git a/sources/shiboken6/ApiExtractor/tests/injectedcode.txt b/sources/shiboken6/ApiExtractor/tests/injectedcode.txt
new file mode 100644
index 000000000..872898810
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/injectedcode.txt
@@ -0,0 +1,5 @@
+// Bla
+// @snippet label
+code line
+// @snippet label
+// Bla
diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp
new file mode 100644
index 000000000..4b5da0c3a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp
@@ -0,0 +1,778 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testabstractmetaclass.h"
+#include "abstractmetabuilder.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <usingmember.h>
+#include <typesystem.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestAbstractMetaClass::testClassName()
+{
+ const char cppCode[] = "class ClassName {};";
+ const char xmlCode[] = R"(<typesystem package="Foo">
+ <value-type name="ClassName"/>
+</typesystem>)";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ QCOMPARE(classes[0]->name(), u"ClassName");
+}
+
+void TestAbstractMetaClass::testClassNameUnderNamespace()
+{
+ const char cppCode[] = "namespace Namespace { class ClassName {}; }\n";
+ const char xmlCode[] = R"XML(
+ <typesystem package="Foo">
+ <namespace-type name="Namespace">
+ <value-type name="ClassName"/>
+ </namespace-type>
+ </typesystem>)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2); // 1 namespace + 1 class
+ if (classes.constFirst()->name() != u"ClassName")
+ qSwap(classes[0], classes[1]);
+
+ QCOMPARE(classes[0]->name(), u"ClassName");
+ QCOMPARE(classes[0]->qualifiedCppName(), u"Namespace::ClassName");
+ QCOMPARE(classes[1]->name(), u"Namespace");
+ QVERIFY(classes[1]->isNamespace());
+
+ // Check ctors info
+ QVERIFY(classes[0]->hasConstructors());
+ QCOMPARE(classes[0]->functions().size(), 2); // default ctor + copy ctor
+
+ auto ctors = classes[0]->queryFunctions(FunctionQueryOption::AnyConstructor);
+ QCOMPARE(ctors.size(), 2);
+ if (ctors.constFirst()->minimalSignature() != u"ClassName()")
+ qSwap(ctors[0], ctors[1]);
+
+ QCOMPARE(ctors[0]->arguments().size(), 0);
+ QCOMPARE(ctors[0]->minimalSignature(), u"ClassName()");
+ QCOMPARE(ctors[1]->arguments().size(), 1);
+ QCOMPARE(ctors[1]->minimalSignature(), u"ClassName(Namespace::ClassName)");
+
+ QVERIFY(!classes[0]->hasPrivateDestructor());
+ QVERIFY(classes[0]->isCopyConstructible()); // implicit default copy ctor
+
+ // This method is buggy and nobody wants to fix it or needs it fixed :-/
+ // QVERIFY(classes[0]->hasNonPrivateConstructor());
+}
+
+static AbstractMetaFunctionCList virtualFunctions(const AbstractMetaClassCPtr &c)
+{
+ AbstractMetaFunctionCList result;
+ const auto &functions = c->functions();
+ for (const auto &f : functions) {
+ if (f->isVirtual())
+ result.append(f);
+ }
+ return result;
+}
+
+void TestAbstractMetaClass::testVirtualMethods()
+{
+ const char cppCode[] =R"CPP(
+class A {
+public:
+ virtual int pureVirtual() const = 0;
+};
+class B : public A {};
+class C : public B {
+public:
+ int pureVirtual() const override { return 0; }
+};
+class F final : public C {
+public:
+ int pureVirtual() const final { return 1; }
+};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package="Foo">
+ <primitive-type name='int'/>
+ <object-type name='A'/>
+ <object-type name='B'/>
+ <object-type name='C'/>
+ <object-type name='F'/>
+</typesystem>
+)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 4);
+ const auto a = AbstractMetaClass::findClass(classes, "A");
+ const auto b = AbstractMetaClass::findClass(classes, "B");
+ const auto c = AbstractMetaClass::findClass(classes, "C");
+ const auto f = AbstractMetaClass::findClass(classes, "F");
+ QVERIFY(f);
+
+ QCOMPARE(a->baseClass(), nullptr);
+ QCOMPARE(b->baseClass(), a);
+ QCOMPARE(c->baseClass(), b);
+ QCOMPARE(f->baseClass(), c);
+
+ QCOMPARE(a->functions().size(), 2); // default ctor + the pure virtual method
+ QCOMPARE(b->functions().size(), 2);
+ QCOMPARE(c->functions().size(), 2);
+ QCOMPARE(f->functions().size(), 2);
+ QVERIFY(f->attributes().testFlag(AbstractMetaClass::FinalCppClass));
+
+ // implementing class, ownclass, declaringclass
+ const auto ctorA = a->queryFunctions(FunctionQueryOption::Constructors).constFirst();
+ const auto ctorB = b->queryFunctions(FunctionQueryOption::Constructors).constFirst();
+ const auto ctorC = c->queryFunctions(FunctionQueryOption::Constructors).constFirst();
+ QVERIFY(ctorA->isConstructor());
+ QVERIFY(!ctorA->isVirtual());
+ QVERIFY(ctorB->isConstructor());
+ QVERIFY(!ctorB->isVirtual());
+ QVERIFY(ctorC->isConstructor());
+ QVERIFY(!ctorC->isVirtual());
+ QCOMPARE(ctorA->implementingClass(), a);
+ QCOMPARE(ctorA->ownerClass(), a);
+ QCOMPARE(ctorA->declaringClass(), a);
+
+ const auto virtualFunctionsA = virtualFunctions(a);
+ const auto virtualFunctionsB = virtualFunctions(b);
+ const auto virtualFunctionsC = virtualFunctions(c);
+ const auto virtualFunctionsF = virtualFunctions(f);
+ QCOMPARE(virtualFunctionsA.size(), 1); // Add a pureVirtualMethods method !?
+ QCOMPARE(virtualFunctionsB.size(), 1);
+ QCOMPARE(virtualFunctionsC.size(), 1);
+ QCOMPARE(virtualFunctionsF.size(), 1);
+
+ const auto funcA = virtualFunctionsA.constFirst();
+ const auto funcB = virtualFunctionsB.constFirst();
+ const auto funcC = virtualFunctionsC.constFirst();
+ const auto funcF = virtualFunctionsF.constFirst();
+
+ QCOMPARE(funcA->ownerClass(), a);
+ QVERIFY(funcC->isVirtual());
+ QCOMPARE(funcB->ownerClass(), b);
+ QCOMPARE(funcC->ownerClass(), c);
+ QVERIFY(funcC->cppAttributes().testFlag(FunctionAttribute::Override));
+ QVERIFY(funcF->cppAttributes().testFlag(FunctionAttribute::Final));
+
+ QCOMPARE(funcA->declaringClass(), a);
+ QCOMPARE(funcB->declaringClass(), a);
+ QCOMPARE(funcC->declaringClass(), a);
+
+ // The next two tests could return null, because it makes more sense.
+ // But we have too many code written relying on this behaviour where
+ // implementingClass is equals to declaringClass on pure virtual functions
+ QCOMPARE(funcA->implementingClass(), a);
+ QCOMPARE(funcB->implementingClass(), a);
+ QCOMPARE(funcC->implementingClass(), c);
+}
+
+void TestAbstractMetaClass::testVirtualBase()
+{
+ const char cppCode[] =R"CPP(
+class Base {
+public:
+ virtual ~Base() = default;
+};
+class Derived : public Base {};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package="Foo">
+ <object-type name='Base'/>
+ <object-type name='Derived'/>
+</typesystem>
+)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
+ QVERIFY(base);
+ QVERIFY(base->isPolymorphic());
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
+ QVERIFY(derived);
+ QVERIFY(derived->isPolymorphic());
+}
+
+void TestAbstractMetaClass::testDefaultValues()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ class B {};\n\
+ void method(B b = B());\n\
+ };\n";
+ const char xmlCode[] = R"XML(
+ <typesystem package="Foo">
+ <value-type name='A'>
+ <value-type name='B'/>
+ </value-type>
+ </typesystem>)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto candidates = classA->queryFunctionsByName(u"method"_s);
+ QCOMPARE(candidates.size(), 1);
+ const auto &method = candidates.constFirst();
+ const AbstractMetaArgument &arg = method->arguments().constFirst();
+ QCOMPARE(arg.defaultValueExpression(), arg.originalDefaultValueExpression());
+}
+
+void TestAbstractMetaClass::testModifiedDefaultValues()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ class B {};\n\
+ void method(B b = B());\n\
+ };\n";
+ const char xmlCode[] = R"XML(
+ <typesystem package="Foo">
+ <value-type name='A'>
+ <modify-function signature='method(A::B)'>
+ <modify-argument index='1'>
+ <replace-default-expression with='Hello'/>
+ </modify-argument>
+ </modify-function>
+ <value-type name='B'/>
+ </value-type>
+ </typesystem>)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto methodMatches = classA->queryFunctionsByName(u"method"_s);
+ QCOMPARE(methodMatches.size(), 1);
+ const auto method = methodMatches.constFirst();
+ const AbstractMetaArgument &arg = method->arguments().constFirst();
+ QCOMPARE(arg.defaultValueExpression(), u"Hello");
+ QCOMPARE(arg.originalDefaultValueExpression(), u"A::B()");
+}
+
+void TestAbstractMetaClass::testInnerClassOfAPolymorphicOne()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ class B {};\n\
+ virtual void method();\n\
+ };\n";
+ const char xmlCode[] = R"XML(
+ <typesystem package="Foo">
+ <object-type name='A'>
+ <value-type name='B'/>
+ </object-type>
+ </typesystem>)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QVERIFY(classA->isPolymorphic());
+ const auto classB = AbstractMetaClass::findClass(classes, "A::B");
+ QVERIFY(classB);
+ QVERIFY(!classB->isPolymorphic());
+}
+
+void TestAbstractMetaClass::testForwardDeclaredInnerClass()
+{
+ const char cppCode[] ="\
+ class A {\n\
+ class B;\n\
+ };\n\
+ class A::B {\n\
+ public:\n\
+ void foo();\n\
+ };\n";
+ const char xmlCode[] = R"XML(
+ <typesystem package="Foo">
+ <value-type name='A'>
+ <value-type name='B'/>
+ </value-type>
+ </typesystem>)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto classB = AbstractMetaClass::findClass(classes, "A::B");
+ QVERIFY(classB);
+ const auto fooF = classB->findFunction("foo");
+ QVERIFY(fooF);
+}
+
+void TestAbstractMetaClass::testSpecialFunctions()
+{
+ const char cppCode[] ="\
+ struct A {\n\
+ A();\n\
+ A(const A&);\n\
+ A &operator=(const A&);\n\
+ };\n\
+ struct B {\n\
+ B();\n\
+ B(const B &);\n\
+ B &operator=(B);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <object-type name='A'/>\n\
+ <object-type name='B'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor);
+ QCOMPARE(ctors.size(), 2);
+ QCOMPARE(ctors.constFirst()->functionType(), AbstractMetaFunction::ConstructorFunction);
+ QCOMPARE(ctors.at(1)->functionType(), AbstractMetaFunction::CopyConstructorFunction);
+ auto assigmentOps = classA->queryFunctionsByName(u"operator="_s);
+ QCOMPARE(assigmentOps.size(), 1);
+ QCOMPARE(assigmentOps.constFirst()->functionType(),
+ AbstractMetaFunction::AssignmentOperatorFunction);
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ ctors = classB->queryFunctions(FunctionQueryOption::AnyConstructor);
+ QCOMPARE(ctors.size(), 2);
+ QCOMPARE(ctors.constFirst()->functionType(), AbstractMetaFunction::ConstructorFunction);
+ QCOMPARE(ctors.at(1)->functionType(), AbstractMetaFunction::CopyConstructorFunction);
+ assigmentOps = classA->queryFunctionsByName(u"operator="_s);
+ QCOMPARE(assigmentOps.size(), 1);
+ QCOMPARE(assigmentOps.constFirst()->functionType(), AbstractMetaFunction::AssignmentOperatorFunction);
+}
+
+void TestAbstractMetaClass::testClassDefaultConstructors()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ \n\
+ struct B {\n\
+ B();\n\
+ private: \n\
+ B(const B&);\n\
+ };\n\
+ \n\
+ struct C {\n\
+ C(const C&);\n\
+ };\n\
+ \n\
+ struct D {\n\
+ private: \n\
+ D(const D&);\n\
+ };\n\
+ \n\
+ struct E {\n\
+ private: \n\
+ ~E();\n\
+ };\n\
+ \n\
+ struct F {\n\
+ F(int, int);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'/>\n\
+ <object-type name='B'/>\n\
+ <value-type name='C'/>\n\
+ <object-type name='D'/>\n\
+ <object-type name='E'/>\n\
+ <value-type name='F'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 6);
+
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->functions().size(), 2);
+
+ auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor);
+ QCOMPARE(ctors.size(), 2);
+ if (ctors.constFirst()->minimalSignature() != u"A()")
+ qSwap(ctors[0], ctors[1]);
+
+ QCOMPARE(ctors[0]->arguments().size(), 0);
+ QCOMPARE(ctors[0]->minimalSignature(), u"A()");
+ QCOMPARE(ctors[1]->arguments().size(), 1);
+ QCOMPARE(ctors[1]->minimalSignature(), u"A(A)");
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ QCOMPARE(classB->functions().size(), 2);
+ QCOMPARE(classB->functions().constFirst()->minimalSignature(), u"B()");
+
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
+ QVERIFY(classC);
+ QCOMPARE(classC->functions().size(), 1);
+ QCOMPARE(classC->functions().constFirst()->minimalSignature(), u"C(C)");
+
+ const auto classD = AbstractMetaClass::findClass(classes, "D");
+ QVERIFY(classD);
+ QCOMPARE(classD->functions().size(), 1);
+ QCOMPARE(classD->functions().constFirst()->minimalSignature(), u"D(D)");
+ QVERIFY(classD->functions().constFirst()->isPrivate());
+
+ const auto classE = AbstractMetaClass::findClass(classes, "E");
+ QVERIFY(classE);
+ QVERIFY(classE->hasPrivateDestructor());
+ QCOMPARE(classE->functions().size(), 0);
+
+ const auto classF = AbstractMetaClass::findClass(classes, "F");
+ QVERIFY(classF);
+
+ ctors = classF->queryFunctions(FunctionQueryOption::AnyConstructor);
+ QCOMPARE(ctors.size(), 2);
+ if (ctors.constFirst()->minimalSignature() != u"F(int,int)")
+ qSwap(ctors[0], ctors[1]);
+
+ QCOMPARE(ctors[0]->arguments().size(), 2);
+ QCOMPARE(ctors[0]->minimalSignature(), u"F(int,int)");
+ QCOMPARE(ctors[1]->arguments().size(), 1);
+ QCOMPARE(ctors[1]->minimalSignature(), u"F(F)");
+}
+
+void TestAbstractMetaClass::testClassInheritedDefaultConstructors()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ A();\n\
+ private: \n\
+ A(const A&);\n\
+ };\n\
+ struct B : public A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <object-type name='A'/>\n\
+ <object-type name='B'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor);
+ QCOMPARE(ctors.size(), 2);
+ if (ctors.constFirst()->minimalSignature() != u"A()")
+ qSwap(ctors[0], ctors[1]);
+
+ QCOMPARE(ctors[0]->arguments().size(), 0);
+ QCOMPARE(ctors[0]->minimalSignature(), u"A()");
+ QCOMPARE(ctors[1]->arguments().size(), 1);
+ QCOMPARE(ctors[1]->minimalSignature(), u"A(A)");
+ QVERIFY(ctors[1]->isPrivate());
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+
+ ctors = classB->queryFunctions(FunctionQueryOption::Constructors);
+ QCOMPARE(ctors.size(), 1);
+ QCOMPARE(ctors.constFirst()->arguments().size(), 0);
+ QCOMPARE(ctors.constFirst()->minimalSignature(), u"B()");
+}
+
+void TestAbstractMetaClass::testAbstractClassDefaultConstructors()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ virtual void method() = 0;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <object-type name='A'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ const auto ctors = classA->queryFunctions(FunctionQueryOption::Constructors);
+ QCOMPARE(ctors.size(), 1);
+ QCOMPARE(ctors.constFirst()->arguments().size(), 0);
+ QCOMPARE(ctors.constFirst()->minimalSignature(), u"A()");
+}
+
+void TestAbstractMetaClass::testObjectTypesMustNotHaveCopyConstructors()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <object-type name='A'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ const auto ctors = classA->queryFunctions(FunctionQueryOption::Constructors);
+ QCOMPARE(ctors.size(), 1);
+ QCOMPARE(ctors.constFirst()->arguments().size(), 0);
+ QCOMPARE(ctors.constFirst()->minimalSignature(), u"A()");
+}
+
+void TestAbstractMetaClass::testIsPolymorphic()
+{
+ const char cppCode[] = "\
+ class A\n\
+ {\n\
+ public:\n\
+ A();\n\
+ inline bool abc() const { return false; }\n\
+ };\n\
+ \n\
+ class B : public A\n\
+ {\n\
+ public:\n\
+ B();\n\
+ inline bool abc() const { return false; }\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='bool'/>\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto b = AbstractMetaClass::findClass(classes, "A");
+
+ QVERIFY(!b->isPolymorphic());
+ const auto a = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(!a->isPolymorphic());
+}
+
+void TestAbstractMetaClass::testClassTypedefedBaseClass()
+{
+ const char cppCode[] =R"CPP(
+class Base {
+};
+
+using BaseAlias1 = Base;
+using BaseAlias2 = BaseAlias1;
+
+class Derived : public BaseAlias2 {
+};
+)CPP";
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <object-type name='Base'/>
+ <object-type name='Derived'/>
+</typesystem>
+)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
+ QVERIFY(base);
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
+ QVERIFY(derived);
+ 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);
+ const auto classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ QVERIFY(classes.constFirst()->hasArithmeticOperatorOverload());
+ FunctionQueryOptions opts(FunctionQueryOption::OperatorOverloads);
+ QCOMPARE(classes.constFirst()->queryFunctions(opts).size(), 1);
+}
+
+void TestAbstractMetaClass::testUsingMembers()
+{
+ const char cppCode[] =R"CPP(
+class Base {
+public:
+ explicit Base(int);
+
+protected:
+ void member();
+};
+
+class Derived : public Base {
+public:
+ using Base::Base;
+ using Base::member;
+};
+)CPP";
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='Derived'/>
+</typesystem>
+)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
+ QVERIFY(base);
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
+ QVERIFY(derived);
+ const auto usingMembers = derived->usingMembers();
+ QCOMPARE(usingMembers.size(), 2);
+ for (const auto &um : usingMembers) {
+ QCOMPARE(um.access, Access::Public);
+ QCOMPARE(um.baseClass, base);
+ QVERIFY(um.memberName == u"Base" || um.memberName == u"member");
+ }
+}
+
+void TestAbstractMetaClass::testUsingTemplateMembers_data()
+{
+ const QByteArray cppCode(R"CPP(
+struct Value {
+ int value = 0;
+};
+
+template <class T> class List {
+public:
+ List();
+ void append(const T &t);
+};
+
+class ValueList : public List<Value> {
+public:
+ void append(const Value &v1, const Value &v2);
+)CPP");
+
+ QTest::addColumn<QByteArray>("code");
+ QTest::newRow("simple")
+ << (cppCode + "using List::append;\n};\n");
+ QTest::newRow("with-template-params")
+ << (cppCode + "using List<Value>::append;\n};\n");
+}
+
+void TestAbstractMetaClass::testUsingTemplateMembers()
+{
+ QFETCH(QByteArray, code);
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <value-type name='Value'/>
+ <container-type name='List' type='list'/>
+ <value-type name='ValueList'/>
+</typesystem>
+)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(code.constData(), xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto valueList = AbstractMetaClass::findClass(classes, "ValueList");
+ QVERIFY(valueList);
+ auto list = valueList->templateBaseClass();
+ QVERIFY(valueList->isUsingMember(list, u"append"_s, Access::Public));
+ QCOMPARE(valueList->queryFunctionsByName(u"append"_s).size(), 2);
+}
+
+void TestAbstractMetaClass::testGenerateFunctions()
+{
+ const char cppCode[] = R"CPP(
+class TestClass {
+public:
+ TestClass();
+
+ void alpha(int);
+ void beta(int);
+ void beta(double);
+ void gamma(int);
+};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <object-type name='TestClass' generate-functions='beta(double);gamma'/>
+</typesystem>
+)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto tc = AbstractMetaClass::findClass(classes, "TestClass");
+ // Verify that the constructor and 2 functions are generated.
+ const auto &functions = tc->functions();
+ QCOMPARE(functions.size(), 5);
+ const auto generateCount =
+ std::count_if(functions.cbegin(), functions.cend(),
+ [](const auto &af) { return af->generateBinding(); });
+ QCOMPARE(generateCount, 3);
+}
+
+QTEST_APPLESS_MAIN(TestAbstractMetaClass)
diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h
new file mode 100644
index 000000000..a6bd2bf06
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTABSTRACTMETACLASS_H
+#define TESTABSTRACTMETACLASS_H
+
+#include <QtCore/QObject>
+
+class AbstractMetaBuilder;
+
+class TestAbstractMetaClass : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testClassName();
+ void testClassNameUnderNamespace();
+ void testVirtualMethods();
+ void testVirtualBase();
+ void testDefaultValues();
+ void testModifiedDefaultValues();
+ void testInnerClassOfAPolymorphicOne();
+ void testForwardDeclaredInnerClass();
+ void testSpecialFunctions();
+ void testClassDefaultConstructors();
+ void testClassInheritedDefaultConstructors();
+ void testAbstractClassDefaultConstructors();
+ void testObjectTypesMustNotHaveCopyConstructors();
+ void testIsPolymorphic();
+ void testClassTypedefedBaseClass();
+ void testFreeOperators_data();
+ void testFreeOperators();
+ void testUsingMembers();
+ void testUsingTemplateMembers_data();
+ void testUsingTemplateMembers();
+ void testGenerateFunctions();
+};
+
+#endif // TESTABSTRACTMETACLASS_H
diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp
new file mode 100644
index 000000000..2c320c874
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp
@@ -0,0 +1,229 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testabstractmetatype.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <typesystem.h>
+#include <parser/codemodel.h>
+#include <typeparser.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestAbstractMetaType::parsing_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("output");
+ QTest::newRow("primitive")
+ << QString::fromLatin1("int") << QString::fromLatin1("int");
+ QTest::newRow("ref")
+ << QString::fromLatin1("int &") << QString::fromLatin1("int&");
+ QTest::newRow("pointer")
+ << QString::fromLatin1("int **") << QString::fromLatin1("int**");
+ QTest::newRow("const ref")
+ << QString::fromLatin1("const int &") << QString::fromLatin1("const int&");
+ QTest::newRow("const pointer")
+ << QString::fromLatin1("const int **") << QString::fromLatin1("const int**");
+ QTest::newRow("const pointer const")
+ << QString::fromLatin1("const int *const*") << QString::fromLatin1("const int*const*");
+}
+
+void TestAbstractMetaType::parsing()
+{
+ QFETCH(QString, input);
+ QFETCH(QString, output);
+ QString errorMessage;
+ const TypeInfo ti = TypeParser::parse(input, &errorMessage);
+ QVERIFY2(errorMessage.isEmpty(), qPrintable(errorMessage));
+ const QString actual = ti.toString();
+ QCOMPARE(actual, output);
+}
+
+void TestAbstractMetaType::testConstCharPtrType()
+{
+ const char cppCode[] = "const char* justAtest();\n";
+ const char xmlCode[] = "<typesystem package=\"Foo\">\n\
+ <primitive-type name='char'/>\n\
+ <function signature='justAtest()' />\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ QCOMPARE(builder->globalFunctions().size(), 1);
+ const auto func = builder->globalFunctions().constFirst();
+ AbstractMetaType rtype = func->type();
+ // Test properties of const char*
+ QVERIFY(!rtype.isVoid());
+ QCOMPARE(rtype.package(), u"Foo");
+ QCOMPARE(rtype.name(), u"char");
+ QVERIFY(rtype.isConstant());
+ QVERIFY(!rtype.isArray());
+ QVERIFY(!rtype.isContainer());
+ QVERIFY(!rtype.isObject());
+ QVERIFY(!rtype.isPrimitive()); // const char* differs from char, so it's not considered a primitive type by apiextractor
+ QVERIFY(rtype.isNativePointer());
+ QCOMPARE(rtype.referenceType(), NoReference);
+ QVERIFY(!rtype.isValue());
+ QVERIFY(!rtype.isValuePointer());
+}
+
+void TestAbstractMetaType::testApiVersionSupported()
+{
+ const char cppCode[] = "class foo {}; class foo2 {};\n\
+ void justAtest(); void justAtest3();\n";
+ const char xmlCode[] = "<typesystem package='Foo'>\n\
+ <value-type name='foo' since='0.1'/>\n\
+ <value-type name='foo2' since='1.0'/>\n\
+ <value-type name='foo3' since='1.1'/>\n\
+ <function signature='justAtest()' since='0.1'/>\n\
+ <function signature='justAtest2()' since='1.1'/>\n\
+ <function signature='justAtest3()'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ false, u"1.0"_s));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+
+
+ QCOMPARE(builder->globalFunctions().size(), 2);
+}
+
+
+void TestAbstractMetaType::testApiVersionNotSupported()
+{
+ const char cppCode[] = "class object {};\n";
+ const char xmlCode[] = "<typesystem package='Foo'>\n\
+ <value-type name='object' since='0.1'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ true, u"0.1"_s));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+}
+
+void TestAbstractMetaType::testCharType()
+{
+ const char cppCode[] = "char justAtest(); class A {};\n";
+ const char xmlCode[] = "<typesystem package=\"Foo\">\n\
+ <primitive-type name='char'/>\n\
+ <value-type name='A'/>\n\
+ <function signature='justAtest()'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ QCOMPARE(classes.constFirst()->package(), u"Foo");
+
+ const auto functions = builder->globalFunctions();
+ QCOMPARE(functions.size(), 1);
+ const auto func = functions.constFirst();
+ AbstractMetaType rtype = func->type();
+ // Test properties of const char*
+ QVERIFY(!rtype.isVoid());
+ QCOMPARE(rtype.package(), u"Foo");
+ QCOMPARE(rtype.name(), u"char");
+ QVERIFY(!rtype.isConstant());
+ QVERIFY(!rtype.isArray());
+ QVERIFY(!rtype.isContainer());
+ QVERIFY(!rtype.isObject());
+ QVERIFY(rtype.isPrimitive());
+ QVERIFY(!rtype.isNativePointer());
+ QCOMPARE(rtype.referenceType(), NoReference);
+ QVERIFY(!rtype.isValue());
+ QVERIFY(!rtype.isValuePointer());
+}
+
+void TestAbstractMetaType::testTypedef()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ void someMethod();\n\
+ };\n\
+ typedef A B;\n\
+ typedef B C;\n";
+ const char xmlCode[] = "<typesystem package=\"Foo\">\n\
+ <value-type name='C' />\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto c = AbstractMetaClass::findClass(classes, "C");
+ QVERIFY(c);
+ QVERIFY(c->isTypeDef());
+}
+
+void TestAbstractMetaType::testTypedefWithTemplates()
+{
+ const char cppCode[] = "\
+ template<typename T>\n\
+ class A {};\n\
+ \n\
+ class B {};\n\
+ typedef A<B> C;\n\
+ \n\
+ void func(C c);\n";
+ const char xmlCode[] = "<typesystem package=\"Foo\">\n\
+ <container-type name='A' type='list'/>\n\
+ <value-type name='B' />\n\
+ <function signature='func(A&lt;B&gt;)'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto functions = builder->globalFunctions();
+ QCOMPARE(functions.size(), 1);
+ const auto function = functions.constFirst();
+ AbstractMetaArgumentList args = function->arguments();
+ QCOMPARE(args.size(), 1);
+ const AbstractMetaArgument &arg = args.constFirst();
+ AbstractMetaType metaType = arg.type();
+ QCOMPARE(metaType.cppSignature(), u"A<B>");
+}
+
+
+void TestAbstractMetaType::testObjectTypeUsedAsValue()
+{
+ const char cppCode[] = "\
+ class A {\n\
+ void method(A);\n\
+ };\n";
+ const char xmlCode[] = "<typesystem package='Foo'>\n\
+ <object-type name='A'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto overloads = classA->queryFunctionsByName(u"method"_s);
+ QCOMPARE(overloads.size(), 1);
+ const auto method = overloads.constFirst();
+ QVERIFY(method);
+ AbstractMetaArgumentList args = method->arguments();
+ QCOMPARE(args.size(), 1);
+ const AbstractMetaArgument &arg = args.constFirst();
+ AbstractMetaType metaType = arg.type();
+ QCOMPARE(metaType.cppSignature(), u"A");
+ QVERIFY(metaType.isValue());
+ QVERIFY(metaType.typeEntry()->isObject());
+}
+
+QTEST_APPLESS_MAIN(TestAbstractMetaType)
diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h
new file mode 100644
index 000000000..fdcf0c787
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTABSTRACTMETATYPE_H
+#define TESTABSTRACTMETATYPE_H
+
+#include <QtCore/QObject>
+
+class TestAbstractMetaType : public QObject
+{
+ Q_OBJECT
+private slots:
+ void parsing_data();
+ void parsing();
+ void testConstCharPtrType();
+ void testCharType();
+ void testTypedef();
+ void testTypedefWithTemplates();
+ void testApiVersionSupported();
+ void testApiVersionNotSupported();
+ void testObjectTypeUsedAsValue();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp
new file mode 100644
index 000000000..a891e1e28
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp
@@ -0,0 +1,522 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testaddfunction.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <codesnip.h>
+#include <addedfunction.h>
+#include <addedfunction_p.h>
+#include <complextypeentry.h>
+#include <primitivetypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+static constexpr auto voidT = "void"_L1;
+
+void TestAddFunction::testParsingFuncNameAndConstness()
+{
+ // generic test...
+ static constexpr auto sig1 = "func(type1, const type2, const type3* const)"_L1;
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 3);
+ TypeInfo retval = f1->returnType();
+ QCOMPARE(retval.qualifiedName(), QStringList{voidT});
+ QCOMPARE(retval.indirections(), 0);
+ QCOMPARE(retval.isConstant(), false);
+ QCOMPARE(retval.referenceType(), NoReference);
+
+ // test with a ugly template as argument and other ugly stuff
+ static constexpr auto sig2 =
+ " _fu__nc_ ( type1, const type2, const Abc<int& , C<char*> * >"
+ " * *@my_name@, const type3* const ) const "_L1;
+ auto f2 = AddedFunction::createAddedFunction(sig2,
+ u"const Abc<int& , C<char*> * > * *"_s,
+ &errorMessage);
+ QVERIFY2(f2, qPrintable(errorMessage));
+ QCOMPARE(f2->name(), u"_fu__nc_");
+ const auto &args = f2->arguments();
+ QCOMPARE(args.size(), 4);
+ retval = f2->returnType();
+ QCOMPARE(retval.qualifiedName(), QStringList{u"Abc"_s});
+ QCOMPARE(retval.instantiations().size(), 2);
+ QCOMPARE(retval.toString(), u"const Abc<int&, C<char*>*>**");
+ QCOMPARE(retval.indirections(), 2);
+ QCOMPARE(retval.isConstant(), true);
+ QCOMPARE(retval.referenceType(), NoReference);
+ QVERIFY(args.at(0).name.isEmpty());
+ QVERIFY(args.at(1).name.isEmpty());
+
+ QCOMPARE(args.at(2).name, u"my_name");
+ auto arg2Type = args.at(2).typeInfo;
+ QCOMPARE(arg2Type.qualifiedName(), QStringList{u"Abc"_s});
+ QCOMPARE(arg2Type.instantiations().size(), 2);
+ QCOMPARE(arg2Type.toString(), u"const Abc<int&, C<char*>*>**");
+ QCOMPARE(arg2Type.indirections(), 2);
+ QCOMPARE(arg2Type.isConstant(), true);
+ QCOMPARE(arg2Type.referenceType(), NoReference);
+
+ QVERIFY(args.at(3).name.isEmpty());
+
+ // function with no args.
+ auto f3 = AddedFunction::createAddedFunction("func()"_L1, voidT, &errorMessage);
+ QVERIFY2(f3, qPrintable(errorMessage));
+ QCOMPARE(f3->name(), u"func");
+ QCOMPARE(f3->arguments().size(), 0);
+
+ // const call operator
+ auto f4 = AddedFunction::createAddedFunction("operator()(int)const"_L1,
+ "int"_L1, &errorMessage);
+ QVERIFY2(f4, qPrintable(errorMessage));
+ QCOMPARE(f4->name(), u"operator()");
+ QCOMPARE(f4->arguments().size(), 1);
+ QVERIFY(f4->isConstant());
+}
+
+void TestAddFunction::testAddFunction()
+{
+ const char cppCode[] = R"CPP(
+struct B {};
+struct A {
+ void a(int);
+};)CPP";
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <primitive-type name='float'/>
+ <value-type name='B'/>
+ <value-type name='A'>
+ <add-function signature='b(int, float = 4.6, const B&amp;)' return-type='int' access='protected'/>
+ <add-function signature='operator()(int)' return-type='int' access='public'/>
+ </value-type>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ auto *typeDb = TypeDatabase::instance();
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ // default ctor, default copy ctor, func a() and the added functions
+ QCOMPARE(classA->functions().size(), 5);
+
+ auto addedFunc = classA->findFunction("b");
+ QVERIFY(addedFunc);
+ QCOMPARE(addedFunc->access(), Access::Protected);
+ QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction);
+ QVERIFY(addedFunc->isUserAdded());
+ QCOMPARE(addedFunc->ownerClass(), classA);
+ QCOMPARE(addedFunc->implementingClass(), classA);
+ QCOMPARE(addedFunc->declaringClass(), classA);
+ QVERIFY(!addedFunc->isVirtual());
+ QVERIFY(!addedFunc->isSignal());
+ QVERIFY(!addedFunc->isSlot());
+ QVERIFY(!addedFunc->isStatic());
+
+ AbstractMetaType returnType = addedFunc->type();
+ QCOMPARE(returnType.typeEntry(), typeDb->findPrimitiveType(u"int"_s));
+ const AbstractMetaArgumentList &args = addedFunc->arguments();
+ QCOMPARE(args.size(), 3);
+ QCOMPARE(args.at(0).type().typeEntry(), returnType.typeEntry());
+ QCOMPARE(args.at(1).defaultValueExpression(), u"4.6");
+ QCOMPARE(args.at(2).type().typeEntry(), typeDb->findType(u"B"_s));
+
+ auto addedCallOperator = classA->findFunction("operator()");
+ QVERIFY(addedCallOperator);
+}
+
+void TestAddFunction::testAddFunctionConstructor()
+{
+ const char cppCode[] = "struct A { A() {} };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='A(int)'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->functions().size(), 3); // default and added ctors
+ const auto addedFunc = classA->functions().constLast();
+ QCOMPARE(addedFunc->access(), Access::Public);
+ QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::ConstructorFunction);
+ QCOMPARE(addedFunc->arguments().size(), 1);
+ QVERIFY(addedFunc->isUserAdded());
+ QVERIFY(addedFunc->isVoid());
+}
+
+void TestAddFunction::testAddFunctionTagDefaultValues()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func()'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ // default ctor, default copy ctor and the added function
+ QCOMPARE(classA->functions().size(), 3);
+ const auto addedFunc = classA->functions().constLast();
+ QCOMPARE(addedFunc->access(), Access::Public);
+ QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction);
+ QVERIFY(addedFunc->isUserAdded());
+ QVERIFY(addedFunc->isVoid());
+}
+
+void TestAddFunction::testAddFunctionCodeSnippets()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func()'>\n\
+ <inject-code class='target' position='end'>Hi!, I am the code.</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->functions().constLast();
+ QVERIFY(addedFunc->hasInjectedCode());
+}
+
+void TestAddFunction::testAddFunctionWithoutParenteses()
+{
+ static constexpr auto sig1 = "func"_L1;
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 0);
+ QCOMPARE(f1->isConstant(), false);
+
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func'>\n\
+ <inject-code class='target' position='end'>Hi!, I am the code.</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction(sig1);
+ QVERIFY(addedFunc);
+ QVERIFY(addedFunc->hasInjectedCode());
+ const auto snips = addedFunc->injectedCodeSnips(TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode);
+ QCOMPARE(snips.size(), 1);
+}
+
+void TestAddFunction::testAddFunctionWithDefaultArgs()
+{
+ static constexpr auto sig1 = "func"_L1;
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 0);
+ QCOMPARE(f1->isConstant(), false);
+
+ const char cppCode[] = "struct A { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func(int, int)'>\n\
+ <modify-argument index='2'>\n\
+ <replace-default-expression with='2'/>\n\
+ </modify-argument>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction(sig1);
+ QVERIFY(addedFunc);
+ const AbstractMetaArgument &arg = addedFunc->arguments().at(1);
+ QCOMPARE(arg.defaultValueExpression(), u"2");
+}
+
+void TestAddFunction::testAddFunctionAtModuleLevel()
+{
+ const char cppCode[] = "struct A { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'/>\n\
+ <add-function signature='func(int, int)'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ auto *typeDb = TypeDatabase::instance();
+
+ AddedFunctionList addedFuncs = typeDb->findGlobalUserFunctions(u"func"_s);
+
+ QCOMPARE(addedFuncs.size(), 1);
+
+ auto &mods = addedFuncs.constFirst()->modifications();
+
+ QCOMPARE(mods.size(), 1);
+ QVERIFY(mods.constFirst().isCodeInjection());
+ CodeSnip snip = mods.constFirst().snips().constFirst();
+ QCOMPARE(snip.code().trimmed(), u"custom_code();");
+}
+
+void TestAddFunction::testAddFunctionWithVarargs()
+{
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction("func(int,char,...)"_L1, voidT,
+ &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 3);
+ QVERIFY(!f1->isConstant());
+
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='char'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func(int,char,...)'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("func");
+ QVERIFY(addedFunc);
+ const AbstractMetaArgument &arg = addedFunc->arguments().constLast();
+ QVERIFY(arg.type().isVarargs());
+ QVERIFY(arg.type().typeEntry()->isVarargs());
+}
+
+void TestAddFunction::testAddStaticFunction()
+{
+ const char cppCode[] = "struct A { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func(int, int)' static='yes'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("func");
+ QVERIFY(addedFunc);
+ QVERIFY(addedFunc->isStatic());
+}
+
+void TestAddFunction::testAddGlobalFunction()
+{
+ const char cppCode[] = "struct A { };struct B {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'/>\n\
+ <add-function signature='globalFunc(int, int)' static='yes'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <add-function signature='globalFunc2(int, int)' static='yes'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ const auto globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 2);
+ const auto classB = AbstractMetaClass::findClass(builder->classes(), "B");
+ QVERIFY(classB);
+ QVERIFY(!classB->findFunction("globalFunc"));
+ QVERIFY(!classB->findFunction("globalFunc2"));
+ QVERIFY(!globalFuncs[0]->injectedCodeSnips().isEmpty());
+ QVERIFY(!globalFuncs[1]->injectedCodeSnips().isEmpty());
+}
+
+void TestAddFunction::testAddFunctionWithApiVersion()
+{
+ const char cppCode[] = "";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <add-function signature='globalFunc(int, int)' static='yes' since='1.3'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <add-function signature='globalFunc2(int, int)' static='yes' since='0.1'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ true, u"0.1"_s));
+ QVERIFY(builder);
+ const auto globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 1);
+}
+
+void TestAddFunction::testModifyAddedFunction()
+{
+ const char cppCode[] = "class Foo { };\n";
+ const char xmlCode[] = R"(
+<typesystem package='Package'>
+ <primitive-type name='float'/>
+ <primitive-type name='int'/>
+ <value-type name='Foo'>
+ <add-function signature='method(float, int)'>
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>
+ <modify-argument index='2' rename='varName'>
+ <replace-default-expression with='0'/>
+ </modify-argument>
+ </add-function>
+ </value-type>
+</typesystem>
+)";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto foo = AbstractMetaClass::findClass(classes, "Foo");
+ const auto method = foo->findFunction("method");
+ QVERIFY(method);
+ QCOMPARE(method->arguments().size(), 2);
+ const AbstractMetaArgument &arg = method->arguments().at(1);
+ QCOMPARE(arg.defaultValueExpression(), u"0");
+ QCOMPARE(arg.name(), u"varName");
+ QCOMPARE(method->argumentName(2), u"varName");
+}
+
+void TestAddFunction::testAddFunctionOnTypedef()
+{
+ const char cppCode[] = "template<class T> class Foo { }; typedef Foo<int> FooInt;\n";
+ const char xmlCode[] = "\
+ <typesystem package='Package'>\n\
+ <value-type name='FooInt'>\n\
+ <add-function signature='FooInt(PySequence)'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <add-function signature='method()'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto foo = AbstractMetaClass::findClass(classes, "FooInt");
+ QVERIFY(foo);
+ QVERIFY(foo->hasNonPrivateConstructor());
+ const auto &lst = foo->queryFunctions(FunctionQueryOption::AnyConstructor);
+ for (const auto &f : lst)
+ QVERIFY(f->signature().startsWith(f->name()));
+ QCOMPARE(lst.size(), 2);
+ const auto method = foo->findFunction("method");
+ QVERIFY(method);
+}
+
+void TestAddFunction::testAddFunctionWithTemplateArg()
+{
+ const char cppCode[] = "template<class T> class Foo { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Package'>\n\
+ <primitive-type name='int'/>\n\
+ <container-type name='Foo' type='list'/>\n\
+ <add-function signature='func(Foo&lt;int>)'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ QCOMPARE(builder->globalFunctions().size(), 1);
+ const auto func = builder->globalFunctions().constFirst();
+ const AbstractMetaArgument &arg = func->arguments().constFirst();
+ QCOMPARE(arg.type().instantiations().size(), 1);
+}
+
+// Test splitting of <add-function> parameter lists.
+
+Q_DECLARE_METATYPE(AddedFunctionParser::Argument)
+
+using Arguments = AddedFunctionParser::Arguments;
+
+void TestAddFunction::testAddFunctionTypeParser_data()
+{
+ QTest::addColumn<QString>("parameterList");
+ QTest::addColumn<Arguments>("expected");
+
+ QTest::newRow("empty")
+ << QString() << Arguments{};
+
+ QTest::newRow("1-arg")
+ << QString::fromLatin1("int @a@=42")
+ << Arguments{{u"int"_s, u"a"_s, u"42"_s}};
+
+ QTest::newRow("2-args")
+ << QString::fromLatin1("double @d@, int @a@=42")
+ << Arguments{{u"double"_s, u"d"_s, {}},
+ {u"int"_s, u"a"_s, u"42"_s}};
+
+ QTest::newRow("template-var_args")
+ << QString::fromLatin1("const QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...")
+ << Arguments{{u"const QList<X,Y> &"_s, u"list"_s, u"QList<X,Y>{1,2}"_s},
+ {u"int"_s, u"b"_s, u"5"_s},
+ {u"..."_s, {}, {}}};
+}
+
+void TestAddFunction::testAddFunctionTypeParser()
+{
+
+ QFETCH(QString, parameterList);
+ QFETCH(Arguments, expected);
+
+ const auto actual = AddedFunctionParser::splitParameters(parameterList);
+ QCOMPARE(actual, expected);
+}
+
+QTEST_APPLESS_MAIN(TestAddFunction)
diff --git a/sources/shiboken6/ApiExtractor/tests/testaddfunction.h b/sources/shiboken6/ApiExtractor/tests/testaddfunction.h
new file mode 100644
index 000000000..77339609f
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testaddfunction.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTADDFUNCTION_H
+#define TESTADDFUNCTION_H
+#include <QtCore/QObject>
+
+class TestAddFunction : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testParsingFuncNameAndConstness();
+ void testAddFunction();
+ void testAddFunctionConstructor();
+ void testAddFunctionTagDefaultValues();
+ void testAddFunctionCodeSnippets();
+ void testAddFunctionWithoutParenteses();
+ void testAddFunctionWithDefaultArgs();
+ void testAddFunctionAtModuleLevel();
+ void testAddFunctionWithVarargs();
+ void testAddStaticFunction();
+ void testAddGlobalFunction();
+ void testAddFunctionWithApiVersion();
+ void testModifyAddedFunction();
+ void testAddFunctionOnTypedef();
+ void testAddFunctionWithTemplateArg();
+ void testAddFunctionTypeParser_data();
+ void testAddFunctionTypeParser();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp b/sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp
new file mode 100644
index 000000000..6e1820bed
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp
@@ -0,0 +1,155 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testarrayargument.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetaenum.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <primitivetypeentry.h>
+#include <parser/enumvalue.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestArrayArgument::testArrayArgumentWithSizeDefinedByInteger()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ enum SomeEnum { Value0, Value1, NValues };\n\
+ void method(double[3]);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='double'/>\n\
+ <object-type name='A'>\n\
+ <enum-type name='SomeEnum'/>\n\
+ </object-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(!builder.isNull());
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+
+ const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst();
+ QVERIFY(arg.type().isArray());
+ QCOMPARE(arg.type().arrayElementCount(), 3);
+ QCOMPARE(arg.type().arrayElementType()->name(), u"double");
+}
+
+static QString functionMinimalSignature(const AbstractMetaClassCPtr &c, const QString &name)
+{
+ const auto f = c->findFunction(name);
+ return f ? f->minimalSignature() : QString();
+}
+
+void TestArrayArgument::testArraySignature()
+{
+ const char cppCode[] ="\
+ struct A {\n\
+ void mi1(int arg[5]);\n\
+ void mi1c(const int arg[5]);\n\
+ void mi1cu(const int arg[]);\n\
+ void mc1cu(const char arg[]);\n\
+ void mc1cup(const char *arg[]);\n\
+ void muc2(unsigned char *arg[2][3]);\n\
+ void mc2c(const char *arg[5][6]);\n\
+ void mc2cu(const char arg[][2]);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='char'/>\n\
+ <primitive-type name='unsigned char'/>\n\
+ <primitive-type name='int'/>\n\
+ <object-type name='A'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(!builder.isNull());
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QCOMPARE(functionMinimalSignature(classA, u"mi1"_s),
+ u"mi1(int[5])");
+ QCOMPARE(functionMinimalSignature(classA, u"mi1c"_s),
+ u"mi1c(const int[5])");
+ QCOMPARE(functionMinimalSignature(classA, u"mi1cu"_s),
+ u"mi1cu(const int[])");
+ QCOMPARE(functionMinimalSignature(classA, u"mc1cu"_s),
+ u"mc1cu(const char*)");
+ QCOMPARE(functionMinimalSignature(classA, u"mc1cup"_s),
+ u"mc1cup(const char*[])");
+ QCOMPARE(functionMinimalSignature(classA, u"muc2"_s),
+ u"muc2(unsigned char*[2][3])");
+ QCOMPARE(functionMinimalSignature(classA, u"mc2c"_s),
+ u"mc2c(const char*[5][6])");
+ QCOMPARE(functionMinimalSignature(classA, u"mc2cu"_s),
+ u"mc2cu(const char[][2])");
+}
+
+void TestArrayArgument::testArrayArgumentWithSizeDefinedByEnumValue()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ enum SomeEnum { Value0, Value1, NValues };\n\
+ void method(double[NValues]);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='double'/>\n\
+ <object-type name='A'>\n\
+ <enum-type name='SomeEnum'/>\n\
+ </object-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(!builder.isNull());
+ AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+
+ auto someEnum = classA->findEnum(u"SomeEnum"_s);
+ QVERIFY(someEnum.has_value());
+ auto nvalues = classA->findEnumValue(u"NValues"_s);
+ QVERIFY(nvalues.has_value());
+
+ const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst();
+ QVERIFY(arg.type().isArray());
+ QCOMPARE(arg.type().arrayElementCount(), nvalues->value().value());
+ QCOMPARE(arg.type().arrayElementType()->name(), u"double");
+};
+
+void TestArrayArgument::testArrayArgumentWithSizeDefinedByEnumValueFromGlobalEnum()
+{
+ const char cppCode[] = "\
+ enum SomeEnum { Value0, Value1, NValues };\n\
+ struct A {\n\
+ void method(double[NValues]);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='double'/>\n\
+ <enum-type name='SomeEnum'/>\n\
+ <object-type name='A'>\n\
+ </object-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+
+ AbstractMetaEnum someEnum = builder->globalEnums().constFirst();
+ auto nvalues = someEnum.findEnumValue(u"NValues");
+ QVERIFY(nvalues.has_value());
+
+ const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst();
+ QVERIFY(arg.type().isArray());
+ QCOMPARE(arg.type().arrayElementCount(), nvalues->value().value());
+ QCOMPARE(arg.type().arrayElementType()->name(), u"double");
+};
+
+QTEST_APPLESS_MAIN(TestArrayArgument)
diff --git a/sources/shiboken6/ApiExtractor/tests/testarrayargument.h b/sources/shiboken6/ApiExtractor/tests/testarrayargument.h
new file mode 100644
index 000000000..75ef0f792
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testarrayargument.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTARRAYARGUMENT_H
+#define TESTARRAYARGUMENT_H
+#include <QtCore/QObject>
+
+class TestArrayArgument : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testArrayArgumentWithSizeDefinedByInteger();
+ void testArraySignature();
+ void testArrayArgumentWithSizeDefinedByEnumValue();
+ void testArrayArgumentWithSizeDefinedByEnumValueFromGlobalEnum();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp
new file mode 100644
index 000000000..4829e6c33
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testcodeinjection.h"
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <codesnip.h>
+#include <modifications.h>
+#include <textstream.h>
+#include <complextypeentry.h>
+#include <valuetypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestCodeInjections::testReadFile_data()
+{
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<QString>("snippet");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("utf8")
+ << QString::fromLatin1(":/utf8code.txt")
+ << QString()
+ << QString::fromUtf8("\xC3\xA1\xC3\xA9\xC3\xAD\xC3\xB3\xC3\xBA");
+
+ QTest::newRow("snippet")
+ << QString::fromLatin1(":/injectedcode.txt")
+ << QString::fromLatin1("label")
+ << QString::fromLatin1("code line");
+}
+
+void TestCodeInjections::testReadFile()
+{
+ QFETCH(QString, filePath);
+ QFETCH(QString, snippet);
+ QFETCH(QString, expected);
+
+ const char cppCode[] = "struct A {};\n";
+ int argc = 0;
+ char *argv[] = {nullptr};
+ QCoreApplication app(argc, argv);
+
+ QString attribute = u"file='"_s + filePath + u'\'';
+ if (!snippet.isEmpty())
+ attribute += u" snippet='"_s + snippet + u'\'';
+
+ QString xmlCode = u"\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'>\n\
+ <conversion-rule class='target' "_s + attribute + u"/>\n\
+ <inject-code class='target' "_s + attribute + u"/>\n\
+ <value-type name='B'/>\n\
+ </value-type>\n\
+ </typesystem>\n"_s;
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().constData()));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QCOMPARE(classA->typeEntry()->codeSnips().size(), 1);
+ QString code = classA->typeEntry()->codeSnips().constFirst().code();
+ QVERIFY(code.indexOf(expected) != -1);
+ QVERIFY(classA->typeEntry()->isValue());
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(classA->typeEntry());
+ code = vte->targetConversionRule();
+ QVERIFY(code.indexOf(expected) != -1);
+}
+
+void TestCodeInjections::testInjectWithValidApiVersion()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <inject-code class='target' since='1.0'>\n\
+ test Inject code\n\
+ </inject-code>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ true, u"1.0"_s));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QCOMPARE(classA->typeEntry()->codeSnips().size(), 1);
+}
+
+void TestCodeInjections::testInjectWithInvalidApiVersion()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'>\n\
+ <inject-code class='target' since='1.0'>\n\
+ test Inject code\n\
+ </inject-code>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ true, u"0.1"_s));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QCOMPARE(classA->typeEntry()->codeSnips().size(), 0);
+}
+
+void TestCodeInjections::testTextStream()
+{
+ StringStream str(TextStream::Language::Cpp);
+ str << "void foo(int a, int b) {\n" << indent
+ << "if (a == b)\n" << indent << "return a;\n" << outdent
+ << "#if Q_OS_WIN\nprint()\n#endif\nreturn a + b;\n" << outdent
+ << "}\n\n// A table\n|"
+ << AlignedField("bla", 40, QTextStream::AlignRight) << "|\n|"
+ << AlignedField("bla", 40, QTextStream::AlignLeft) << "|\n|"
+ << AlignedField(QString(), 40, QTextStream::AlignLeft) << "|\n";
+ str << "\n2nd table\n|" << AlignedField("bla", 3, QTextStream::AlignLeft)
+ << '|' << AlignedField(QString{}, 0, QTextStream::AlignLeft) << "|\n";
+
+constexpr auto expected = R"(void foo(int a, int b) {
+ if (a == b)
+ return a;
+#if Q_OS_WIN
+ print()
+#endif
+ return a + b;
+}
+
+// A table
+| bla|
+|bla |
+| |
+
+2nd table
+|bla||
+)"_L1;
+
+ QCOMPARE(str.toString(), expected);
+}
+
+void TestCodeInjections::testTextStreamRst()
+{
+ // Test that sphinx error: "Inline strong start-string without end-string."
+ // is avoided, that is, characters following a formatting end are escaped.
+
+ StringStream str;
+ str << rstBold << "QObject" << rstBoldOff << "'s properties..."
+ << rstItalic << "some italic" << rstItalicOff << " followed by space.";
+
+ static const char16_t expected[] =
+ uR"(**QObject**\'s properties...*some italic* followed by space.)";
+
+ QCOMPARE(str.toString(), expected);
+}
+
+QTEST_APPLESS_MAIN(TestCodeInjections)
diff --git a/sources/shiboken6/ApiExtractor/tests/testcodeinjection.h b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.h
new file mode 100644
index 000000000..a164ea36e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTCODEINJECTIONS_H
+#define TESTCODEINJECTIONS_H
+
+#include <QtCore/QObject>
+
+class AbstractMetaBuilder;
+
+class TestCodeInjections : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testReadFile_data();
+ void testReadFile();
+ void testInjectWithValidApiVersion();
+ void testInjectWithInvalidApiVersion();
+ void testTextStream();
+ void testTextStreamRst();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc
new file mode 100644
index 000000000..fd7616bd2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource>
+ <file>utf8code.txt</file>
+ <file>injectedcode.txt</file>
+ </qresource>
+</RCC>
diff --git a/sources/shiboken6/ApiExtractor/tests/testcontainer.cpp b/sources/shiboken6/ApiExtractor/tests/testcontainer.cpp
new file mode 100644
index 000000000..0bb72b3c1
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testcontainer.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testcontainer.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <complextypeentry.h>
+#include <containertypeentry.h>
+
+void TestContainer::testContainerType()
+{
+ const char cppCode[] = "\
+ namespace std {\n\
+ template<class T>\n\
+ class list {\n\
+ T get(int x) { return 0; }\n\
+ };\n\
+ }\n\
+ class A : public std::list<int> {\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <namespace-type name='std' generate='no' />\n\
+ <container-type name='std::list' type='list' />\n\
+ <object-type name='A'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ //search for class A
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ auto baseContainer = classA->typeEntry()->baseContainerType();
+ QVERIFY(baseContainer);
+ QCOMPARE(reinterpret_cast<const ContainerTypeEntry*>(baseContainer.get())->containerKind(),
+ ContainerTypeEntry::ListContainer);
+}
+
+void TestContainer::testListOfValueType()
+{
+ const char cppCode[] = "\
+ namespace std {\n\
+ template<class T>\n\
+ class list {\n\
+ T get(int x) { return 0; }\n\
+ };\n\
+ }\n\
+ class ValueType {};\n\
+ class A : public std::list<ValueType> {\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <namespace-type name='std' generate='no'/>\n\
+ <container-type name='std::list' type='list'/>\n\
+ <value-type name='ValueType'/>\n\
+ <value-type name='A'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 3);
+
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->templateBaseClassInstantiations().size(), 1);
+ const AbstractMetaType templateInstanceType =
+ classA->templateBaseClassInstantiations().constFirst();
+
+ QCOMPARE(templateInstanceType.indirections(), 0);
+ QVERIFY(!templateInstanceType.typeEntry()->isObject());
+ QVERIFY(templateInstanceType.typeEntry()->isValue());
+ QCOMPARE(templateInstanceType.referenceType(), NoReference);
+ QVERIFY(!templateInstanceType.isObject());
+ QVERIFY(!templateInstanceType.isValuePointer());
+ QVERIFY(templateInstanceType.isValue());
+}
+
+QTEST_APPLESS_MAIN(TestContainer)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testcontainer.h b/sources/shiboken6/ApiExtractor/tests/testcontainer.h
new file mode 100644
index 000000000..3fd23c3f0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testcontainer.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTCONTAINER_H
+#define TESTCONTAINER_H
+#include <QtCore/QObject>
+
+class TestContainer : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testContainerType();
+ void testListOfValueType();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp
new file mode 100644
index 000000000..8f2b277af
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp
@@ -0,0 +1,178 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testconversionoperator.h"
+#include "testutil.h"
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <typesystem.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestConversionOperator::testConversionOperator()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ };\n\
+ struct B {\n\
+ operator A() const;\n\
+ };\n\
+ struct C {\n\
+ operator A() const;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ <value-type name='C'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
+ QVERIFY(classA);
+ QVERIFY(classB);
+ QVERIFY(classC);
+ QCOMPARE(classA->functions().size(), 2);
+ QCOMPARE(classB->functions().size(), 3);
+ QCOMPARE(classC->functions().size(), 3);
+ QCOMPARE(classA->externalConversionOperators().size(), 2);
+
+ AbstractMetaFunctionCPtr convOp;
+ for (const auto &func : classB->functions()) {
+ if (func->isConversionOperator()) {
+ convOp = func;
+ break;
+ }
+ }
+ QVERIFY(convOp);
+ QVERIFY(classA->externalConversionOperators().contains(convOp));
+}
+
+void TestConversionOperator::testConversionOperatorOfDiscardedClass()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ };\n\
+ struct B {\n\
+ operator A() const;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A' />\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->externalConversionOperators().size(), 0);
+}
+
+void TestConversionOperator::testRemovedConversionOperator()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ };\n\
+ struct B {\n\
+ operator A() const;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A' />\n\
+ <value-type name='B'>\n\
+ <modify-function signature='operator A() const' remove='all'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classA);
+ QVERIFY(classB);
+ QCOMPARE(classA->functions().size(), 2);
+ QCOMPARE(classB->functions().size(), 3);
+ QCOMPARE(classA->externalConversionOperators().size(), 0);
+ QCOMPARE(classA->implicitConversions().size(), 0);
+}
+
+void TestConversionOperator::testConversionOperatorReturningReference()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ operator A&() const;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classA);
+ QVERIFY(classB);
+ QCOMPARE(classA->functions().size(), 2);
+ QCOMPARE(classB->functions().size(), 3);
+ QCOMPARE(classA->externalConversionOperators().size(), 1);
+ QCOMPARE(classA->externalConversionOperators().constFirst()->type().cppSignature(),
+ u"A");
+ QCOMPARE(classA->externalConversionOperators().constFirst()->ownerClass()->name(),
+ u"B");
+ QCOMPARE(classA->implicitConversions().size(), 1);
+ QCOMPARE(classA->implicitConversions().constFirst()->type().cppSignature(),
+ u"A");
+ QCOMPARE(classA->implicitConversions().constFirst()->ownerClass()->name(),
+ u"B");
+}
+
+void TestConversionOperator::testConversionOperatorReturningConstReference()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ operator const A&() const;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classA);
+ QVERIFY(classB);
+ QCOMPARE(classA->functions().size(), 2);
+ QCOMPARE(classB->functions().size(), 3);
+ QCOMPARE(classA->externalConversionOperators().size(), 1);
+ QCOMPARE(classA->externalConversionOperators().constFirst()->type().cppSignature(),
+ u"A"_s);
+ QCOMPARE(classA->externalConversionOperators().constFirst()->ownerClass()->name(),
+ u"B"_s);
+ QCOMPARE(classA->implicitConversions().size(), 1);
+ QCOMPARE(classA->implicitConversions().constFirst()->type().cppSignature(),
+ u"A"_s);
+ QCOMPARE(classA->implicitConversions().constFirst()->ownerClass()->name(),
+ u"B"_s);
+}
+
+QTEST_APPLESS_MAIN(TestConversionOperator)
diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionoperator.h b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.h
new file mode 100644
index 000000000..68288d240
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTCONVERSIONOPERATOR_H
+#define TESTCONVERSIONOPERATOR_H
+#include <QtCore/QObject>
+
+class TestConversionOperator : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testConversionOperator();
+ void testConversionOperatorOfDiscardedClass();
+ void testRemovedConversionOperator();
+ void testConversionOperatorReturningReference();
+ void testConversionOperatorReturningConstReference();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp
new file mode 100644
index 000000000..b5efd92a6
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp
@@ -0,0 +1,240 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testconversionruletag.h"
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <complextypeentry.h>
+#include <customconversion.h>
+#include <primitivetypeentry.h>
+#include <valuetypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QTemporaryFile>
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestConversionRuleTag::testConversionRuleTagWithFile()
+{
+ // FIXME PYSIDE7 remove
+ // temp file used later
+ constexpr auto conversionData = "Hi! I'm a conversion rule."_L1;
+ QTemporaryFile file;
+ QVERIFY(file.open());
+ QCOMPARE(file.write(conversionData.constData()), conversionData.size());
+ file.close();
+
+ const char cppCode[] = "struct A {};\n";
+ QString xmlCode = u"\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <conversion-rule class='target' file='"_s + file.fileName() + u"'/>\n\
+ </value-type>\n\
+ </typesystem>\n"_s;
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().data()));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto typeEntry = classA->typeEntry();
+ QVERIFY(typeEntry->isValue());
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry);
+ QVERIFY(vte->hasTargetConversionRule());
+ QCOMPARE(vte->targetConversionRule(), conversionData);
+}
+
+void TestConversionRuleTag::testConversionRuleTagReplace()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ A();\n\
+ A(const char*, int);\n\
+ };\n\
+ struct B {\n\
+ A createA();\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='char'/>\n\
+ <primitive-type name='A'>\n\
+ <conversion-rule>\n\
+ <native-to-target>\n\
+ DoThis();\n\
+ return ConvertFromCppToPython(%IN);\n\
+ </native-to-target>\n\
+ <target-to-native>\n\
+ <add-conversion type='TargetNone' check='%IN == Target_None'>\n\
+ DoThat();\n\
+ DoSomething();\n\
+ %OUT = A();\n\
+ </add-conversion>\n\
+ <add-conversion type='B' check='CheckIfInputObjectIsB(%IN)'>\n\
+ %OUT = %IN.createA();\n\
+ </add-conversion>\n\
+ <add-conversion type='String' check='String_Check(%IN)'>\n\
+ %OUT = new A(String_AsString(%IN), String_GetSize(%IN));\n\
+ </add-conversion>\n\
+ </target-to-native>\n\
+ </conversion-rule>\n\
+ </primitive-type>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ auto *typeDb = TypeDatabase::instance();
+ auto typeA = typeDb->findPrimitiveType(u"A"_s);
+ QVERIFY(typeA);
+
+ QVERIFY(typeA->hasCustomConversion());
+ auto conversion = typeA->customConversion();
+
+ QCOMPARE(typeA, conversion->ownerType());
+ QCOMPARE(conversion->nativeToTargetConversion().simplified(),
+ u"DoThis(); return ConvertFromCppToPython(%IN);");
+
+ QVERIFY(conversion->replaceOriginalTargetToNativeConversions());
+ QVERIFY(conversion->hasTargetToNativeConversions());
+ QCOMPARE(conversion->targetToNativeConversions().size(), 3);
+
+ QVERIFY(!conversion->targetToNativeConversions().isEmpty());
+ auto toNative = conversion->targetToNativeConversions().at(0);
+ QCOMPARE(toNative.sourceTypeName(), u"TargetNone");
+ QVERIFY(toNative.isCustomType());
+ QCOMPARE(toNative.sourceType(), nullptr);
+ QCOMPARE(toNative.sourceTypeCheck(), u"%IN == Target_None");
+ QCOMPARE(toNative.conversion().simplified(),
+ u"DoThat(); DoSomething(); %OUT = A();");
+
+ QVERIFY(conversion->targetToNativeConversions().size() > 1);
+ toNative = conversion->targetToNativeConversions().at(1);
+ QCOMPARE(toNative.sourceTypeName(), u"B");
+ QVERIFY(!toNative.isCustomType());
+ auto typeB = typeDb->findType(u"B"_s);
+ QVERIFY(typeB);
+ QCOMPARE(toNative.sourceType(), typeB);
+ QCOMPARE(toNative.sourceTypeCheck(), u"CheckIfInputObjectIsB(%IN)");
+ QCOMPARE(toNative.conversion().trimmed(), u"%OUT = %IN.createA();");
+
+ QVERIFY(conversion->targetToNativeConversions().size() > 2);
+ toNative = conversion->targetToNativeConversions().at(2);
+ QCOMPARE(toNative.sourceTypeName(), u"String");
+ QVERIFY(toNative.isCustomType());
+ QCOMPARE(toNative.sourceType(), nullptr);
+ QCOMPARE(toNative.sourceTypeCheck(), u"String_Check(%IN)");
+ QCOMPARE(toNative.conversion().trimmed(),
+ u"%OUT = new A(String_AsString(%IN), String_GetSize(%IN));");
+}
+
+void TestConversionRuleTag::testConversionRuleTagAdd()
+{
+ const char cppCode[] = "\
+ struct Date {\n\
+ Date();\n\
+ Date(int, int, int);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='Date'>\n\
+ <conversion-rule>\n\
+ <target-to-native replace='no'>\n\
+ <add-conversion type='TargetDate' check='TargetDate_Check(%IN)'>\n\
+if (!TargetDateTimeAPI) TargetDateTime_IMPORT;\n\
+%OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));\n\
+ </add-conversion>\n\
+ </target-to-native>\n\
+ </conversion-rule>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "Date");
+ QVERIFY(classA);
+
+ QVERIFY(classA->typeEntry()->isValue());
+ auto vte = std::static_pointer_cast<const ValueTypeEntry>(classA->typeEntry());
+ QVERIFY(vte->hasCustomConversion());
+ auto conversion = vte->customConversion();
+
+ QCOMPARE(conversion->nativeToTargetConversion(), QString());
+
+ QVERIFY(!conversion->replaceOriginalTargetToNativeConversions());
+ QVERIFY(conversion->hasTargetToNativeConversions());
+ QCOMPARE(conversion->targetToNativeConversions().size(), 1);
+
+ QVERIFY(!conversion->targetToNativeConversions().isEmpty());
+ const auto &toNative = conversion->targetToNativeConversions().constFirst();
+ QCOMPARE(toNative.sourceTypeName(), u"TargetDate");
+ QVERIFY(toNative.isCustomType());
+ QCOMPARE(toNative.sourceType(), nullptr);
+ QCOMPARE(toNative.sourceTypeCheck(), u"TargetDate_Check(%IN)");
+ QCOMPARE(toNative.conversion().trimmed(),
+ uR"(if (!TargetDateTimeAPI) TargetDateTime_IMPORT;
+%OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));)");
+}
+
+void TestConversionRuleTag::testConversionRuleTagWithInsertTemplate()
+{
+ const char cppCode[] = "struct A {};";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <!-- single line -->\n\
+ <template name='native_to_target'>return ConvertFromCppToPython(%IN);</template>\n\
+ <!-- multi-line -->\n\
+ <template name='target_to_native'>\n\
+%OUT = %IN.createA();\n\
+ </template>\n\
+ <primitive-type name='A'>\n\
+ <conversion-rule>\n\
+ <native-to-target>\n\
+ <insert-template name='native_to_target'/>\n\
+ </native-to-target>\n\
+ <target-to-native>\n\
+ <add-conversion type='TargetType'>\n\
+ <insert-template name='target_to_native'/>\n\
+ </add-conversion>\n\
+ </target-to-native>\n\
+ </conversion-rule>\n\
+ </primitive-type>\n\
+ </typesystem>\n";
+
+ const char nativeToTargetExpected[] =
+ "// TEMPLATE - native_to_target - START\n"
+ "return ConvertFromCppToPython(%IN);\n"
+ "// TEMPLATE - native_to_target - END";
+
+ const char targetToNativeExpected[] =
+ "// TEMPLATE - target_to_native - START\n"
+ "%OUT = %IN.createA();\n"
+ "// TEMPLATE - target_to_native - END";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ auto *typeDb = TypeDatabase::instance();
+ auto typeA = typeDb->findPrimitiveType(u"A"_s);
+ QVERIFY(typeA);
+
+ QVERIFY(typeA->hasCustomConversion());
+ auto conversion = typeA->customConversion();
+
+ QCOMPARE(typeA, conversion->ownerType());
+ QCOMPARE(conversion->nativeToTargetConversion().trimmed(),
+ QLatin1StringView(nativeToTargetExpected));
+
+ QVERIFY(conversion->hasTargetToNativeConversions());
+ QCOMPARE(conversion->targetToNativeConversions().size(), 1);
+
+ QVERIFY(!conversion->targetToNativeConversions().isEmpty());
+ const auto &toNative = conversion->targetToNativeConversions().constFirst();
+ QCOMPARE(toNative.conversion().trimmed(),
+ QLatin1StringView(targetToNativeExpected));
+}
+
+QTEST_APPLESS_MAIN(TestConversionRuleTag)
diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionruletag.h b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.h
new file mode 100644
index 000000000..64d496cc3
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTCONVERSIONRULE_H
+#define TESTCONVERSIONRULE_H
+
+#include <QtCore/QObject>
+
+class TestConversionRuleTag : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testConversionRuleTagWithFile();
+ void testConversionRuleTagReplace();
+ void testConversionRuleTagAdd();
+ void testConversionRuleTagWithInsertTemplate();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp b/sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp
new file mode 100644
index 000000000..c3a3ebef0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testctorinformation.h"
+#include "abstractmetabuilder.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <typesystem.h>
+
+void TestCtorInformation::testCtorIsPrivate()
+{
+ const char cppCode[] = "class Control { public: Control() {} };\n\
+ class Subject { private: Subject() {} };\n\
+ class CtorLess { };\n";
+ const char xmlCode[] = "<typesystem package='Foo'>\n\
+ <value-type name='Control'/>\n\
+ <object-type name='Subject'/>\n\
+ <value-type name='CtorLess'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 3);
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
+ QVERIFY(klass);
+ QVERIFY(klass->hasNonPrivateConstructor());
+ klass = AbstractMetaClass::findClass(classes, "Subject");
+ QVERIFY(klass);
+ QVERIFY(!klass->hasNonPrivateConstructor());
+ klass = AbstractMetaClass::findClass(classes, "CtorLess");
+ QVERIFY(klass);
+ QVERIFY(klass->hasNonPrivateConstructor());
+}
+
+void TestCtorInformation::testHasNonPrivateCtor()
+{
+ const char cppCode[] = "template<typename T>\n\
+ struct Base { Base(double) {} };\n\
+ typedef Base<int> Derived;\n";
+ const char xmlCode[] = "<typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='double'/>\n\
+ <object-type name='Base' generate='no'/>\n\
+ <object-type name='Derived'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
+ QCOMPARE(base->hasNonPrivateConstructor(), true);
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
+ QCOMPARE(derived->hasNonPrivateConstructor(), true);
+}
+
+QTEST_APPLESS_MAIN(TestCtorInformation)
diff --git a/sources/shiboken6/ApiExtractor/tests/testctorinformation.h b/sources/shiboken6/ApiExtractor/tests/testctorinformation.h
new file mode 100644
index 000000000..58f1648e4
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testctorinformation.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTCTORINFORMATION_H
+#define TESTCTORINFORMATION_H
+
+#include <QtCore/QObject>
+
+class AbstractMetaBuilder;
+
+class TestCtorInformation: public QObject
+{
+ Q_OBJECT
+private slots:
+ void testCtorIsPrivate();
+ void testHasNonPrivateCtor();
+};
+
+#endif // TESTCTORINFORMATION_H
diff --git a/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp
new file mode 100644
index 000000000..16f50e69d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp
@@ -0,0 +1,228 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testdroptypeentries.h"
+#include "testutil.h"
+#include <abstractmetaenum.h>
+#include <abstractmetalang.h>
+#include <typesystem.h>
+#include <conditionalstreamreader.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+static const char cppCode[] = "\
+ struct ValueA {};\n\
+ struct ValueB {};\n\
+ struct ObjectA {};\n\
+ struct ObjectB {};\n\
+ namespace NamespaceA {\n\
+ struct InnerClassA {};\n\
+ namespace InnerNamespaceA {}\n\
+ }\n\
+ namespace NamespaceB {}\n\
+ enum EnumA { Value0 };\n\
+ enum EnumB { Value1 };\n\
+ void funcA();\n\
+ void funcB();\n";
+
+static const char xmlCode[] = "\
+<typesystem package='Foo'>\n\
+ <value-type name='ValueA'/>\n\
+ <value-type name='ValueB'/>\n\
+ <object-type name='ObjectA'/>\n\
+ <object-type name='ObjectB'/>\n\
+ <namespace-type name='NamespaceA'>\n\
+ <value-type name='InnerClassA'/>\n\
+ <namespace-type name='InnerNamespaceA'/>\n\
+ </namespace-type>\n\
+ <namespace-type name='NamespaceB'/>\n\
+ <enum-type name='EnumA'/>\n\
+ <enum-type name='EnumB'/>\n\
+ <function signature='funcA()'/>\n\
+ <function signature='funcB()'/>\n\
+</typesystem>\n";
+
+void TestDropTypeEntries::testDropEntries()
+{
+ const QStringList droppedEntries{u"Foo.ValueB"_s,
+ u"ObjectB"_s, // Check whether module can be omitted
+ u"Foo.NamespaceA.InnerClassA"_s,
+ u"Foo.NamespaceB"_s, u"Foo.EnumB"_s,
+ u"Foo.funcB()"_s,
+ u"Foo.NamespaceA.InnerNamespaceA"_s};
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false,
+ QString(), droppedEntries));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QVERIFY(AbstractMetaClass::findClass(classes, "ValueA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "ValueB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ObjectA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "ObjectB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "NamespaceA::InnerClassA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "NamespaceB"));
+
+ AbstractMetaEnumList globalEnums = builder->globalEnums();
+ QCOMPARE(globalEnums.size(), 1);
+ QCOMPARE(globalEnums.constFirst().name(), u"EnumA");
+
+ auto *td = TypeDatabase::instance();
+ QVERIFY(td->findType(u"funcA"_s));
+ QVERIFY(!td->findType(u"funcB"_s));
+}
+
+void TestDropTypeEntries::testDontDropEntries()
+{
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QVERIFY(AbstractMetaClass::findClass(classes, "ValueA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ValueB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ObjectA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ObjectB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA::InnerClassA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceB"));
+
+ QCOMPARE(builder->globalEnums().size(), 2);
+
+ auto *td = TypeDatabase::instance();
+ QVERIFY(td->findType(u"funcA"_s));
+ QVERIFY(td->findType(u"funcB"_s));
+}
+
+static const char cppCode2[] = "\
+ struct ValueA {\n\
+ void func();\n\
+ };\n";
+
+static const char xmlCode2[] = R"(
+<typesystem package='Foo'>
+ <value-type name='ValueA'>
+ <modify-function signature='func()' remove='all'/>
+ </value-type>
+</typesystem>
+)";
+
+void TestDropTypeEntries::testDropEntryWithChildTags()
+{
+ QStringList droppedEntries(u"Foo.ValueA"_s);
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false,
+ QString(), droppedEntries));
+ QVERIFY(builder);
+ QVERIFY(!AbstractMetaClass::findClass(builder->classes(), "ValueA"));
+}
+
+
+void TestDropTypeEntries::testDontDropEntryWithChildTags()
+{
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false));
+ QVERIFY(builder);
+ QVERIFY(AbstractMetaClass::findClass(builder->classes(), "ValueA"));
+}
+
+void TestDropTypeEntries::testConditionalParsing_data()
+{
+ const QString xml = R"(<?xml version="1.0" encoding="UTF-8"?>
+<root>
+ <tag1>text</tag1>
+ <?if keyword1?>
+ <tag2>text</tag2>
+ <?if keyword2?>
+ <tag3>text</tag3>
+ <?endif?>
+ <?if keyword1 !keyword2?>
+ <tag4>text</tag4>
+ <?endif?>
+ <?endif?>
+ <tag5>text</tag5>
+ <?if !keyword99?> <!-- Exclusion only -->
+ <tag6>text</tag6>
+ <?endif?>
+</root>)"_L1;
+
+ constexpr auto root = "root"_L1;
+ constexpr auto tag1 = "tag1"_L1;
+ constexpr auto tag2 = "tag2"_L1;
+ constexpr auto tag3 = "tag3"_L1;
+ constexpr auto tag4 = "tag4"_L1;
+ constexpr auto tag5 = "tag5"_L1;
+ constexpr auto tag6 = "tag6"_L1;
+ constexpr auto keyword1 = "keyword1"_L1;
+ constexpr auto keyword2 = "keyword2"_L1;
+
+ QTest::addColumn<QString>("xml");
+ QTest::addColumn<QStringList>("keywords");
+ QTest::addColumn<QStringList>("expectedTags");
+
+ QTest::newRow("no-keywords")
+ << xml << QStringList{} << QStringList{root, tag1, tag5, tag6};
+
+ QTest::newRow("skip-nested-condition")
+ << xml << QStringList{keyword1}
+ << QStringList{root, tag1, tag2, tag4, tag5, tag6};
+
+ QTest::newRow("both/check-not")
+ << xml << QStringList{keyword1, keyword2}
+ << QStringList{root, tag1, tag2, tag3, tag5, tag6};
+}
+
+// Parse XML and return a list of tags encountered
+static QStringList parseXml(const QString &xml, const QStringList &keywords)
+{
+ QStringList tags;
+ ConditionalStreamReader reader(xml);
+ reader.setConditions(keywords);
+ while (!reader.atEnd()) {
+ auto t = reader.readNext();
+ switch (t) {
+ case QXmlStreamReader::StartElement:
+ tags.append(reader.name().toString());
+ break;
+ default:
+ break;
+ }
+ }
+ return tags;
+}
+
+void TestDropTypeEntries::testConditionalParsing()
+{
+ QFETCH(QString, xml);
+ QFETCH(QStringList, keywords);
+ QFETCH(QStringList, expectedTags);
+
+ const QStringList actualTags = parseXml(xml, keywords);
+ QCOMPARE(actualTags, expectedTags);
+}
+
+void TestDropTypeEntries::testEntityParsing()
+{
+ const QString xml = R"(<?xml version="1.0" encoding="UTF-8"?>
+<root>
+ <?entity testentity word1 word2?>
+ <text>bla &testentity;</text>
+</root>)"_L1;
+
+ QString actual;
+ ConditionalStreamReader reader(xml);
+ while (!reader.atEnd()) {
+ auto t = reader.readNext();
+ switch (t) {
+ case QXmlStreamReader::Characters:
+ actual.append(reader.text());
+ default:
+ break;
+ }
+ }
+ QVERIFY2(!reader.hasError(), qPrintable(reader.errorString()));
+ QCOMPARE(actual.trimmed(), u"bla word1 word2");
+}
+
+QTEST_APPLESS_MAIN(TestDropTypeEntries)
diff --git a/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h
new file mode 100644
index 000000000..98717bd21
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTDROPTYPEENTRIES_H
+#define TESTDROPTYPEENTRIES_H
+
+#include <QtCore/QObject>
+
+class TestDropTypeEntries : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testDropEntries();
+ void testDontDropEntries();
+ void testDropEntryWithChildTags();
+ void testDontDropEntryWithChildTags();
+ void testConditionalParsing_data();
+ void testConditionalParsing();
+ void testEntityParsing();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp
new file mode 100644
index 000000000..2152d39de
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp
@@ -0,0 +1,158 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testdtorinformation.h"
+#include "abstractmetabuilder.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <typesystem.h>
+
+void TestDtorInformation::testDtorIsPrivate()
+{
+ const char cppCode[] = R"(class Control {
+public:
+ ~Control() {}
+};
+class Subject {
+private:
+ ~Subject() {}
+};
+)";
+ const char xmlCode[] = R"(<typesystem package="Foo">
+ <value-type name="Control"/>
+ <value-type name="Subject"/>
+</typesystem>)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
+ QVERIFY(klass);
+ QVERIFY(!klass->hasPrivateDestructor());
+ klass = AbstractMetaClass::findClass(classes, "Subject");
+ QVERIFY(klass);
+ QVERIFY(klass->hasPrivateDestructor());
+}
+
+void TestDtorInformation::testDtorIsProtected()
+{
+ const char cppCode[] = R"(class Control {
+public:
+ ~Control() {}
+};
+class Subject {
+protected:
+ ~Subject() {}
+};
+)";
+ const char xmlCode[] = R"(<typesystem package="Foo">
+ <value-type name="Control"/>
+ <value-type name="Subject"/>
+</typesystem>)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
+ QVERIFY(klass);
+ QVERIFY(!klass->hasProtectedDestructor());
+ klass = AbstractMetaClass::findClass(classes, "Subject");
+ QVERIFY(klass);
+ QVERIFY(klass->hasProtectedDestructor());
+}
+
+void TestDtorInformation::testDtorIsVirtual()
+{
+ const char cppCode[] = R"(class Control {
+public:
+ ~Control() {}
+};
+class Subject {
+protected:
+ virtual ~Subject() {}
+};
+)";
+ const char xmlCode[] = R"(<typesystem package="Foo">
+ <value-type name="Control"/>
+ <value-type name="Subject"/>
+</typesystem>)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
+ QVERIFY(klass);
+ QVERIFY(!klass->hasVirtualDestructor());
+ klass = AbstractMetaClass::findClass(classes, "Subject");
+ QVERIFY(klass);
+ QVERIFY(klass->hasVirtualDestructor());
+}
+
+void TestDtorInformation::testDtorFromBaseIsVirtual()
+{
+ const char cppCode[] = R"CPP(class ControlBase { public: ~ControlBase() {} };
+class Control : public ControlBase {};
+class SubjectBase { public: virtual ~SubjectBase() {} };
+class Subject : public SubjectBase {};
+)CPP";
+ const char xmlCode[] = R"XML(<typesystem package="Foo"><value-type name="ControlBase"/>
+<value-type name="Control"/>"
+<value-type name="SubjectBase"/>"
+<value-type name="Subject"/>
+</typesystem>
+)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 4);
+
+ auto klass = AbstractMetaClass::findClass(classes, "ControlBase");
+ QVERIFY(klass);
+ QVERIFY(!klass->hasVirtualDestructor());
+ klass = AbstractMetaClass::findClass(classes, "Control");
+ QVERIFY(klass);
+ QVERIFY(!klass->hasVirtualDestructor());
+
+ klass = AbstractMetaClass::findClass(classes, "SubjectBase");
+ QVERIFY(klass);
+ QVERIFY(klass->hasVirtualDestructor());
+ klass = AbstractMetaClass::findClass(classes, "Subject");
+ QVERIFY(klass);
+ QVERIFY(klass->hasVirtualDestructor());
+}
+
+void TestDtorInformation::testClassWithVirtualDtorIsPolymorphic()
+{
+ const char cppCode[] = R"(class Control {
+public:
+ virtual ~Control() {}
+};
+class Subject {
+protected:
+ virtual ~Subject() {}
+};
+)";
+ const char xmlCode[] = R"(<typesystem package="Foo">
+ <value-type name="Control"/>
+ <value-type name="Subject"/>
+</typesystem>)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
+ QVERIFY(klass);
+ QVERIFY(klass->isPolymorphic());
+ klass = AbstractMetaClass::findClass(classes, "Subject");
+ QVERIFY(klass);
+ QVERIFY(klass->isPolymorphic());
+}
+
+QTEST_APPLESS_MAIN(TestDtorInformation)
+
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testdtorinformation.h b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.h
new file mode 100644
index 000000000..0f8cb59b3
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTDTORINFORMATION_H
+#define TESTDTORINFORMATION_H
+
+#include <QtCore/QObject>
+
+class AbstractMetaBuilder;
+
+class TestDtorInformation: public QObject
+{
+ Q_OBJECT
+private slots:
+ void testDtorIsPrivate();
+ void testDtorIsProtected();
+ void testDtorIsVirtual();
+ void testDtorFromBaseIsVirtual();
+ void testClassWithVirtualDtorIsPolymorphic();
+};
+
+#endif // TESTDTORINFORMATION_H
diff --git a/sources/shiboken6/ApiExtractor/tests/testenum.cpp b/sources/shiboken6/ApiExtractor/tests/testenum.cpp
new file mode 100644
index 000000000..c7c2b8b3b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testenum.cpp
@@ -0,0 +1,577 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testenum.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetaenum.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetabuilder_p.h>
+#include <enumtypeentry.h>
+#include <flagstypeentry.h>
+#include <parser/enumvalue.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestEnum::testEnumCppSignature()
+{
+ const char cppCode[] = "\
+ enum GlobalEnum { A, B };\n\
+ \n\
+ struct A {\n\
+ enum ClassEnum { CA, CB };\n\
+ void method(ClassEnum);\n\
+ };\n\
+ void func(A::ClassEnum);\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <enum-type name='GlobalEnum'/>\n\
+ <value-type name='A'>\n\
+ <enum-type name='ClassEnum'/>\n\
+ </value-type>\n\
+ <function signature='func(A::ClassEnum)'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+
+ AbstractMetaEnumList globalEnums = builder->globalEnums();
+ QCOMPARE(globalEnums.size(), 1);
+ QCOMPARE(globalEnums.constFirst().name(), u"GlobalEnum");
+
+ // enum as parameter of a function
+ const auto functions = builder->globalFunctions();
+ QCOMPARE(functions.size(), 1);
+ QCOMPARE(functions.constFirst()->arguments().size(), 1);
+ QCOMPARE(functions.constFirst()->arguments().constFirst().type().cppSignature(),
+ u"A::ClassEnum");
+
+ // enum as parameter of a method
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QCOMPARE(classA->enums().size(), 1);
+ const auto funcs = classA->queryFunctionsByName(u"method"_s);
+ QVERIFY(!funcs.isEmpty());
+ const auto method = funcs.constFirst();
+ AbstractMetaArgument arg = method->arguments().constFirst();
+ QCOMPARE(arg.type().name(), u"ClassEnum");
+ QCOMPARE(arg.type().cppSignature(), u"A::ClassEnum");
+ QCOMPARE(functions.constFirst()->arguments().size(), 1);
+ arg = functions.constFirst()->arguments().constFirst();
+ QCOMPARE(arg.type().name(), u"ClassEnum");
+ QCOMPARE(arg.type().cppSignature(), u"A::ClassEnum");
+
+ AbstractMetaEnumList classEnums = classA->enums();
+ QVERIFY(!classEnums.isEmpty());
+ QCOMPARE(classEnums.constFirst().name(), u"ClassEnum");
+ auto e = AbstractMetaClass::findEnumValue(classes, u"CA"_s);
+ QVERIFY(e.has_value());
+ e = AbstractMetaClass::findEnumValue(classes, u"ClassEnum::CA"_s);
+ QVERIFY(e.has_value());
+}
+
+void TestEnum::testEnumWithApiVersion()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ enum ClassEnum { EnumA, EnumB };\n\
+ enum ClassEnum2 { EnumC, EnumD };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'>\n\
+ <enum-type name='ClassEnum' since='0.1'/>\n\
+ <enum-type name='ClassEnum2' since='0.2'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ true, u"0.1"_s));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ QCOMPARE(classes[0]->enums().size(), 1);
+}
+
+void TestEnum::testAnonymousEnum()
+{
+ const char cppCode[] = "\
+ enum { Global0, Global1 };\n\
+ struct A {\n\
+ enum { A0, A1 };\n\
+ enum { isThis = true, isThat = false };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <!-- Uses the first value of the enum to identify it. -->\n\
+ <enum-type identified-by-value='Global0'/>\n\
+ <value-type name='A'>\n\
+ <!-- Uses the second value of the enum to identify it. -->\n\
+ <enum-type identified-by-value='A1'/>\n\
+ <enum-type identified-by-value='isThis'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaEnumList globalEnums = builder->globalEnums();
+ QCOMPARE(globalEnums.size(), 1);
+ QCOMPARE(globalEnums.constFirst().typeEntry()->qualifiedCppName(),
+ u"Global0");
+ QVERIFY(globalEnums.constFirst().isAnonymous());
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ QCOMPARE(classes[0]->enums().size(), 2);
+
+ auto anonEnumA1 = classes[0]->findEnum(u"A1"_s);
+ QVERIFY(anonEnumA1.has_value());
+ QVERIFY(anonEnumA1->isAnonymous());
+ QCOMPARE(anonEnumA1->typeEntry()->qualifiedCppName(), u"A::A1");
+
+ AbstractMetaEnumValue enumValueA0 = anonEnumA1->values().constFirst();
+ QCOMPARE(enumValueA0.name(), u"A0");
+ QCOMPARE(enumValueA0.value().value(), 0);
+ QCOMPARE(enumValueA0.stringValue(), QString());
+
+ AbstractMetaEnumValue enumValueA1 = anonEnumA1->values().constLast();
+ QCOMPARE(enumValueA1.name(), u"A1");
+ QCOMPARE(enumValueA1.value().value(), 1);
+ QCOMPARE(enumValueA1.stringValue(), QString());
+
+ auto anonEnumIsThis = classes[0]->findEnum(u"isThis"_s);
+ QVERIFY(anonEnumIsThis.has_value());
+ QVERIFY(anonEnumIsThis->isAnonymous());
+ QCOMPARE(anonEnumIsThis->typeEntry()->qualifiedCppName(), u"A::isThis");
+
+ AbstractMetaEnumValue enumValueIsThis = anonEnumIsThis->values().constFirst();
+ QCOMPARE(enumValueIsThis.name(), u"isThis");
+ QCOMPARE(enumValueIsThis.value().value(), static_cast<int>(true));
+ QCOMPARE(enumValueIsThis.stringValue(), u"true");
+
+ AbstractMetaEnumValue enumValueIsThat = anonEnumIsThis->values().constLast();
+ QCOMPARE(enumValueIsThat.name(), u"isThat");
+ QCOMPARE(enumValueIsThat.value().value(), static_cast<int>(false));
+ QCOMPARE(enumValueIsThat.stringValue(), u"false");
+}
+
+void TestEnum::testGlobalEnums()
+{
+ const char cppCode[] = "\
+ enum EnumA { A0, A1 };\n\
+ enum EnumB { B0 = 2, B1 = 0x4 };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <enum-type name='EnumA'/>\n\
+ <enum-type name='EnumB'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaEnumList globalEnums = builder->globalEnums();
+ QCOMPARE(globalEnums.size(), 2);
+
+ AbstractMetaEnum enumA = globalEnums.constFirst();
+ QCOMPARE(enumA.typeEntry()->qualifiedCppName(), u"EnumA");
+
+ AbstractMetaEnumValue enumValueA0 = enumA.values().constFirst();
+ QCOMPARE(enumValueA0.name(), u"A0");
+ QCOMPARE(enumValueA0.value().value(), 0);
+ QCOMPARE(enumValueA0.stringValue(), QString());
+
+ AbstractMetaEnumValue enumValueA1 = enumA.values().constLast();
+ QCOMPARE(enumValueA1.name(), u"A1");
+ QCOMPARE(enumValueA1.value().value(), 1);
+ QCOMPARE(enumValueA1.stringValue(), QString());
+
+ AbstractMetaEnum enumB = globalEnums.constLast();
+ QCOMPARE(enumB.typeEntry()->qualifiedCppName(), u"EnumB");
+
+ AbstractMetaEnumValue enumValueB0 = enumB.values().constFirst();
+ QCOMPARE(enumValueB0.name(), u"B0");
+ QCOMPARE(enumValueB0.value().value(), 2);
+ QCOMPARE(enumValueB0.stringValue(), u"2");
+
+ AbstractMetaEnumValue enumValueB1 = enumB.values().constLast();
+ QCOMPARE(enumValueB1.name(), u"B1");
+ QCOMPARE(enumValueB1.value().value(), 4);
+ QCOMPARE(enumValueB1.stringValue(), u"0x4");
+}
+
+void TestEnum::testEnumValueFromNeighbourEnum()
+{
+ const char cppCode[] = "\
+ namespace A {\n\
+ enum EnumA { ValueA0, ValueA1 };\n\
+ enum EnumB { ValueB0 = A::ValueA1, ValueB1 = ValueA0 };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <namespace-type name='A'>\n\
+ <enum-type name='EnumA'/>\n\
+ <enum-type name='EnumB'/>\n\
+ </namespace-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ QCOMPARE(classes[0]->enums().size(), 2);
+
+ auto enumA = classes[0]->findEnum(u"EnumA"_s);
+ QVERIFY(enumA.has_value());
+ QCOMPARE(enumA->typeEntry()->qualifiedCppName(), u"A::EnumA");
+
+ AbstractMetaEnumValue enumValueA0 = enumA->values().constFirst();
+ QCOMPARE(enumValueA0.name(), u"ValueA0");
+ QCOMPARE(enumValueA0.value().value(), 0);
+ QCOMPARE(enumValueA0.stringValue(), QString());
+
+ AbstractMetaEnumValue enumValueA1 = enumA->values().constLast();
+ QCOMPARE(enumValueA1.name(), u"ValueA1");
+ QCOMPARE(enumValueA1.value().value(), 1);
+ QCOMPARE(enumValueA1.stringValue(), QString());
+
+ auto enumB = classes[0]->findEnum(u"EnumB"_s);
+ QVERIFY(enumB.has_value());
+ QCOMPARE(enumB->typeEntry()->qualifiedCppName(), u"A::EnumB");
+
+ AbstractMetaEnumValue enumValueB0 = enumB->values().constFirst();
+ QCOMPARE(enumValueB0.name(), u"ValueB0");
+ QCOMPARE(enumValueB0.value().value(), 1);
+ QCOMPARE(enumValueB0.stringValue(), u"A::ValueA1");
+
+ AbstractMetaEnumValue enumValueB1 = enumB->values().constLast();
+ QCOMPARE(enumValueB1.name(), u"ValueB1");
+ QCOMPARE(enumValueB1.value().value(), 0);
+ QCOMPARE(enumValueB1.stringValue(), u"ValueA0");
+}
+
+void TestEnum::testEnumValueFromExpression()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ enum EnumA : unsigned {\n\
+ ValueA0 = 3u,\n\
+ ValueA1 = ~3u,\n\
+ ValueA2 = 0xffffffff,\n\
+ ValueA3 = 0xf0,\n\
+ ValueA4 = 8 |ValueA3,\n\
+ ValueA5 = ValueA3|32,\n\
+ ValueA6 = ValueA3 >> 1,\n\
+ ValueA7 = ValueA3 << 1\n\
+ };\n\
+ enum EnumB : int {\n\
+ ValueB0 = ~3,\n\
+ };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'>\n\
+ <enum-type name='EnumA'/>\n\
+ <enum-type name='EnumB'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+
+ auto enumA = classA->findEnum(u"EnumA"_s);
+ QVERIFY(enumA.has_value());
+ QVERIFY(!enumA->isSigned());
+ QCOMPARE(enumA->typeEntry()->qualifiedCppName(), u"A::EnumA");
+
+ AbstractMetaEnumValue valueA0 = enumA->values().at(0);
+ QCOMPARE(valueA0.name(), u"ValueA0");
+ QCOMPARE(valueA0.stringValue(), u"3u");
+ QCOMPARE(valueA0.value().unsignedValue(), 3u);
+
+ AbstractMetaEnumValue valueA1 = enumA->values().at(1);
+ QCOMPARE(valueA1.name(), u"ValueA1");
+ QCOMPARE(valueA1.stringValue(), u"~3u");
+ QCOMPARE(valueA1.value().unsignedValue(), ~3u);
+
+ AbstractMetaEnumValue valueA2 = enumA->values().at(2);
+ QCOMPARE(valueA2.name(), u"ValueA2");
+ QCOMPARE(valueA2.stringValue(), u"0xffffffff");
+ QCOMPARE(valueA2.value().unsignedValue(), 0xffffffffu);
+
+ AbstractMetaEnumValue valueA3 = enumA->values().at(3);
+ QCOMPARE(valueA3.name(), u"ValueA3");
+ QCOMPARE(valueA3.stringValue(), u"0xf0");
+ QCOMPARE(valueA3.value().unsignedValue(), 0xf0u);
+
+ AbstractMetaEnumValue valueA4 = enumA->values().at(4);
+ QCOMPARE(valueA4.name(), u"ValueA4");
+ QCOMPARE(valueA4.stringValue(), u"8 |ValueA3");
+ QCOMPARE(valueA4.value().unsignedValue(), 8|0xf0u);
+
+ AbstractMetaEnumValue valueA5 = enumA->values().at(5);
+ QCOMPARE(valueA5.name(), u"ValueA5");
+ QCOMPARE(valueA5.stringValue(), u"ValueA3|32");
+ QCOMPARE(valueA5.value().unsignedValue(), 0xf0u|32);
+
+ AbstractMetaEnumValue valueA6 = enumA->values().at(6);
+ QCOMPARE(valueA6.name(), u"ValueA6");
+ QCOMPARE(valueA6.stringValue(), u"ValueA3 >> 1");
+ QCOMPARE(valueA6.value().unsignedValue(), 0xf0u >> 1);
+
+ AbstractMetaEnumValue valueA7 = enumA->values().at(7);
+ QCOMPARE(valueA7.name(), u"ValueA7");
+ QCOMPARE(valueA7.stringValue(), u"ValueA3 << 1");
+ QCOMPARE(valueA7.value().unsignedValue(), 0xf0u << 1);
+
+ const auto enumB = classA->findEnum(u"EnumB"_s);
+ QVERIFY(enumB.has_value());
+ QVERIFY(enumB->isSigned());
+ QCOMPARE(enumB->typeEntry()->qualifiedCppName(), u"A::EnumB");
+ QCOMPARE(enumB->values().size(), 1);
+ const AbstractMetaEnumValue valueB0 = enumB->values().at(0);
+ QCOMPARE(valueB0.name(), u"ValueB0");
+ QCOMPARE(valueB0.stringValue(), u"~3");
+ QCOMPARE(valueB0.value().value(), ~3);
+}
+
+void TestEnum::testPrivateEnum()
+{
+ const char cppCode[] = "\
+ class A {\n\
+ private:\n\
+ enum PrivateEnum { Priv0 = 0x0f, Priv1 = 0xf0 };\n\
+ public:\n\
+ enum PublicEnum { Pub0 = Priv0, Pub1 = A::Priv1 };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'>\n\
+ <enum-type name='PublicEnum'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->enums().size(), 2);
+
+ auto privateEnum = classA->findEnum(u"PrivateEnum"_s);
+ QVERIFY(privateEnum.has_value());
+ QVERIFY(privateEnum->isPrivate());
+ QCOMPARE(privateEnum->typeEntry()->qualifiedCppName(), u"A::PrivateEnum");
+
+ auto publicEnum = classA->findEnum(u"PublicEnum"_s);
+ QVERIFY(publicEnum.has_value());
+ QCOMPARE(publicEnum->typeEntry()->qualifiedCppName(), u"A::PublicEnum");
+
+ AbstractMetaEnumValue pub0 = publicEnum->values().constFirst();
+ QCOMPARE(pub0.name(), u"Pub0");
+ QCOMPARE(pub0.value().value(), 0x0f);
+ QCOMPARE(pub0.stringValue(), u"Priv0");
+
+ AbstractMetaEnumValue pub1 = publicEnum->values().constLast();
+ QCOMPARE(pub1.name(), u"Pub1");
+ QCOMPARE(pub1.value().value(), 0xf0);
+ QCOMPARE(pub1.stringValue(), u"A::Priv1");
+}
+
+void TestEnum::testTypedefEnum()
+{
+ const char cppCode[] = "\
+ typedef enum EnumA {\n\
+ A0,\n\
+ A1,\n\
+ } EnumA;\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <enum-type name='EnumA'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaEnumList globalEnums = builder->globalEnums();
+ QCOMPARE(globalEnums.size(), 1);
+
+ AbstractMetaEnum enumA = globalEnums.constFirst();
+ QCOMPARE(enumA.typeEntry()->qualifiedCppName(), u"EnumA");
+
+ AbstractMetaEnumValue enumValueA0 = enumA.values().constFirst();
+ QCOMPARE(enumValueA0.name(), u"A0");
+ QCOMPARE(enumValueA0.value().value(), 0);
+ QCOMPARE(enumValueA0.stringValue(), u"");
+
+ AbstractMetaEnumValue enumValueA1 = enumA.values().constLast();
+ QCOMPARE(enumValueA1.name(), u"A1");
+ QCOMPARE(enumValueA1.value().value(), 1);
+ QCOMPARE(enumValueA1.stringValue(), QString());
+}
+
+// Helper classes and functions for testing enum default value fixing.
+// Put the AbstractMetaBuilder into test fixture struct to avoid having
+// to re-parse for each data row.
+
+struct EnumDefaultValuesFixture
+{
+ std::shared_ptr<AbstractMetaBuilder> builder;
+
+ AbstractMetaType globalEnum;
+ AbstractMetaType testEnum;
+ AbstractMetaType testOptions;
+};
+
+Q_DECLARE_METATYPE(EnumDefaultValuesFixture)
+Q_DECLARE_METATYPE(AbstractMetaType)
+
+static int populateDefaultValuesFixture(EnumDefaultValuesFixture *fixture)
+{
+ static const char cppCode[] =R"(
+enum GlobalEnum { GE1, GE2 };
+namespace Test1
+{
+namespace Test2
+{
+ enum Enum1 { E1, E2 };
+ enum Option { O1, O2 };
+} // namespace Test2
+} // namespace Test1
+)";
+ static const char xmlCode[] = R"(
+<typesystem package="Foo">
+ <enum-type name='GlobalEnum'/>
+ <namespace-type name='Test1'>
+ <namespace-type name='Test2'>
+ <enum-type name='Enum1'/>
+ <enum-type name='Option' flags='Options'/>
+ </namespace-type>
+ </namespace-type>
+</typesystem>
+)";
+
+ fixture->builder.reset(TestUtil::parse(cppCode, xmlCode, false));
+ if (!fixture->builder)
+ return -1;
+
+ const auto globalEnums = fixture->builder->globalEnums();
+ if (globalEnums.size() != 1)
+ return -2;
+
+ fixture->globalEnum = AbstractMetaType(globalEnums.constFirst().typeEntry());
+ fixture->globalEnum.decideUsagePattern();
+
+ AbstractMetaClassCPtr testNamespace;
+ for (const auto &c : fixture->builder->classes()) {
+ if (c->name() == u"Test2") {
+ testNamespace = c;
+ break;
+ }
+ }
+ if (!testNamespace)
+ return -3;
+
+ const auto namespaceEnums = testNamespace->enums();
+ if (namespaceEnums.size() != 2)
+ return -4;
+ QList<EnumTypeEntryCPtr > enumTypeEntries{
+ std::static_pointer_cast<const EnumTypeEntry>(namespaceEnums.at(0).typeEntry()),
+ std::static_pointer_cast<const EnumTypeEntry>(namespaceEnums.at(1).typeEntry())};
+ if (enumTypeEntries.constFirst()->flags())
+ std::swap(enumTypeEntries[0], enumTypeEntries[1]);
+ fixture->testEnum = AbstractMetaType(enumTypeEntries.at(0));
+ fixture->testEnum.decideUsagePattern();
+ fixture->testOptions = AbstractMetaType(enumTypeEntries.at(1)->flags());
+ fixture->testOptions.decideUsagePattern();
+ return 0;
+}
+
+void TestEnum::testEnumDefaultValues_data()
+{
+ EnumDefaultValuesFixture fixture;
+ const int setupOk = populateDefaultValuesFixture(&fixture);
+
+ QTest::addColumn<EnumDefaultValuesFixture>("fixture");
+ QTest::addColumn<int>("setupOk"); // To verify setup
+ QTest::addColumn<AbstractMetaType>("metaType"); // Type and parameters for fixup
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("expected");
+
+ // Global should just remain unmodified
+ QTest::newRow("global") << fixture << setupOk
+ << fixture.globalEnum << "GE1" << "GE1";
+ QTest::newRow("global-int") << fixture << setupOk
+ << fixture.globalEnum << "42" << "42";
+ QTest::newRow("global-hex-int") << fixture << setupOk
+ << fixture.globalEnum << "0x10" << "0x10";
+ QTest::newRow("global-int-cast") << fixture << setupOk
+ << fixture.globalEnum << "GlobalEnum(-1)" << "GlobalEnum(-1)";
+
+ // Namespaced enum as number should remain unmodified
+ QTest::newRow("namespace-enum-int") << fixture << setupOk
+ << fixture.testEnum << "42" << "42";
+ QTest::newRow("namespace-enum-hex-int") << fixture << setupOk
+ << fixture.testEnum << "0x10" << "0x10";
+ // Partial qualification of namespaced enum
+ QTest::newRow("namespace-enum-qualified") << fixture << setupOk
+ << fixture.testEnum << "Enum1::E1" << "Test1::Test2::Enum1::E1";
+ // Unqualified namespaced enums
+ QTest::newRow("namespace-enum-unqualified") << fixture << setupOk
+ << fixture.testEnum << "E1" << "Test1::Test2::Enum1::E1";
+ // Namespaced enums cast from int should be qualified by scope
+ QTest::newRow("namespace-enum-int-cast") << fixture << setupOk
+ << fixture.testEnum << "Enum1(-1)" << "Test1::Test2::Enum1(-1)";
+
+ // Namespaced option as number should remain unmodified
+ QTest::newRow("namespace-option-int") << fixture << setupOk
+ << fixture.testOptions << "0x10" << "0x10";
+ QTest::newRow("namespace-option-expression") << fixture << setupOk
+ << fixture.testOptions << "0x10 | 0x20" << "0x10 | 0x20";
+ QTest::newRow("namespace-option-expression1") << fixture << setupOk
+ << fixture.testOptions << "0x10 | Test1::Test2::Option::O1"
+ << "0x10 | Test1::Test2::Option::O1";
+ QTest::newRow("namespace-option-expression2") << fixture << setupOk
+ << fixture.testOptions << "0x10 | O1" << "0x10 | Test1::Test2::Option::O1";
+ // Complicated expressions - should remain unmodified
+ QTest::newRow("namespace-option-expression-paren") << fixture << setupOk
+ << fixture.testOptions << "0x10 | (0x20 | 0x40 | O1)"
+ << "0x10 | (0x20 | 0x40 | O1)";
+
+ // Option: Cast Enum from int should be qualified
+ QTest::newRow("namespace-option-int-cast") << fixture << setupOk
+ << fixture.testOptions << "Option(0x10)" << "Test1::Test2::Option(0x10)";
+ // Option: Cast Flags from int should be qualified
+ QTest::newRow("namespace-options-int-cast") << fixture << setupOk
+ << fixture.testOptions << "Options(0x10 | 0x20)" << "Test1::Test2::Options(0x10 | 0x20)";
+ QTest::newRow("namespace-option-cast-expression1") << fixture << setupOk
+ << fixture.testOptions << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)"
+ << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)";
+ QTest::newRow("namespace-option-cast-expression2") << fixture << setupOk
+ << fixture.testOptions << "Options(0x10 | O1)"
+ << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)";
+}
+
+void TestEnum::testEnumDefaultValues()
+{
+ QFETCH(EnumDefaultValuesFixture, fixture);
+ QFETCH(int, setupOk);
+ QFETCH(AbstractMetaType, metaType);
+ QFETCH(QString, input);
+ QFETCH(QString, expected);
+ QCOMPARE(setupOk, 0);
+ const QString actual = fixture.builder->fixEnumDefault(metaType, input);
+ QCOMPARE(actual, expected);
+}
+
+QTEST_APPLESS_MAIN(TestEnum)
diff --git a/sources/shiboken6/ApiExtractor/tests/testenum.h b/sources/shiboken6/ApiExtractor/tests/testenum.h
new file mode 100644
index 000000000..452755490
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testenum.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTENUM_H
+#define TESTENUM_H
+
+#include <QtCore/QObject>
+
+class TestEnum : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testEnumCppSignature();
+ void testEnumWithApiVersion();
+ void testAnonymousEnum();
+ void testGlobalEnums();
+ void testEnumValueFromNeighbourEnum();
+ void testEnumValueFromExpression();
+ void testPrivateEnum();
+ void testTypedefEnum();
+ void testEnumDefaultValues_data();
+ void testEnumDefaultValues();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp b/sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp
new file mode 100644
index 000000000..fcc409a42
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testextrainclude.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <complextypeentry.h>
+#include <typesystemtypeentry.h>
+
+void TestExtraInclude::testClassExtraInclude()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <extra-includes>\n\
+ <include file-name='header.h' location='global'/>\n\
+ </extra-includes>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ QList<Include> includes = classA->typeEntry()->extraIncludes();
+ QCOMPARE(includes.size(), 1);
+ QCOMPARE(includes.constFirst().name(), u"header.h");
+}
+
+void TestExtraInclude::testGlobalExtraIncludes()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <extra-includes>\n\
+ <include file-name='header1.h' location='global'/>\n\
+ <include file-name='header2.h' location='global'/>\n\
+ </extra-includes>\n\
+ <value-type name='A'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QVERIFY(AbstractMetaClass::findClass(classes, "A"));
+
+ auto *td = TypeDatabase::instance();
+ TypeSystemTypeEntryCPtr module = td->defaultTypeSystemType();
+ QVERIFY(module);
+ QCOMPARE(module->name(), u"Foo");
+
+ QList<Include> includes = module->extraIncludes();
+ QCOMPARE(includes.size(), 2);
+ QCOMPARE(includes.constFirst().name(), u"header1.h");
+ QCOMPARE(includes.constLast().name(), u"header2.h");
+}
+
+QTEST_APPLESS_MAIN(TestExtraInclude)
diff --git a/sources/shiboken6/ApiExtractor/tests/testextrainclude.h b/sources/shiboken6/ApiExtractor/tests/testextrainclude.h
new file mode 100644
index 000000000..6bcb57993
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testextrainclude.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTEXTRAINCLUDE_H
+#define TESTEXTRAINCLUDE_H
+
+#include <QtCore/QObject>
+
+class TestExtraInclude : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testClassExtraInclude();
+ void testGlobalExtraIncludes();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp
new file mode 100644
index 000000000..18eaf5774
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp
@@ -0,0 +1,79 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testfunctiontag.h"
+#include "testutil.h"
+#include <abstractmetafunction.h>
+#include <modifications.h>
+#include <typesystem.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestFunctionTag::testFunctionTagForSpecificSignature()
+{
+ const char cppCode[] = "void globalFunction(int); void globalFunction(float); void dummy();\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='float'/>\n\
+ <function signature='globalFunction(int)'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ TypeEntryCPtr func = TypeDatabase::instance()->findType(u"globalFunction"_s);
+ QVERIFY(func);
+ QCOMPARE(builder->globalFunctions().size(), 1);
+}
+
+void TestFunctionTag::testFunctionTagForAllSignatures()
+{
+ const char cppCode[] = "void globalFunction(int); void globalFunction(float); void dummy();\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='float'/>\n\
+ <function signature='globalFunction(int)'/>\n\
+ <function signature='globalFunction(float)'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ TypeEntryCPtr func = TypeDatabase::instance()->findType(u"globalFunction"_s);
+ QVERIFY(func);
+ QCOMPARE(builder->globalFunctions().size(), 2);
+}
+
+void TestFunctionTag::testRenameGlobalFunction()
+{
+ const char cppCode[] = "void global_function_with_ugly_name();\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <function signature='global_function_with_ugly_name()' rename='smooth'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ TypeEntryCPtr func = TypeDatabase::instance()->findType(u"global_function_with_ugly_name"_s);
+ QVERIFY(func);
+
+ QCOMPARE(builder->globalFunctions().size(), 1);
+ const auto metaFunc = builder->globalFunctions().constFirst();
+
+ QVERIFY(metaFunc);
+ QCOMPARE(metaFunc->modifications().size(), 1);
+ QVERIFY(metaFunc->modifications().constFirst().isRenameModifier());
+ QCOMPARE(metaFunc->modifications().constFirst().renamedToName(),
+ u"smooth");
+
+ QCOMPARE(metaFunc->name(), u"smooth");
+ QCOMPARE(metaFunc->originalName(), u"global_function_with_ugly_name");
+ QCOMPARE(metaFunc->minimalSignature(), u"global_function_with_ugly_name()");
+}
+
+QTEST_APPLESS_MAIN(TestFunctionTag)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testfunctiontag.h b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.h
new file mode 100644
index 000000000..7c60cb4e0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTFUNCTIONTAG_H
+#define TESTFUNCTIONTAG_H
+
+#include <QtCore/QObject>
+
+class TestFunctionTag : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testFunctionTagForSpecificSignature();
+ void testFunctionTagForAllSignatures();
+ void testRenameGlobalFunction();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp
new file mode 100644
index 000000000..899d00ad4
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testimplicitconversions.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <complextypeentry.h>
+#include <QtTest/QTest>
+
+void TestImplicitConversions::testWithPrivateCtors()
+{
+ const char cppCode[] = "\
+ class B;\n\
+ class C;\n\
+ class A {\n\
+ A(const B&);\n\
+ public:\n\
+ A(const C&);\n\
+ };\n\
+ class B {};\n\
+ class C {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ <value-type name='C'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 3);
+
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
+ const auto implicitConvs = classA->implicitConversions();
+ QCOMPARE(implicitConvs.size(), 1);
+ QCOMPARE(implicitConvs.constFirst()->arguments().constFirst().type().typeEntry(),
+ classC->typeEntry());
+}
+
+void TestImplicitConversions::testWithModifiedVisibility()
+{
+ const char cppCode[] = "\
+ class B;\n\
+ class A {\n\
+ public:\n\
+ A(const B&);\n\
+ };\n\
+ class B {};\n";
+ const char xmlCode[] = R"(
+<typesystem package='Foo'>
+ <value-type name='A'>
+ <modify-function signature='A(const B&amp;)' access='private'/>
+ </value-type>
+ <value-type name='B'/>
+</typesystem>
+)";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto implicitConvs = classA->implicitConversions();
+ QCOMPARE(implicitConvs.size(), 1);
+ QCOMPARE(implicitConvs.constFirst()->arguments().constFirst().type().typeEntry(),
+ classB->typeEntry());
+}
+
+
+void TestImplicitConversions::testWithAddedCtor()
+{
+ const char cppCode[] = "\
+ class B;\n\
+ class A {\n\
+ public:\n\
+ A(const B&);\n\
+ };\n\
+ class B {};\n\
+ class C {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <custom-type name='TARGETLANGTYPE'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='A(const C&amp;)'/>\n\
+ </value-type>\n\
+ <value-type name='B'>\n\
+ <add-function signature='B(TARGETLANGTYPE*)'/>\n\
+ </value-type>\n\
+ <value-type name='C'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 3);
+
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ auto implicitConvs = classA->implicitConversions();
+ QCOMPARE(implicitConvs.size(), 2);
+
+ // Added constructors with custom types should never result in implicit converters.
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ implicitConvs = classB->implicitConversions();
+ QCOMPARE(implicitConvs.size(), 0);
+}
+
+void TestImplicitConversions::testWithExternalConversionOperator()
+{
+ const char cppCode[] = "\
+ class A {};\n\
+ struct B {\n\
+ operator A() const;\n\
+ };\n";
+ const char xmlCode[] = "\n\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto implicitConvs = classA->implicitConversions();
+ QCOMPARE(implicitConvs.size(), 1);
+ const auto &externalConvOps = classA->externalConversionOperators();
+ QCOMPARE(externalConvOps.size(), 1);
+
+ AbstractMetaFunctionCPtr convOp;
+ for (const auto &func : classB->functions()) {
+ if (func->isConversionOperator())
+ convOp = func;
+ }
+ QVERIFY(convOp);
+ QCOMPARE(implicitConvs.constFirst(), convOp);
+}
+
+QTEST_APPLESS_MAIN(TestImplicitConversions)
diff --git a/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h
new file mode 100644
index 000000000..e0678c5f5
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTIMPLICITCONVERSIONS_H
+#define TESTIMPLICITCONVERSIONS_H
+
+#include <QtCore/QObject>
+
+class AbstractMetaBuilder;
+
+class TestImplicitConversions : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testWithPrivateCtors();
+ void testWithModifiedVisibility();
+ void testWithAddedCtor();
+ void testWithExternalConversionOperator();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp
new file mode 100644
index 000000000..23cf0f9ea
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp
@@ -0,0 +1,63 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testinserttemplate.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <codesnip.h>
+#include <modifications.h>
+#include <complextypeentry.h>
+#include <typesystemtypeentry.h>
+
+void TestInsertTemplate::testInsertTemplateOnClassInjectCode()
+{
+ const char cppCode[] = "struct A{};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <template name='code_template'>\n\
+ code template content\n\
+ </template>\n\
+ <value-type name='A'>\n\
+ <inject-code class='native'>\n\
+ <insert-template name='code_template'/>\n\
+ </inject-code>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->typeEntry()->codeSnips().size(), 1);
+ QString code = classA->typeEntry()->codeSnips().constFirst().code();
+ QVERIFY(code.contains(u"code template content"));
+}
+
+void TestInsertTemplate::testInsertTemplateOnModuleInjectCode()
+{
+ const char cppCode[] = "";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <template name='code_template'>\n\
+ code template content\n\
+ </template>\n\
+ <inject-code class='native'>\n\
+ <insert-template name='code_template'/>\n\
+ </inject-code>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QVERIFY(classes.isEmpty());
+
+ TypeSystemTypeEntryCPtr module = TypeDatabase::instance()->defaultTypeSystemType();
+ QVERIFY(module);
+ QCOMPARE(module->name(), u"Foo");
+ QCOMPARE(module->codeSnips().size(), 1);
+ QString code = module->codeSnips().constFirst().code().trimmed();
+ QVERIFY(code.contains(u"code template content"));
+}
+
+QTEST_APPLESS_MAIN(TestInsertTemplate)
diff --git a/sources/shiboken6/ApiExtractor/tests/testinserttemplate.h b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.h
new file mode 100644
index 000000000..f4f67abc0
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTINSERTTEMPLATE_H
+#define TESTINSERTTEMPLATE_H
+
+#include <QtCore/QObject>
+
+class TestInsertTemplate : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testInsertTemplateOnClassInjectCode();
+ void testInsertTemplateOnModuleInjectCode();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp
new file mode 100644
index 000000000..9cf2e0cc7
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp
@@ -0,0 +1,117 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testmodifydocumentation.h"
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <abstractmetafunction.h>
+#include <documentation.h>
+#include <modifications.h>
+#include <complextypeentry.h>
+#include <qtdocparser.h>
+
+#include <qtcompat.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTemporaryDir>
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestModifyDocumentation::testModifyDocumentation()
+{
+ const char cppCode[] = "struct B { void b(); }; class A {};\n";
+ const char xmlCode[] =
+R"(<typesystem package="Foo">
+ <value-type name='B'>
+ <modify-function signature='b()' remove='all'/>
+ </value-type>
+ <value-type name='A'>
+ <modify-documentation xpath='description/brief'>&lt;brief>Modified Brief&lt;/brief></modify-documentation>
+ <modify-documentation xpath='description/para[3]'>&lt;para>Some changed contents here&lt;/para></modify-documentation>
+ </value-type>
+</typesystem>
+)";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+ DocModificationList docMods = classA->typeEntry()->docModifications();
+ QCOMPARE(docMods.size(), 2);
+ QCOMPARE(docMods[0].code().trimmed(), u"<brief>Modified Brief</brief>");
+ QCOMPARE(docMods[0].signature(), QString());
+ QCOMPARE(docMods[1].code().trimmed(), u"<para>Some changed contents here</para>");
+ QCOMPARE(docMods[1].signature(), QString());
+
+ // Create a temporary directory for the documentation file since libxml2
+ // cannot handle Qt resources.
+ QTemporaryDir tempDir(QDir::tempPath() + u"/shiboken_testmodifydocXXXXXX"_s);
+ QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
+ constexpr auto docFileName = "a.xml"_L1;
+ QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.filePath(docFileName)));
+
+ QtDocParser docParser;
+ docParser.setDocumentationDataDirectory(tempDir.path());
+ docParser.fillDocumentation(classA);
+
+ const Documentation &doc = classA->documentation();
+ const QString actualDocSimplified = doc.detailed().simplified();
+ const QString actualBriefSimplified = doc.brief().simplified();
+ QVERIFY(!actualDocSimplified.isEmpty());
+
+const char expectedDoc[] =
+R"(<?xml version="1.0"?>
+<description>oi
+<para>Paragraph number 1</para>
+<para>Paragraph number 2</para>
+<para>Some changed contents here</para>
+</description>
+)";
+ const QString expectedDocSimplified = QString::fromLatin1(expectedDoc).simplified();
+ // Check whether the first modification worked.
+ QVERIFY(actualBriefSimplified.contains(u"Modified Brief"));
+
+#ifndef HAVE_LIBXSLT
+ // QtXmlPatterns is unable to handle para[3] in style sheets,
+ // this only works in its XPath search.
+ QEXPECT_FAIL("", "QtXmlPatterns cannot handle para[3] (QTBUG-66925)", Abort);
+#endif
+ QCOMPARE(actualDocSimplified, expectedDocSimplified);
+}
+
+void TestModifyDocumentation::testInjectAddedFunctionDocumentation()
+{
+ const char cppCode[] ="class A {};\n";
+ const char xmlCode[] = R"XML(
+<typesystem package="Foo">
+ <value-type name='A'>
+ <add-function signature="foo(int@parameter_name@)">
+ <inject-documentation format="target" mode="append">
+ Injected documentation of added function foo.
+ </inject-documentation>
+ </add-function>
+ </value-type>
+</typesystem>
+)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+ const auto f = classA->findFunction("foo");
+ QVERIFY(f);
+ QVERIFY(f->isUserAdded());
+ auto docMods = f->addedFunctionDocModifications();
+ QCOMPARE(docMods.size(), 1);
+ const QString code = docMods.constFirst().code();
+ QVERIFY(code.contains(u"Injected documentation of added function foo."));
+}
+
+// We expand QTEST_MAIN macro but using QCoreApplication instead of QApplication
+// because this test needs an event loop but can't use QApplication to avoid a crash
+// on our ARMEL/FRAMANTLE buildbot
+int main(int argc, char** argv)
+{
+ QCoreApplication app(argc, argv);
+ TestModifyDocumentation tc;
+ return QTest::qExec(&tc, argc, argv);
+}
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h
new file mode 100644
index 000000000..c1cc8f480
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTMODIFYDOCUMENTATION_H
+#define TESTMODIFYDOCUMENTATION_H
+
+#include <QtCore/QObject>
+
+class TestModifyDocumentation : public QObject
+{
+Q_OBJECT
+private slots:
+ void testModifyDocumentation();
+ void testInjectAddedFunctionDocumentation();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc
new file mode 100644
index 000000000..76b1bfc61
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource>
+ <file>a.xml</file>
+ </qresource>
+</RCC>
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp
new file mode 100644
index 000000000..a7d40f70a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp
@@ -0,0 +1,480 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testmodifyfunction.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetabuilder_p.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <modifications.h>
+#include <typesystem.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestModifyFunction::testRenameArgument_data()
+{
+ QTest::addColumn<QByteArray>("pattern");
+ QTest::newRow("fixed_string") << QByteArrayLiteral("method(int)");
+ QTest::newRow("regular_expression") << QByteArrayLiteral("^method.*");
+}
+
+void TestModifyFunction::testRenameArgument()
+{
+ QFETCH(QByteArray, pattern);
+
+ const char cppCode[] = "\
+ struct A {\n\
+ void method(int=0);\n\
+ };\n";
+ const char xmlCode1[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <object-type name='A'>\n\
+ <modify-function signature='";
+ const char xmlCode2[] = R"('>
+ <modify-argument index='1' rename='otherArg'/>
+ </modify-function>
+ </object-type>
+ </typesystem>
+)";
+
+ const QByteArray xmlCode = QByteArray(xmlCode1) + pattern + QByteArray(xmlCode2);
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.constData(), false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto func = classA->findFunction("method");
+ QVERIFY(func);
+
+ QCOMPARE(func->argumentName(1), u"otherArg");
+}
+
+void TestModifyFunction::testOwnershipTransfer()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ virtual A* method();\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <object-type name='A' />\n\
+ <object-type name='B'>\n\
+ <modify-function signature='method()'>\n\
+ <modify-argument index='return'>\n\
+ <define-ownership owner='c++'/>\n\
+ </modify-argument>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto func = classB->findFunction("method");
+ QVERIFY(func);
+
+ QCOMPARE(func->argumentTargetOwnership(func->ownerClass(), 0),
+ TypeSystem::CppOwnership);
+}
+
+
+void TestModifyFunction::invalidateAfterUse()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ virtual void call(int *a);\n\
+ };\n\
+ struct B : A {\n\
+ };\n\
+ struct C : B {\n\
+ virtual void call2(int *a);\n\
+ };\n\
+ struct D : C {\n\
+ virtual void call2(int *a);\n\
+ };\n\
+ struct E : D {\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <object-type name='A'>\n\
+ <modify-function signature='call(int*)'>\n\
+ <modify-argument index='1' invalidate-after-use='true'/>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ <object-type name='B' />\n\
+ <object-type name='C'>\n\
+ <modify-function signature='call2(int*)'>\n\
+ <modify-argument index='1' invalidate-after-use='true'/>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ <object-type name='D'>\n\
+ <modify-function signature='call2(int*)'>\n\
+ <modify-argument index='1' invalidate-after-use='true'/>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ <object-type name='E' />\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ false, u"0.1"_s));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ auto func = classB->findFunction("call");
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
+ QVERIFY(classC);
+ func = classC->findFunction("call");
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+
+ func = classC->findFunction("call2");
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+
+ AbstractMetaClassCPtr classD = AbstractMetaClass::findClass(classes, "D");
+ QVERIFY(classD);
+ func = classD->findFunction("call");
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+
+ func = classD->findFunction("call2");
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+
+ const auto classE = AbstractMetaClass::findClass(classes, "E");
+ QVERIFY(classE);
+ func = classE->findFunction("call");
+ QVERIFY(func);
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+
+ func = classE->findFunction("call2");
+ QVERIFY(func);
+ QCOMPARE(func->modifications().size(), 1);
+ QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
+ QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
+}
+
+void TestModifyFunction::testWithApiVersion()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ virtual A* method();\n\
+ virtual B* methodB();\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <object-type name='A' />\n\
+ <object-type name='B'>\n\
+ <modify-function signature='method()' since='0.1'>\n\
+ <modify-argument index='return'>\n\
+ <define-ownership owner='c++'/>\n\
+ </modify-argument>\n\
+ </modify-function>\n\
+ <modify-function signature='methodB()' since='0.2'>\n\
+ <modify-argument index='return'>\n\
+ <define-ownership owner='c++'/>\n\
+ </modify-argument>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ false, u"0.1"_s));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ auto func = classB->findFunction("method");
+
+ auto returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0);
+ QCOMPARE(returnOwnership, TypeSystem::CppOwnership);
+
+ func = classB->findFunction("methodB");
+ returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0);
+ QVERIFY(returnOwnership != TypeSystem::CppOwnership);
+}
+
+// Modifications on class/typesystem level are tested below
+// in testScopedModifications().
+void TestModifyFunction::testAllowThread()
+{
+ const char cppCode[] =R"CPP(\
+struct A {
+ void f1();
+ void f2();
+ void f3();
+ int getter1() const;
+ int getter2() const;
+};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='A'>
+ <modify-function signature='f2()' allow-thread='auto'/>
+ <modify-function signature='f3()' allow-thread='no'/>
+ <modify-function signature='getter2()const' allow-thread='yes'/>
+ </object-type>
+</typesystem>
+)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ false, u"0.1"_s));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ // Nothing specified, true
+ const auto f1 = classA->findFunction("f1");
+ QVERIFY(f1);
+ QVERIFY(!f1->allowThread());
+
+ // 'auto' specified, should be false for nontrivial function
+ const auto f2 = classA->findFunction("f2");
+ QVERIFY(f2);
+ QVERIFY(f2->allowThread());
+
+ // 'no' specified, should be false
+ const auto f3 = classA->findFunction("f3");
+ QVERIFY(f3);
+ QVERIFY(!f3->allowThread());
+
+ // Nothing specified, should be false for simple getter
+ const auto getter1 = classA->findFunction("getter1");
+ QVERIFY(getter1);
+ QVERIFY(!getter1->allowThread());
+
+ // Forced to true simple getter
+ const auto getter2 = classA->findFunction("getter2");
+ QVERIFY(getter2);
+ QVERIFY(getter2->allowThread()); // Forced to true simple getter
+}
+
+void TestModifyFunction::testGlobalFunctionModification()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ void function(A* a = 0);\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='A'/>\n\
+ <function signature='function(A*)'>\n\
+ <modify-function signature='function(A*)'>\n\
+ <modify-argument index='1'>\n\
+ <replace-type modified-type='A'/>\n\
+ <replace-default-expression with='A()'/>\n\
+ </modify-argument>\n\
+ </modify-function>\n\
+ </function>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ QCOMPARE(builder->globalFunctions().size(), 1);
+
+ auto *td = TypeDatabase::instance();
+ FunctionModificationList mods = td->globalFunctionModifications({u"function(A*)"_s});
+ QCOMPARE(mods.size(), 1);
+ const QList<ArgumentModification> &argMods = mods.constFirst().argument_mods();
+ QCOMPARE(argMods.size(), 1);
+ ArgumentModification argMod = argMods.constFirst();
+ QCOMPARE(argMod.replacedDefaultExpression(), u"A()");
+
+ QVERIFY(!builder->globalFunctions().isEmpty());
+ const auto func = builder->globalFunctions().constFirst();
+ QCOMPARE(func->arguments().size(), 1);
+ const AbstractMetaArgument &arg = func->arguments().constFirst();
+ QCOMPARE(arg.type().cppSignature(), u"A *");
+ QCOMPARE(arg.originalDefaultValueExpression(), u"0");
+ QCOMPARE(arg.defaultValueExpression(), u"A()");
+}
+
+// Tests modifications of exception handling and allow-thread
+// on various levels.
+void TestModifyFunction::testScopedModifications_data()
+{
+ QTest::addColumn<QByteArray>("cppCode");
+ QTest::addColumn<QByteArray>("xmlCode");
+ QTest::addColumn<bool>("expectedGenerateUnspecified");
+ QTest::addColumn<bool>("expectedGenerateNonThrowing");
+ QTest::addColumn<bool>("expectedGenerateThrowing");
+ QTest::addColumn<bool>("expectedAllowThread");
+
+ const QByteArray cppCode = R"CPP(
+struct Base {
+};
+
+struct A : public Base {
+ void unspecified();
+ void nonThrowing() noexcept;
+# if __cplusplus >= 201703L // C++ 17
+ void throwing() noexcept(false);
+#else
+ void throwing() throw(int);
+#endif
+};
+)CPP";
+
+ // Default: Off
+ QTest::newRow("none")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package= 'Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='A'/>
+</typesystem>)XML")
+ << false << false << false // exception
+ << false; // allowthread
+
+ // Modify one function
+ QTest::newRow("modify-function1")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='A'>
+ <modify-function signature='throwing()' exception-handling='auto-on'/>
+ </object-type>
+</typesystem>)XML")
+ << false << false << true // exception
+ << false; // allowthread
+
+ // Flip defaults by modifying functions
+ QTest::newRow("modify-function2")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='A'>
+ <modify-function signature='unspecified()' exception-handling='auto-on'/>
+ <modify-function signature='throwing()' exception-handling='no'/>
+ </object-type>
+</typesystem>)XML")
+ << true << false << false // exception
+ << false; // allowthread
+
+ // Activate on type system level
+ QTest::newRow("typesystem-on")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package='Foo' exception-handling='auto-on' allow-thread='no'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='A'/>
+</typesystem>)XML")
+ << true << false << true // exception
+ << false; // allowthread
+
+ // Activate on class level
+ QTest::newRow("class-on")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='A' exception-handling='auto-on' allow-thread='no'/>
+</typesystem>)XML")
+ << true << false << true // exception
+ << false; // allowthread
+
+ // Activate on base class level
+ QTest::newRow("baseclass-on")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base' exception-handling='auto-on' allow-thread='no'/>
+ <object-type name='A'/>
+</typesystem>)XML")
+ << true << false << true // exception
+ << false; // allowthread
+
+ // Override value on class level
+ QTest::newRow("override-class-on")
+ << cppCode
+ << QByteArray(R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <object-type name='Base'/>
+ <object-type name='A' exception-handling='auto-on'>
+ <modify-function signature='throwing()' exception-handling='no'/>
+ </object-type>
+</typesystem>)XML")
+ << true << false << false // exception
+ << false; // allowthread
+}
+
+void TestModifyFunction::testScopedModifications()
+{
+ QFETCH(QByteArray, cppCode);
+ QFETCH(QByteArray, xmlCode);
+ QFETCH(bool, expectedGenerateUnspecified);
+ QFETCH(bool, expectedGenerateNonThrowing);
+ QFETCH(bool, expectedGenerateThrowing);
+ QFETCH(bool, expectedAllowThread);
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode.constData(), xmlCode.constData(), false));
+ QVERIFY(builder);
+
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
+ QVERIFY(classA);
+
+ auto f = classA->findFunction("unspecified");
+ QVERIFY(f);
+ QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown);
+ QCOMPARE(f->generateExceptionHandling(), expectedGenerateUnspecified);
+ QCOMPARE(f->allowThread(), expectedAllowThread);
+
+ f = classA->findFunction("nonThrowing");
+ QVERIFY(f);
+ QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept);
+ QCOMPARE(f->generateExceptionHandling(), expectedGenerateNonThrowing);
+
+ f = classA->findFunction("throwing");
+ QVERIFY(f);
+ QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws);
+ QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing);
+}
+
+void TestModifyFunction::testSnakeCaseRenaming_data()
+{
+ QTest::addColumn<QLatin1StringView>("name");
+ QTest::addColumn<QLatin1StringView>("expected");
+ QTest::newRow("s1")
+ << "snakeCaseFunc"_L1 << "snake_case_func"_L1;
+ QTest::newRow("s2")
+ << "SnakeCaseFunc"_L1 << "snake_case_func"_L1;
+ QTest::newRow("consecutive-uppercase")
+ << "snakeCAseFunc"_L1 << "snakeCAseFunc"_L1;
+}
+
+void TestModifyFunction::testSnakeCaseRenaming()
+{
+ QFETCH(QLatin1StringView, name);
+ QFETCH(QLatin1StringView, expected);
+
+ const QString actual = AbstractMetaBuilder::getSnakeCaseName(name);
+ QCOMPARE(actual, expected);
+}
+
+QTEST_APPLESS_MAIN(TestModifyFunction)
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h
new file mode 100644
index 000000000..8a4f5d826
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTABSTRACTMETACLASS_H
+#define TESTABSTRACTMETACLASS_H
+
+#include <QtCore/QObject>
+
+class TestModifyFunction : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testOwnershipTransfer();
+ void testWithApiVersion();
+ void testAllowThread();
+ void testRenameArgument_data();
+ void testRenameArgument();
+ void invalidateAfterUse();
+ void testGlobalFunctionModification();
+ void testScopedModifications_data();
+ void testScopedModifications();
+ void testSnakeCaseRenaming_data();
+ void testSnakeCaseRenaming();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp
new file mode 100644
index 000000000..1cf4c8e0f
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testmultipleinheritance.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <typesystem.h>
+
+void TestMultipleInheritance::testVirtualClass()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ virtual ~A();\n\
+ virtual void theBug();\n\
+ };\n\
+ struct B {\n\
+ virtual ~B();\n\
+ };\n\
+ struct C : A, B {\n\
+ };\n\
+ struct D : C {\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <object-type name='A' />\n\
+ <object-type name='B' />\n\
+ <object-type name='C' />\n\
+ <object-type name='D' />\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 4);
+
+ const auto classD = AbstractMetaClass::findClass(classes, "D");
+ bool functionFound = false;
+ for (const auto &f : classD->functions()) {
+ if (f->name() == u"theBug") {
+ functionFound = true;
+ break;
+ }
+ }
+ QVERIFY(functionFound);
+
+}
+
+QTEST_APPLESS_MAIN(TestMultipleInheritance)
diff --git a/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h
new file mode 100644
index 000000000..ec9935305
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTMULTIPLEINHERITANCE_H
+#define TESTMULTIPLEINHERITANCE_H
+
+#include <QtCore/QObject>
+
+class AbstractMetaBuilder;
+
+class TestMultipleInheritance : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testVirtualClass();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testnamespace.cpp b/sources/shiboken6/ApiExtractor/tests/testnamespace.cpp
new file mode 100644
index 000000000..3773e614a
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testnamespace.cpp
@@ -0,0 +1,77 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testnamespace.h"
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <abstractmetaenum.h>
+#include <typesystem.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void NamespaceTest::testNamespaceMembers()
+{
+ const char cppCode[] = "\
+ namespace Namespace\n\
+ {\n\
+ enum Option {\n\
+ OpZero,\n\
+ OpOne\n\
+ };\n\
+ void foo(Option opt);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <namespace-type name='Namespace'>\n\
+ <enum-type name='Option' />\n\
+ </namespace-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto ns = AbstractMetaClass::findClass(classes, "Namespace");
+ QVERIFY(ns);
+ auto metaEnum = ns->findEnum(u"Option"_s);
+ QVERIFY(metaEnum.has_value());
+ const auto func = ns->findFunction("foo");
+ QVERIFY(func);
+}
+
+void NamespaceTest::testNamespaceInnerClassMembers()
+{
+ const char cppCode[] = "\
+ namespace OuterNamespace\n\
+ {\n\
+ namespace InnerNamespace {\n\
+ struct SomeClass {\n\
+ void method();\n\
+ };\n\
+ };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <namespace-type name='OuterNamespace'>\n\
+ <namespace-type name='InnerNamespace'>\n\
+ <value-type name='SomeClass'/>\n\
+ </namespace-type>\n\
+ </namespace-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto ons = AbstractMetaClass::findClass(classes, "OuterNamespace");
+ QVERIFY(ons);
+ const auto ins = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace");
+ QVERIFY(ins);
+ const auto sc = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace::SomeClass");
+ QVERIFY(sc);
+ const auto meth = sc->findFunction("method");
+ QVERIFY(meth);
+}
+
+QTEST_APPLESS_MAIN(NamespaceTest)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testnamespace.h b/sources/shiboken6/ApiExtractor/tests/testnamespace.h
new file mode 100644
index 000000000..af46bdea3
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testnamespace.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTNAMESPACE_H
+#define TESTNAMESPACE_H
+
+#include <QtCore/QObject>
+
+// The class is named 'NamespaceTest' to avoid clashes with Qt COIN using
+// '-qtnamespace TestNamespace'.
+class NamespaceTest : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testNamespaceMembers();
+ void testNamespaceInnerClassMembers();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp
new file mode 100644
index 000000000..10ca1a0f6
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp
@@ -0,0 +1,115 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testnestedtypes.h"
+#include "testutil.h"
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <codesnip.h>
+#include <modifications.h>
+#include <complextypeentry.h>
+#include <primitivetypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestNestedTypes::testNestedTypesModifications()
+{
+ const char cppCode[] = "\
+ namespace OuterNamespace {\n\
+ namespace InnerNamespace {\n\
+ struct SomeClass {\n\
+ void method() {}\n\
+ };\n\
+ };\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <namespace-type name='OuterNamespace'>\n\
+ <namespace-type name='InnerNamespace'>\n\
+ <inject-code class='native'>custom_code1();</inject-code>\n\
+ <add-function signature='method()' return-type='OuterNamespace::InnerNamespace::SomeClass'>\n\
+ <inject-code class='target'>custom_code2();</inject-code>\n\
+ </add-function>\n\
+ <object-type name='SomeClass' target-lang-name='RenamedSomeClass'>\n\
+ <modify-function signature='method()' remove='all'/>\n\
+ </object-type>\n\
+ </namespace-type>\n\
+ </namespace-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto ons = AbstractMetaClass::findClass(classes, "OuterNamespace");
+ QVERIFY(ons);
+
+ const auto ins = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace");
+ QVERIFY(ins);
+ QCOMPARE(ins->functions().size(), 1);
+ QCOMPARE(ins->typeEntry()->codeSnips().size(), 1);
+ CodeSnip snip = ins->typeEntry()->codeSnips().constFirst();
+ QCOMPARE(snip.code().trimmed(), u"custom_code1();");
+
+ const auto addedFunc = ins->functions().constFirst();
+ QVERIFY(addedFunc->isUserAdded());
+ QCOMPARE(addedFunc->access(), Access::Public);
+ QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction);
+ QCOMPARE(addedFunc->type().minimalSignature(),
+ u"OuterNamespace::InnerNamespace::SomeClass");
+
+ QCOMPARE(addedFunc->modifications().size(), 1);
+ QVERIFY(addedFunc->modifications().constFirst().isCodeInjection());
+ snip = addedFunc->modifications().constFirst().snips().constFirst();
+ QCOMPARE(snip.code().trimmed(), u"custom_code2();");
+
+ const auto sc =
+ AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace::SomeClass");
+ QVERIFY(sc);
+ QCOMPARE(sc->functions().size(), 2); // default constructor and removed method
+ const auto removedFunc = sc->functions().constLast();
+ QVERIFY(removedFunc->isModifiedRemoved());
+}
+
+
+void TestNestedTypes::testDuplicationOfNestedTypes()
+{
+ const char cppCode[] = "\
+ namespace Namespace {\n\
+ class SomeClass {};\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <namespace-type name='Namespace'>\n\
+ <value-type name='SomeClass'>\n\
+ <add-function signature='createSomeClass(Namespace::SomeClass)'/>\n\
+ </value-type>\n\
+ </namespace-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 2);
+ const auto nspace = AbstractMetaClass::findClass(classes, "Namespace");
+ QVERIFY(nspace);
+ const auto cls1 = AbstractMetaClass::findClass(classes, "SomeClass");
+ QVERIFY(cls1);
+ const auto cls2 = AbstractMetaClass::findClass(classes, "Namespace::SomeClass");
+ QVERIFY(cls2);
+ QCOMPARE(cls1, cls2);
+ QCOMPARE(cls1->name(), u"SomeClass");
+ QCOMPARE(cls1->qualifiedCppName(), u"Namespace::SomeClass");
+
+ auto t1 = TypeDatabase::instance()->findType(u"Namespace::SomeClass"_s);
+ QVERIFY(t1);
+ auto t2 = TypeDatabase::instance()->findType(u"SomeClass"_s);
+ QVERIFY(!t2);
+}
+
+QTEST_APPLESS_MAIN(TestNestedTypes)
diff --git a/sources/shiboken6/ApiExtractor/tests/testnestedtypes.h b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.h
new file mode 100644
index 000000000..544ea05ab
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTNESTEDTYPES_H
+#define TESTNESTEDTYPES_H
+
+#include <QtCore/QObject>
+
+class TestNestedTypes : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testNestedTypesModifications();
+ void testDuplicationOfNestedTypes();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp
new file mode 100644
index 000000000..9eef7ec47
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testnumericaltypedef.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <typesystem.h>
+
+void TestNumericalTypedef::testNumericalTypedef()
+{
+ const char cppCode[] = "\
+ typedef double real;\n\
+ void funcDouble(double);\n\
+ void funcReal(real);\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='double'/>\n\
+ <primitive-type name='real'/>\n\
+ <function signature='funcDouble(double)'/>\n\
+ <function signature='funcReal(real)'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ QCOMPARE(builder->globalFunctions().size(), 2);
+ auto funcDouble = builder->globalFunctions().constFirst();
+ auto funcReal = builder->globalFunctions().constLast();
+ QVERIFY(funcReal);
+
+ if (funcDouble->name() == u"funcReal")
+ std::swap(funcDouble, funcReal);
+
+ QCOMPARE(funcDouble->minimalSignature(), u"funcDouble(double)");
+ QCOMPARE(funcReal->minimalSignature(), u"funcReal(real)");
+
+ const AbstractMetaType doubleType = funcDouble->arguments().constFirst().type();
+ QCOMPARE(doubleType.cppSignature(), u"double");
+ QVERIFY(doubleType.isPrimitive());
+ QVERIFY(isCppPrimitive(doubleType.typeEntry()));
+
+ const AbstractMetaType realType = funcReal->arguments().constFirst().type();
+ QCOMPARE(realType.cppSignature(), u"real");
+ QVERIFY(realType.isPrimitive());
+ QVERIFY(isCppPrimitive(realType.typeEntry()));
+}
+
+void TestNumericalTypedef::testUnsignedNumericalTypedef()
+{
+ const char cppCode[] = "\
+ typedef unsigned short custom_ushort;\n\
+ void funcUnsignedShort(unsigned short);\n\
+ void funcUShort(custom_ushort);\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='short'/>\n\
+ <primitive-type name='unsigned short'/>\n\
+ <primitive-type name='custom_ushort'/>\n\
+ <function signature='funcUnsignedShort(unsigned short)'/>\n\
+ <function signature='funcUShort(custom_ushort)'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ QCOMPARE(builder->globalFunctions().size(), 2);
+ auto funcUnsignedShort = builder->globalFunctions().constFirst();
+ auto funcUShort = builder->globalFunctions().constLast();
+
+ if (funcUnsignedShort->name() == u"funcUShort")
+ std::swap(funcUnsignedShort, funcUShort);
+
+ QCOMPARE(funcUnsignedShort->minimalSignature(), u"funcUnsignedShort(unsigned short)");
+ QCOMPARE(funcUShort->minimalSignature(), u"funcUShort(custom_ushort)");
+
+ const AbstractMetaType unsignedShortType = funcUnsignedShort->arguments().constFirst().type();
+ QCOMPARE(unsignedShortType.cppSignature(), u"unsigned short");
+ QVERIFY(unsignedShortType.isPrimitive());
+ QVERIFY(isCppPrimitive(unsignedShortType.typeEntry()));
+
+ const AbstractMetaType ushortType = funcUShort->arguments().constFirst().type();
+ QCOMPARE(ushortType.cppSignature(), u"custom_ushort");
+ QVERIFY(ushortType.isPrimitive());
+ QVERIFY(isCppPrimitive(ushortType.typeEntry()));
+}
+
+QTEST_APPLESS_MAIN(TestNumericalTypedef)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h
new file mode 100644
index 000000000..32f549836
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTNUMERICALTYPEDEF_H
+#define TESTNUMERICALTYPEDEF_H
+
+#include <QtCore/QObject>
+
+class TestNumericalTypedef : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testNumericalTypedef();
+ void testUnsignedNumericalTypedef();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp
new file mode 100644
index 000000000..99cced09d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testprimitivetypetag.h"
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <primitivetypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestPrimitiveTypeTag::testPrimitiveTypeDefaultConstructor()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {};\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <primitive-type name='A' default-constructor='A()'/>\n\
+ <object-type name='B'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+
+ auto typeEntry = TypeDatabase::instance()->findPrimitiveType(u"A"_s);
+ QVERIFY(typeEntry);
+ QVERIFY(typeEntry->hasDefaultConstructor());
+ QCOMPARE(typeEntry->defaultConstructor(), u"A()");
+}
+
+QTEST_APPLESS_MAIN(TestPrimitiveTypeTag)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h
new file mode 100644
index 000000000..3a0e05138
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTPRIMITIVETYPETAG_H
+#define TESTPRIMITIVETYPETAG_H
+
+#include <QtCore/QObject>
+
+class TestPrimitiveTypeTag : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testPrimitiveTypeDefaultConstructor();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp
new file mode 100644
index 000000000..f2e261624
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testrefcounttag.h"
+#include "testutil.h"
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <modifications.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestRefCountTag::testReferenceCountTag()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ void keepObject(B* b);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <object-type name='A'/>\n\
+ <object-type name='B'>\n\
+ <modify-function signature='keepObject(B*)'>\n\
+ <modify-argument index='1'>\n\
+ <reference-count action='add'/>\n\
+ </modify-argument>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto func = classB->findFunction("keepObject");
+ QVERIFY(func);
+ const auto refCount =
+ func->modifications().constFirst().argument_mods().constFirst().referenceCounts().constFirst();
+ QCOMPARE(refCount.action, ReferenceCount::Add);
+}
+
+void TestRefCountTag::testWithApiVersion()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ void keepObject(B*, B*);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <object-type name='A'/>\n\
+ <object-type name='B'>\n\
+ <modify-function signature='keepObject(B*, B*)'>\n\
+ <modify-argument index='1' since='0.1'>\n\
+ <reference-count action='add'/>\n\
+ </modify-argument>\n\
+ <modify-argument index='2' since='0.2'>\n\
+ <reference-count action='add'/>\n\
+ </modify-argument>\n\
+ </modify-function>\n\
+ </object-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ false, u"0.1"_s));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto func = classB->findFunction("keepObject");
+ QVERIFY(func);
+ const auto refCount =
+ func->modifications().constFirst().argument_mods().constFirst().referenceCounts().constFirst();
+ QCOMPARE(refCount.action, ReferenceCount::Add);
+
+ QCOMPARE(func->modifications().size(), 1);
+}
+
+
+QTEST_APPLESS_MAIN(TestRefCountTag)
+
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testrefcounttag.h b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.h
new file mode 100644
index 000000000..6093c6f7b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTREFCOUNTTAG_H
+#define TESTREFCOUNTTAG_H
+
+#include <QtCore/QObject>
+
+class TestRefCountTag : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testReferenceCountTag();
+ void testWithApiVersion();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp
new file mode 100644
index 000000000..ae85c5a86
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testreferencetopointer.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <typesystem.h>
+
+void TestReferenceToPointer::testReferenceToPointerArgument()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {\n\
+ void dummy(A*&);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <object-type name='A'/>\n\
+ <object-type name='B'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ const auto func = classB->findFunction("dummy");
+ QVERIFY(func);
+ QCOMPARE(func->arguments().constFirst().type().minimalSignature(), u"A*&");
+}
+
+QTEST_APPLESS_MAIN(TestReferenceToPointer)
+
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h
new file mode 100644
index 000000000..2a7b34807
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTREFERENCETOPOINTER_H
+#define TESTREFERENCETOPOINTER_H
+
+#include <QtCore/QObject>
+
+class TestReferenceToPointer : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testReferenceToPointerArgument();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testremovefield.cpp b/sources/shiboken6/ApiExtractor/tests/testremovefield.cpp
new file mode 100644
index 000000000..2cc82071b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testremovefield.cpp
@@ -0,0 +1,76 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testremovefield.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetatype.h>
+#include <abstractmetalang.h>
+#include <typesystem.h>
+
+using namespace Qt::StringLiterals;
+
+void TestRemoveField::testRemoveField()
+{
+ const char cppCode[] = "\
+ struct A {\n\
+ int fieldA;\n\
+ int fieldB;\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <modify-field name='fieldB' remove='all'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->fields().size(), 1);
+ const AbstractMetaField &fieldA = classA->fields().constFirst();
+ QCOMPARE(fieldA.name(), u"fieldA");
+}
+
+// Verify that 'static constexpr' fields are seen as static/const and
+// appear fully qualified for function parameter default values.
+void TestRemoveField::testConstExprField()
+{
+ const char cppCode[] = R"(
+struct A {
+ static constexpr int constExprField = 44;
+
+ void f(int iParam=constExprField);
+};
+)";
+
+ const char xmlCode[] = R"(
+<typesystem package="Foo">
+ <value-type name='A'/>
+</typesystem>
+)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto &fields = classA->fields();
+ QCOMPARE(fields.size(), 1);
+ QVERIFY(fields.constFirst().isStatic());
+ QVERIFY(fields.constFirst().type().isConstant());
+ const auto function = classA->findFunction("f"_L1);
+ QVERIFY(function);
+ const auto &arguments = function->arguments();
+ QCOMPARE(arguments.size(), 1);
+ QCOMPARE(arguments.constFirst().defaultValueExpression(), "A::constExprField"_L1);
+}
+
+QTEST_APPLESS_MAIN(TestRemoveField)
+
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testremovefield.h b/sources/shiboken6/ApiExtractor/tests/testremovefield.h
new file mode 100644
index 000000000..05912d99e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testremovefield.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTREMOVEFIELD_H
+#define TESTREMOVEFIELD_H
+
+#include <QtCore/QObject>
+
+class TestRemoveField : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testRemoveField();
+ void testConstExprField();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp
new file mode 100644
index 000000000..87e318e95
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testremoveimplconv.h"
+#include "testutil.h"
+#include <QtTest/QTest>
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <complextypeentry.h>
+
+// When a constructor able to trigger implicity conversions is removed
+// it should not appear in the implicity conversion list.
+void TestRemoveImplConv::testRemoveImplConv()
+{
+ const char cppCode[] = "\
+ struct A {};\n\
+ struct B {};\n\
+ struct C {\n\
+ C(const A&);\n\
+ C(const B&);\n\
+ };\n";
+ const char xmlCode[] = "\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'/>\n\
+ <value-type name='B'/>\n\
+ <value-type name='C'>\n\
+ <modify-function signature='C(const A&amp;)' remove='all'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 3);
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
+ QVERIFY(classC);
+ const auto implConv = classC->implicitConversions();
+ QCOMPARE(implConv.size(), 1);
+ QCOMPARE(implConv.constFirst()->arguments().constFirst().type().typeEntry(),
+ classB->typeEntry());
+}
+
+QTEST_APPLESS_MAIN(TestRemoveImplConv)
diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h
new file mode 100644
index 000000000..d11d30633
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTREMOVEIMPLCONV_H
+#define TESTREMOVEIMPLCONV_H
+
+#include <QtCore/QObject>
+
+class TestRemoveImplConv : public QObject
+{
+Q_OBJECT
+private slots:
+ void testRemoveImplConv();
+};
+
+#endif // TESTREMOVEIMPLCONV_H
diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp
new file mode 100644
index 000000000..17a069b5e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp
@@ -0,0 +1,98 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testremoveoperatormethod.h"
+#include "testutil.h"
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <typesystem.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestRemoveOperatorMethod::testRemoveOperatorMethod()
+{
+ const char cppCode[] = R"(#include <cstdint>
+
+struct Char {};
+struct ByteArray {};
+struct String {};
+
+struct A {
+ A& operator>>(char&);
+ A& operator>>(char*);
+ A& operator>>(short&);
+ A& operator>>(unsigned short&);
+ A& operator>>(int&);
+ A& operator>>(unsigned int&);
+ A& operator>>(int64_t&);
+ A& operator>>(uint64_t&);
+ A& operator>>(float&);
+ A& operator>>(double&);
+ A& operator>>(Char&);
+ A& operator>>(ByteArray&);
+ A& operator>>(String&);
+};
+)";
+
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='char'/>\n\
+ <primitive-type name='short'/>\n\
+ <primitive-type name='unsigned short'/>\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='unsigned int'/>\n\
+ <primitive-type name='int64_t'/>\n\
+ <primitive-type name='uint64_t'/>\n\
+ <primitive-type name='float'/>\n\
+ <primitive-type name='double'/>\n\
+ <primitive-type name='Char'/>\n\
+ <primitive-type name='String'/>\n\
+ <value-type name='ByteArray'/>\n\
+ <object-type name='A'>\n\
+ <modify-function signature='operator&gt;&gt;(char&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(char*)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(short&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(unsigned short&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(int&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(unsigned int&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(int64_t&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(uint64_t&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(float&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(double&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(Char&amp;)' remove='all'/>\n\
+ <modify-function signature='operator&gt;&gt;(String&amp;)' remove='all'/>\n\
+ </object-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->functions().size(), 14);
+ QStringList removedSignatures;
+ removedSignatures.append(u"operator>>(char&)"_s);
+ removedSignatures.append(u"operator>>(char*)"_s);
+ removedSignatures.append(u"operator>>(short&)"_s);
+ removedSignatures.append(u"operator>>(unsigned short&)"_s);
+ removedSignatures.append(u"operator>>(int&)"_s);
+ removedSignatures.append(u"operator>>(unsigned int&)"_s);
+ removedSignatures.append(u"operator>>(int64_t&)"_s);
+ removedSignatures.append(u"operator>>(uint64_t&)"_s);
+ removedSignatures.append(u"operator>>(float&)"_s);
+ removedSignatures.append(u"operator>>(double&)"_s);
+ removedSignatures.append(u"operator>>(Char&)"_s);
+ removedSignatures.append(u"operator>>(String&)"_s);
+ auto notRemoved = classA->functions().size();
+ for (const auto &f : classA->functions()) {
+ QCOMPARE(f->isModifiedRemoved(), bool(removedSignatures.contains(f->minimalSignature())));
+ notRemoved -= int(f->isModifiedRemoved());
+ }
+ QCOMPARE(notRemoved, 2);
+}
+
+QTEST_APPLESS_MAIN(TestRemoveOperatorMethod)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h
new file mode 100644
index 000000000..6ec335e0c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTREMOVEOPERATORMETHOD_H
+#define TESTREMOVEOPERATORMETHOD_H
+
+#include <QtCore/QObject>
+
+class TestRemoveOperatorMethod : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testRemoveOperatorMethod();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp b/sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp
new file mode 100644
index 000000000..67ebcc606
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp
@@ -0,0 +1,281 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testresolvetype.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetaenum.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <complextypeentry.h>
+#include <enumtypeentry.h>
+#include <primitivetypeentry.h>
+#include <typedatabase.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestResolveType::initTestCase()
+{
+ // For enum lookup in testFixDefaultArguments()
+ AbstractMetaBuilder::setCodeModelTestMode(true);
+}
+
+void TestResolveType::testResolveReturnTypeFromParentScope()
+{
+ const char cppCode[] = "\n\
+ namespace A {\n\
+ struct B {\n\
+ struct C {};\n\
+ };\n\
+ struct D : public B::C {\n\
+ C* foo = 0;\n\
+ C* method();\n\
+ };\n\
+ };";
+ const char xmlCode[] = R"XML(
+ <typesystem package='Foo'>
+ <namespace-type name='A'>
+ <value-type name='B'>
+ <value-type name='C'/>
+ </value-type>
+ <value-type name='D'/>
+ </namespace-type>
+ </typesystem>)XML";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classD = AbstractMetaClass::findClass(classes, "A::D");
+ QVERIFY(classD);
+ const auto meth = classD->findFunction("method");
+ QVERIFY(meth);
+ QVERIFY(meth);
+}
+
+// Helper classes and functions for testing default value fixing.
+// Put the AbstractMetaBuilder into test fixture struct to avoid having
+// to re-parse for each data row.
+
+struct DefaultValuesFixture
+{
+ std::shared_ptr<AbstractMetaBuilder> builder;
+
+ AbstractMetaType intType;
+ AbstractMetaType stringType;
+ AbstractMetaType classType;
+ AbstractMetaType listType;
+ AbstractMetaType enumType;
+ AbstractMetaClassCPtr klass{};
+};
+
+Q_DECLARE_METATYPE(DefaultValuesFixture)
+Q_DECLARE_METATYPE(AbstractMetaType)
+
+static int populateDefaultValuesFixture(DefaultValuesFixture *fixture)
+{
+ static const char cppCode[] =R"(
+#include <string>
+#include <list>
+
+namespace Namespace {
+class Test
+{
+public:
+ enum Enum { enumValue1, enumValue2 };
+
+ explicit Test(int x = INT_FIELD_1);
+ explicit Test(const std::string &t = std::string(CHAR_FIELD_1));
+
+ static void listFunc(std::list<Test> list = std::list<Test>());
+
+ static const int INT_FIELD_1 = 42;
+ static const char *CHAR_FIELD_1;
+ static const Enum DefaultValue = enumValue1;
+};
+} // Namespace
+)";
+ static const char xmlCode[] = R"(
+<typesystem package="Foo">
+ <namespace-type name='Namespace'>
+ <value-type name='Test'>
+ <enum-type name='Enum'/>
+ </value-type>
+ </namespace-type>
+ <container-type name="std::list" type="list"/>
+</typesystem>
+)";
+
+ fixture->builder.reset(TestUtil::parse(cppCode, xmlCode, false));
+ if (!fixture->builder)
+ return -1;
+
+ for (const auto &klass : fixture->builder->classes()) {
+ if (klass->name() == u"Test") {
+ fixture->klass = klass;
+ break;
+ }
+ }
+ if (!fixture->klass)
+ return -2;
+
+ fixture->classType = AbstractMetaType(fixture->klass->typeEntry());
+ fixture->classType.decideUsagePattern();
+
+ for (const auto &f : fixture->klass->findFunctions("Test")) {
+ if (f->functionType() == AbstractMetaFunction::ConstructorFunction
+ && f->arguments().size() == 1) {
+ const auto type = f->arguments().constFirst().type();
+ if (type.name() == u"int")
+ fixture->intType = type;
+ else
+ fixture->stringType = type;
+ }
+ }
+ if (fixture->intType.isVoid() || fixture->stringType.isVoid())
+ return -3;
+
+ auto listFunc = fixture->klass->findFunction("listFunc");
+ if (!listFunc || listFunc->arguments().size() != 1)
+ return -3;
+ fixture->listType = listFunc->arguments().constFirst().type();
+
+ fixture->enumType = AbstractMetaType(fixture->klass->enums().constFirst().typeEntry());
+ fixture->enumType.decideUsagePattern();
+
+ return 0;
+}
+
+void TestResolveType::testFixDefaultArguments_data()
+{
+ DefaultValuesFixture fixture;
+ const int setupOk = populateDefaultValuesFixture(&fixture);
+
+ QTest::addColumn<DefaultValuesFixture>("fixture");
+ QTest::addColumn<int>("setupOk"); // To verify setup
+ QTest::addColumn<AbstractMetaType>("metaType"); // Type and parameters for fixup
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("int") << fixture << setupOk
+ << fixture.intType << "1" << "1";
+ QTest::newRow("int-macro") << fixture << setupOk
+ << fixture.intType << "GL_MACRO" << "GL_MACRO";
+ QTest::newRow("int-enum") << fixture << setupOk
+ << fixture.intType << "enumValue1" << "Namespace::Test::Enum::enumValue1";
+
+ // Test expansion of container types
+ QString expected = u"std::list<Namespace::Test>()"_s;
+ QTest::newRow("list")
+ << fixture << setupOk << fixture.listType
+ << expected << expected;
+ QTest::newRow("partially qualified list")
+ << fixture << setupOk << fixture.listType
+ << "std::list<Test>()" << expected;
+
+ // Test field expansion
+ expected = u"Namespace::Test::INT_FIELD_1"_s;
+ QTest::newRow("qualified class field")
+ << fixture << setupOk << fixture.intType
+ << expected << expected;
+ QTest::newRow("partially qualified class field")
+ << fixture << setupOk << fixture.intType
+ << "Test::INT_FIELD_1" << expected;
+ QTest::newRow("unqualified class field")
+ << fixture << setupOk << fixture.intType
+ << "INT_FIELD_1" << expected;
+
+ // Test field expansion when constructing some class
+ expected = u"QLatin1String(Namespace::Test::CHAR_FIELD_1)"_s;
+ QTest::newRow("class from qualified class field")
+ << fixture << setupOk << fixture.classType
+ << expected << expected;
+ QTest::newRow("class from partially qualified class field")
+ << fixture << setupOk << fixture.classType
+ << "QLatin1String(Test::CHAR_FIELD_1)" << expected;
+ QTest::newRow("class from unqualified class field")
+ << fixture << setupOk << fixture.classType
+ << "QLatin1String(CHAR_FIELD_1)" << expected;
+
+ // Test field expansion when constructing class itself
+ expected = u"Namespace::Test(Namespace::Test::CHAR_FIELD_1)"_s;
+ QTest::newRow("self from qualified class field")
+ << fixture << setupOk << fixture.classType
+ << expected << expected;
+ QTest::newRow("self from partially qualified class field")
+ << fixture << setupOk << fixture.classType
+ << "Test(Test::CHAR_FIELD_1)" << expected;
+ QTest::newRow("self from unqualified class field")
+ << fixture << setupOk << fixture.classType
+ << "Test(CHAR_FIELD_1)" << expected;
+
+ // Test enum expansion when constructing class itself
+ expected = u"Namespace::Test(Namespace::Test::Enum::enumValue1)"_s;
+ QTest::newRow("self from qualified enum")
+ << fixture << setupOk << fixture.classType
+ << expected << expected;
+ QTest::newRow("self from enum")
+ << fixture << setupOk << fixture.classType
+ << "Test(enumValue1)" << expected;
+
+ // Don't qualify fields to "Test::Enum::DefaultValue"
+ QTest::newRow("enum from static field")
+ << fixture << setupOk << fixture.enumType
+ << "DefaultValue" << u"Namespace::Test::DefaultValue"_s;
+}
+
+void TestResolveType::testFixDefaultArguments()
+{
+ QFETCH(DefaultValuesFixture, fixture);
+ QFETCH(int, setupOk);
+ QFETCH(AbstractMetaType, metaType);
+ QFETCH(QString, input);
+ QFETCH(QString, expected);
+ QCOMPARE(setupOk, 0);
+ const QString actual = fixture.builder->fixDefaultValue(input, metaType, fixture.klass);
+ QCOMPARE(actual, expected);
+}
+
+// Verify that the typedefs of the C++ 11 integer types (int32_t, ...)
+// are seen by the C++ parser, otherwise they are handled as unknown
+// primitive types, causing invalid code to be generated.
+// (see BuilderPrivate::visitHeader(),
+// sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp).
+void TestResolveType::testCppTypes()
+{
+ static const char cppCode[] =R"(
+#include <cstdint>
+
+class Test
+{
+public:
+ explicit Test(int32_t v);
+};
+)";
+ static const char xmlCode[] = R"(
+<typesystem package="Foo">
+ <value-type name='Test'/>
+ <primitive-type name='int32_t'/>
+</typesystem>
+)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto testClass = AbstractMetaClass::findClass(classes, "Test");
+ QVERIFY(testClass);
+
+ auto *tdb = TypeDatabase::instance();
+ auto int32TEntry = tdb->findType(u"int32_t"_s);
+ QVERIFY2(int32TEntry, "int32_t not found");
+ QVERIFY(int32TEntry->isPrimitive());
+ auto int32T = std::static_pointer_cast<const PrimitiveTypeEntry>(int32TEntry);
+ auto basicType = basicReferencedTypeEntry(int32T);
+ QVERIFY2(basicType != int32T,
+ "Typedef for int32_t not found. Check the system include paths.");
+}
+
+QTEST_APPLESS_MAIN(TestResolveType)
diff --git a/sources/shiboken6/ApiExtractor/tests/testresolvetype.h b/sources/shiboken6/ApiExtractor/tests/testresolvetype.h
new file mode 100644
index 000000000..a07855eab
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testresolvetype.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTRESOLVETYPE_H
+#define TESTRESOLVETYPE_H
+
+#include <QtCore/QObject>
+
+class TestResolveType : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void initTestCase();
+
+ void testResolveReturnTypeFromParentScope();
+ void testFixDefaultArguments_data();
+ void testFixDefaultArguments();
+ void testCppTypes();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp
new file mode 100644
index 000000000..f4eecff2c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp
@@ -0,0 +1,129 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testreverseoperators.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <typesystem.h>
+#include <clangparser/compilersupport.h>
+
+#include <algorithm>
+
+void TestReverseOperators::testReverseSum()
+{
+ const char cppCode[] = "struct A {\n\
+ A& operator+(int);\n\
+ };\n\
+ A& operator+(int, const A&);";
+ const char xmlCode[] = "\n\
+ <typesystem package=\"Foo\">\n\
+ <primitive-type name='int' />\n\
+ <value-type name='A' />\n\
+ </typesystem>";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->functions().size(), 4);
+
+ AbstractMetaFunctionCPtr reverseOp;
+ AbstractMetaFunctionCPtr normalOp;
+ for (const auto &func : classA->functions()) {
+ if (func->name() == u"operator+") {
+ if (func->isReverseOperator())
+ reverseOp = func;
+ else
+ normalOp = func;
+ }
+ }
+
+ QVERIFY(normalOp);
+ QVERIFY(!normalOp->isReverseOperator());
+ QCOMPARE(normalOp->arguments().size(), 1);
+ QVERIFY(reverseOp);
+ QVERIFY(reverseOp->isReverseOperator());
+ QCOMPARE(reverseOp->arguments().size(), 1);
+}
+
+void TestReverseOperators::testReverseSumWithAmbiguity()
+{
+ const char cppCode[] = "\n\
+ struct A { A operator+(int); };\n\
+ A operator+(int, const A&);\n\
+ struct B {};\n\
+ B operator+(const A&, const B&);\n\
+ B operator+(const B&, const A&);\n\
+ ";
+ const char xmlCode[] = "\n\
+ <typesystem package=\"Foo\">\n\
+ <primitive-type name='int' />\n\
+ <value-type name='A' />\n\
+ <value-type name='B' />\n\
+ </typesystem>";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->functions().size(), 4);
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ QCOMPARE(classB->functions().size(), 4);
+
+ AbstractMetaFunctionCPtr reverseOp;
+ AbstractMetaFunctionCPtr normalOp;
+ for (const auto &func : classB->functions()) {
+ if (func->name() == u"operator+") {
+ if (func->isReverseOperator())
+ reverseOp = func;
+ else
+ normalOp = func;
+ }
+ }
+ QVERIFY(normalOp);
+ QVERIFY(!normalOp->isReverseOperator());
+ QCOMPARE(normalOp->arguments().size(), 1);
+ QCOMPARE(normalOp->minimalSignature(), u"operator+(B,A)");
+ QVERIFY(reverseOp);
+ QVERIFY(reverseOp->isReverseOperator());
+ QCOMPARE(reverseOp->arguments().size(), 1);
+ QCOMPARE(reverseOp->minimalSignature(), u"operator+(A,B)");
+}
+
+void TestReverseOperators::testSpaceshipOperator()
+{
+ const char cppCode[] = R"(
+ class Test {
+ public:
+ explicit Test(int v);
+ int operator<=>(const Test &rhs) const = default;
+ };)";
+ const char xmlCode[] = R"(
+ <typesystem package="Foo">
+ <value-type name='Test'/>
+ </typesystem>)";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false,
+ {}, {}, LanguageLevel::Cpp20));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+ const auto testClass = AbstractMetaClass::findClass(classes, "Test");
+ QVERIFY(testClass);
+ const auto &functions = testClass->functions();
+ // 6 operators should be synthesized
+ const auto count = std::count_if(functions.cbegin(), functions.cend(),
+ [](const AbstractMetaFunctionCPtr &f) {
+ return f->isComparisonOperator();
+ });
+ QCOMPARE(count, 6);
+}
+
+QTEST_APPLESS_MAIN(TestReverseOperators)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h
new file mode 100644
index 000000000..fb8d97c97
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTREVERSEOPERATORS_H
+#define TESTREVERSEOPERATORS_H
+#include <QtCore/QObject>
+
+class TestReverseOperators : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testReverseSum();
+ void testReverseSumWithAmbiguity();
+ void testSpaceshipOperator();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp
new file mode 100644
index 000000000..ea37c6255
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp
@@ -0,0 +1,628 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testtemplates.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <complextypeentry.h>
+#include <containertypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QTextStream>
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestTemplates::testTemplateWithNamespace()
+{
+ const char cppCode[] = R"CPP(
+template<typename T> struct QList {};
+struct Url {
+ void name();
+};
+namespace Internet {
+ struct Url{};
+ struct Bookmarks {
+ QList<Url> list();
+ };
+};
+)CPP";
+
+ const char xmlCode0[] = R"XML(
+<typesystem package='Package.Network'>
+ <value-type name='Url'/>
+</typesystem>)XML";
+
+ QTemporaryFile file;
+ QVERIFY(file.open());
+ file.write(xmlCode0);
+ file.close();
+
+ QString xmlCode1 = QString::fromLatin1(R"XML(
+<typesystem package='Package.Internet'>
+ <load-typesystem name='%1' generate='no'/>
+ <container-type name='QList' type='list'/>
+ <namespace-type name='Internet' generate='no'>
+ <value-type name='Url'/>
+ <value-type name='Bookmarks'/>
+ </namespace-type>
+</typesystem>)XML").arg(file.fileName());
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, qPrintable(xmlCode1), false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto classB = AbstractMetaClass::findClass(classes, "Bookmarks");
+ QVERIFY(classB);
+ const auto func = classB->findFunction("list");
+ QVERIFY(func);
+ AbstractMetaType funcType = func->type();
+ QVERIFY(!funcType.isVoid());
+ QCOMPARE(funcType.cppSignature(), u"QList<Internet::Url>");
+}
+
+void TestTemplates::testTemplateOnContainers()
+{
+ const char cppCode[] = R"CPP(
+struct Base {};
+template<typename T> struct QList {};
+namespace Namespace {
+ enum SomeEnum { E1, E2 };
+ template<SomeEnum type> struct A {
+ A<type> foo(const QList<A<type> >& a);
+ };
+ typedef A<E1> B;
+}
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package="Package">
+ <container-type name='QList' type='list'/>
+ <namespace-type name='Namespace'>
+ <enum-type name='SomeEnum'/>
+ <object-type name='A' generate='no'/>
+ <object-type name='B'/>
+ </namespace-type>
+ <object-type name='Base'/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ QVERIFY(!classB->baseClass());
+ QVERIFY(classB->baseClassName().isEmpty());
+ const auto func = classB->findFunction("foo");
+ QVERIFY(func);
+ AbstractMetaType argType = func->arguments().constFirst().type();
+ QCOMPARE(argType.instantiations().size(), 1);
+ QCOMPARE(argType.typeEntry()->qualifiedCppName(), u"QList");
+
+ const AbstractMetaType &instance1 = argType.instantiations().constFirst();
+ QCOMPARE(instance1.instantiations().size(), 1);
+ QCOMPARE(instance1.typeEntry()->qualifiedCppName(), u"Namespace::A");
+
+ const AbstractMetaType &instance2 = instance1.instantiations().constFirst();
+ QCOMPARE(instance2.instantiations().size(), 0);
+ QCOMPARE(instance2.typeEntry()->qualifiedCppName(), u"Namespace::E1");
+}
+
+void TestTemplates::testTemplateValueAsArgument()
+{
+ const char cppCode[] = R"CPP(
+template<typename T> struct List {};
+void func(List<int> arg) {}
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Package'>
+ <primitive-type name='int'/>
+ <container-type name='List' type='list'/>
+ <function signature='func(List&lt;int&gt;)'/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ const auto globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 1);
+
+ const auto func = globalFuncs.constFirst();
+ QCOMPARE(func->minimalSignature(), u"func(List<int>)");
+ QCOMPARE(func->arguments().constFirst().type().cppSignature(),
+ u"List<int>");
+}
+
+void TestTemplates::testTemplatePointerAsArgument()
+{
+ const char cppCode[] = R"CPP(
+template<typename T> struct List {};
+void func(List<int>* arg) {}
+)CPP";
+
+ const char xmlCode[] = R"XML(
+ <typesystem package='Package'>
+ <primitive-type name='int'/>
+ <container-type name='List' type='list'/>
+ <function signature='func(List&lt;int&gt;*)'/>
+ </typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaFunctionCList globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 1);
+
+ const auto func = globalFuncs.constFirst();
+ QCOMPARE(func->minimalSignature(), u"func(List<int>*)");
+ QCOMPARE(func->arguments().constFirst().type().cppSignature(),
+ u"List<int> *");
+}
+
+void TestTemplates::testTemplateReferenceAsArgument()
+{
+ const char cppCode[] = R"CPP(
+template<typename T> struct List {};
+void func(List<int>& arg) {}
+ )CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Package'>
+ <primitive-type name='int'/>
+ <container-type name='List' type='list'/>
+ <function signature='func(List&lt;int&gt;&amp;)'/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ const auto globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 1);
+
+ const auto func = globalFuncs.constFirst();
+ QCOMPARE(func->minimalSignature(), u"func(List<int>&)");
+ QCOMPARE(func->arguments().constFirst().type().cppSignature(),
+ u"List<int> &");
+}
+
+void TestTemplates::testTemplateParameterFixup()
+{
+ const char cppCode[] = R"CPP(
+template<typename T>
+struct List {
+ struct Iterator {};
+ void append(List l);
+ void erase(List::Iterator it);
+};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+ <typesystem package='Package'>
+ <container-type name='List' type='list'>
+ <value-type name='Iterator'/>
+ </container-type>
+ </typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ const AbstractMetaClassList templates = builder->templates();
+
+ QCOMPARE(templates.size(), 1);
+ AbstractMetaClassCPtr list = templates.constFirst();
+ // Verify that the parameter of "void append(List l)" gets fixed to "List<T>"
+ const auto append = list->findFunction("append");
+ QVERIFY(append);
+ QCOMPARE(append->arguments().size(), 1);
+ QCOMPARE(append->arguments().at(0).type().cppSignature(), u"List<T>");
+ // Verify that the parameter of "void erase(Iterator)" is not modified
+ const auto erase = list->findFunction("erase");
+ QVERIFY(erase);
+ QCOMPARE(erase->arguments().size(), 1);
+ QCOMPARE(erase->arguments().at(0).type().cppSignature(), u"List::Iterator");
+}
+
+void TestTemplates::testInheritanceFromContainterTemplate()
+{
+ const char cppCode[] = R"CPP(
+template<typename T>
+struct ListContainer {
+ inline void push_front(const T& t);
+ inline T& front();
+};
+struct FooBar {};
+struct FooBars : public ListContainer<FooBar> {};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Package'>
+ <container-type name='ListContainer' type='list'/>
+ <value-type name='FooBar'/>
+ <value-type name='FooBars'>
+ <modify-function signature='push_front(FooBar)' remove='all'/>
+ <modify-function signature='front()' remove='all'/>
+ </value-type>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ AbstractMetaClassList templates = builder->templates();
+ QCOMPARE(classes.size(), 2);
+ QCOMPARE(templates.size(), 1);
+
+ const auto foobars = AbstractMetaClass::findClass(classes, "FooBars");
+ QCOMPARE(foobars->functions().size(), 4);
+
+ AbstractMetaClassCPtr lc = templates.constFirst();
+ QCOMPARE(lc->functions().size(), 2);
+}
+
+void TestTemplates::testTemplateInheritanceMixedWithForwardDeclaration()
+{
+ const char cppCode[] = R"CPP(
+enum SomeEnum { E1, E2 };
+template<SomeEnum type> struct Future;
+template<SomeEnum type>
+struct A {
+ A();
+ void method();
+ friend struct Future<type>;
+};
+typedef A<E1> B;
+template<SomeEnum type> struct Future {};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Package'>
+ <enum-type name='SomeEnum'/>
+ <value-type name='A' generate='no'/>
+ <value-type name='B'/>
+ <value-type name='Future' generate='no'/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ QVERIFY(!classB->baseClass());
+ QVERIFY(classB->baseClassName().isEmpty());
+ // 3 functions: simple constructor, copy constructor and "method()".
+ QCOMPARE(classB->functions().size(), 3);
+}
+
+void TestTemplates::testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration()
+{
+ const char cppCode[] = R"CPP(
+namespace Namespace {
+enum SomeEnum { E1, E2 };
+template<SomeEnum type> struct Future;
+template<SomeEnum type>
+struct A {
+ A();
+ void method();
+ friend struct Future<type>;
+};
+typedef A<E1> B;
+template<SomeEnum type> struct Future {};
+};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Package'>
+ <namespace-type name='Namespace'>
+ <enum-type name='SomeEnum'/>
+ <value-type name='A' generate='no'/>
+ <value-type name='B'/>
+ <value-type name='Future' generate='no'/>
+ </namespace-type>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto classB = AbstractMetaClass::findClass(classes, "Namespace::B");
+ QVERIFY(classB);
+ QVERIFY(!classB->baseClass());
+ QVERIFY(classB->baseClassName().isEmpty());
+ // 3 functions: simple constructor, copy constructor and "method()".
+ QCOMPARE(classB->functions().size(), 3);
+}
+
+void TestTemplates::testTypedefOfInstantiationOfTemplateClass()
+{
+ const char cppCode[] = R"CPP(
+namespace NSpace {
+enum ClassType {
+ TypeOne
+};
+template<ClassType CLASS_TYPE>
+struct BaseTemplateClass {
+ inline ClassType getClassType() const { return CLASS_TYPE; }
+};
+typedef BaseTemplateClass<TypeOne> TypeOneClass;
+}
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Package'>
+ <namespace-type name='NSpace'>
+ <enum-type name='ClassType'/>
+ <object-type name='BaseTemplateClass' generate='no'/>
+ <object-type name='TypeOneClass'/>
+ </namespace-type>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 3);
+
+ const auto base = AbstractMetaClass::findClass(classes, "BaseTemplateClass");
+ QVERIFY(base);
+ const auto one = AbstractMetaClass::findClass(classes, "TypeOneClass");
+ QVERIFY(one);
+ QCOMPARE(one->templateBaseClass(), base);
+ QCOMPARE(one->functions().size(), base->functions().size());
+ QVERIFY(one->isTypeDef());
+ auto oneType = one->typeEntry();
+ auto baseType = base->typeEntry();
+ QCOMPARE(oneType->baseContainerType(), baseType);
+ QCOMPARE(one->baseClassNames(), QStringList(u"NSpace::BaseTemplateClass<NSpace::TypeOne>"_s));
+
+ QVERIFY(one->hasTemplateBaseClassInstantiations());
+ AbstractMetaTypeList instantiations = one->templateBaseClassInstantiations();
+ QCOMPARE(instantiations.size(), 1);
+ const AbstractMetaType &inst = instantiations.constFirst();
+ QVERIFY(!inst.isEnum());
+ QVERIFY(!inst.typeEntry()->isEnum());
+ QVERIFY(inst.typeEntry()->isEnumValue());
+ QCOMPARE(inst.cppSignature(), u"NSpace::TypeOne");
+}
+
+void TestTemplates::testContainerTypeIncompleteArgument()
+{
+ const char cppCode[] = R"CPP(
+template<typename T>
+class Vector {
+ void method(const Vector& vector);
+ Vector otherMethod();
+};
+template <typename T>
+void Vector<T>::method(const Vector<T>& vector) {}
+template <typename T>
+Vector<T> Vector<T>::otherMethod() { return Vector<T>(); }
+typedef Vector<int> IntVector;
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <container-type name='Vector' type='vector'/>
+ <value-type name='IntVector'/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ QCOMPARE(classes.size(), 1);
+
+ const auto vector = AbstractMetaClass::findClass(classes, "IntVector");
+ QVERIFY(vector);
+ auto baseContainer = vector->typeEntry()->baseContainerType();
+ QVERIFY(baseContainer);
+ QCOMPARE(reinterpret_cast<const ContainerTypeEntry*>(baseContainer.get())->containerKind(),
+ ContainerTypeEntry::ListContainer);
+ QCOMPARE(vector->functions().size(), 4);
+
+ const auto method = vector->findFunction("method");
+ QVERIFY(method);
+ QCOMPARE(method->signature(), u"method(const Vector<int> & vector)");
+
+ const auto otherMethod = vector->findFunction("otherMethod");
+ QVERIFY(otherMethod);
+ QCOMPARE(otherMethod->signature(), u"otherMethod()");
+ QVERIFY(!otherMethod->type().isVoid());
+ QCOMPARE(otherMethod->type().cppSignature(), u"Vector<int>");
+}
+
+void TestTemplates::testNonTypeTemplates()
+{
+ // PYSIDe-1296, functions with non type templates parameters.
+ const char cppCode[] = R"CPP(
+template <class T, int Size>
+class Array {
+ T array[Size];
+};
+
+Array<int, 2> foo();
+
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <container-type name='Array' type='vector'/>
+ <function signature="foo()"/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true));
+ QVERIFY(builder);
+ auto functions = builder->globalFunctions();
+ QCOMPARE(functions.size(), 1);
+ auto foo = functions.constFirst();
+ QCOMPARE(foo->name(), u"foo");
+ QCOMPARE(foo->type().name(), u"Array");
+}
+
+// Perform checks on template inheritance; a typedef of a template class
+// should result in rewritten types.
+void TestTemplates::testTemplateTypeDefs_data()
+{
+ QTest::addColumn<QString>("cpp");
+ QTest::addColumn<QString>("xml");
+
+ const char optionalClassDef[] = R"CPP(
+template<class T> // Some value type similar to std::optional
+class Optional {
+public:
+ T value() const { return m_value; }
+ operator bool() const { return m_success; }
+
+ T m_value;
+ bool m_success = false;
+};
+)CPP";
+
+ const char xmlPrefix[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <primitive-type name='bool'/>
+)XML";
+
+ const char xmlOptionalDecl[] = "<value-type name='Optional' generate='no'/>\n";
+ const char xmlOptionalIntDecl[] = "<value-type name='IntOptional'/>\n";
+ const char xmlPostFix[] = "</typesystem>\n";
+
+ // Flat, global namespace
+ QString cpp;
+ QTextStream(&cpp) << optionalClassDef
+ << "typedef Optional<int> IntOptional;\n";
+ QString xml;
+ QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl
+ << "<typedef-type name='XmlIntOptional' source='Optional&lt;int&gt;'/>"
+ << xmlPostFix;
+ QTest::newRow("global-namespace")
+ << cpp << xml;
+
+ // Typedef from namespace Std
+ cpp.clear();
+ QTextStream(&cpp) << "namespace Std {\n" << optionalClassDef << "}\n"
+ << "typedef Std::Optional<int> IntOptional;\n";
+ xml.clear();
+ QTextStream(&xml) << xmlPrefix
+ << "<namespace-type name='Std'>\n" << xmlOptionalDecl
+ << "</namespace-type>\n" << xmlOptionalIntDecl
+ << "<typedef-type name='XmlIntOptional' source='Std::Optional&lt;int&gt;'/>"
+ << xmlPostFix;
+ QTest::newRow("namespace-Std")
+ << cpp << xml;
+
+ // Typedef from nested class
+ cpp.clear();
+ QTextStream(&cpp) << "class Outer {\npublic:\n" << optionalClassDef << "\n};\n"
+ << "typedef Outer::Optional<int> IntOptional;\n";
+ xml.clear();
+ QTextStream(&xml) << xmlPrefix
+ << "<object-type name='Outer'>\n" << xmlOptionalDecl
+ << "</object-type>\n" << xmlOptionalIntDecl
+ << "<typedef-type name='XmlIntOptional' source='Outer::Optional&lt;int&gt;'/>"
+ << xmlPostFix;
+ QTest::newRow("nested-class")
+ << cpp << xml;
+}
+
+void TestTemplates::testTemplateTypeDefs()
+{
+ QFETCH(QString, cpp);
+ QFETCH(QString, xml);
+
+ const QByteArray cppBa = cpp.toLocal8Bit();
+ const QByteArray xmlBa = xml.toLocal8Bit();
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppBa.constData(), xmlBa.constData(), true));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto optional = AbstractMetaClass::findClass(classes, "Optional");
+ QVERIFY(optional);
+
+ // Find the typedef'ed class
+ const auto optionalInt = AbstractMetaClass::findClass(classes, "IntOptional");
+ QVERIFY(optionalInt);
+ QCOMPARE(optionalInt->templateBaseClass(), optional);
+
+ // Find the class typedef'ed in the typesystem XML
+ const auto xmlOptionalInt = AbstractMetaClass::findClass(classes, "XmlIntOptional");
+ QVERIFY(xmlOptionalInt);
+ QCOMPARE(xmlOptionalInt->templateBaseClass(), optional);
+
+ // Check whether the value() method now has an 'int' return
+ const auto valueMethod = optionalInt->findFunction("value");
+ QVERIFY(valueMethod);
+ QCOMPARE(valueMethod->type().cppSignature(), u"int");
+
+ // ditto for typesystem XML
+ const auto xmlValueMethod = xmlOptionalInt->findFunction("value");
+ QVERIFY(xmlValueMethod);
+ QCOMPARE(xmlValueMethod->type().cppSignature(), u"int");
+
+ // Check whether the m_value field is of type 'int'
+ const auto valueField = optionalInt->findField(u"m_value");
+ QVERIFY(valueField.has_value());
+ QCOMPARE(valueField->type().cppSignature(), u"int");
+
+ // ditto for typesystem XML
+ const auto xmlValueField =
+ xmlOptionalInt->findField(u"m_value");
+ QVERIFY(xmlValueField.has_value());
+ QCOMPARE(xmlValueField->type().cppSignature(), u"int");
+}
+
+void TestTemplates::testTemplateTypeAliases()
+{
+ // Model Qt 6's "template<typename T> using QList = QVector<T>"
+ const char cppCode[] = R"CPP(
+template<typename T>
+class Container1 { };
+
+template<typename T>
+using Container2 = Container1<T>;
+
+class Test
+{
+public:
+ Container2<int> m_intContainer;
+};
+
+class Derived : public Container2<int>
+{
+public:
+};
+)CPP";
+
+ const char xmlCode[] = R"XML(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <value-type name='Container1'/>
+ <value-type name='Derived'/>
+ <object-type name='Test'/>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+ const auto testClass = AbstractMetaClass::findClass(classes, "Test");
+ QVERIFY(testClass);
+
+ auto fields = testClass->fields();
+ QCOMPARE(fields.size(), 1);
+ auto fieldType = testClass->fields().at(0).type();
+ QCOMPARE(fieldType.name(), u"Container1");
+ QCOMPARE(fieldType.instantiations().size(), 1);
+
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
+ QVERIFY(derived);
+ auto base = derived->templateBaseClass();
+ QVERIFY(base);
+ QCOMPARE(base->name(), u"Container1");
+}
+
+QTEST_APPLESS_MAIN(TestTemplates)
diff --git a/sources/shiboken6/ApiExtractor/tests/testtemplates.h b/sources/shiboken6/ApiExtractor/tests/testtemplates.h
new file mode 100644
index 000000000..36800f723
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testtemplates.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTTEMPLATES_H
+#define TESTTEMPLATES_H
+
+#include <QtCore/QObject>
+
+class TestTemplates : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testTemplateOnContainers();
+ void testTemplateWithNamespace();
+ void testTemplateValueAsArgument();
+ void testTemplatePointerAsArgument();
+ void testTemplateReferenceAsArgument();
+ void testTemplateParameterFixup();
+ void testInheritanceFromContainterTemplate();
+ void testTemplateInheritanceMixedWithForwardDeclaration();
+ void testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration();
+ void testTypedefOfInstantiationOfTemplateClass();
+ void testContainerTypeIncompleteArgument();
+ void testNonTypeTemplates();
+ void testTemplateTypeDefs_data();
+ void testTemplateTypeDefs();
+ void testTemplateTypeAliases();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testtoposort.cpp b/sources/shiboken6/ApiExtractor/tests/testtoposort.cpp
new file mode 100644
index 000000000..50cefcfe9
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testtoposort.cpp
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testtoposort.h"
+#include "graph.h"
+
+#include <QtTest/QTest>
+#include <QtCore/QDebug>
+
+using IntGraph = Graph<int>;
+
+Q_DECLARE_METATYPE(IntGraph)
+
+using IntList = QList<int>;
+
+void TestTopoSort::testTopoSort_data()
+{
+ QTest::addColumn<IntGraph>("graph");
+ QTest::addColumn<bool>("expectedValid");
+ QTest::addColumn<IntList>("expectedOrder");
+
+ const int nodes1[] = {0, 1, 2};
+ IntGraph g(std::begin(nodes1), std::end(nodes1));
+ g.addEdge(1, 2);
+ g.addEdge(0, 1);
+ IntList expected = {0, 1, 2};
+ QTest::newRow("DAG") << g << true << expected;
+
+ const int nodes2[] = {0, 1};
+ g.clear();
+ g.setNodes(std::begin(nodes2), std::end(nodes2));
+ expected = {1, 0};
+ QTest::newRow("No edges") << g << true << expected;
+
+ g.clear();
+ g.setNodes(std::begin(nodes1), std::end(nodes1));
+ g.addEdge(0, 1);
+ g.addEdge(1, 2);
+ g.addEdge(2, 0);
+ expected.clear();
+ QTest::newRow("Cyclic") << g << false << expected;
+}
+
+void TestTopoSort::testTopoSort()
+{
+ QFETCH(IntGraph, graph);
+ QFETCH(bool, expectedValid);
+ QFETCH(IntList, expectedOrder);
+
+ const auto result = graph.topologicalSort();
+ QCOMPARE(result.isValid(), expectedValid);
+ if (expectedValid) {
+ QCOMPARE(result.result, expectedOrder);
+ QVERIFY(result.cyclic.isEmpty());
+ } else {
+ QVERIFY(!result.cyclic.isEmpty());
+ }
+}
+
+QTEST_APPLESS_MAIN(TestTopoSort)
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testtoposort.h b/sources/shiboken6/ApiExtractor/tests/testtoposort.h
new file mode 100644
index 000000000..4271d6a0e
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testtoposort.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTTOPOSORT_H
+#define TESTTOPOSORT_H
+
+#include <QtCore/QObject>
+
+class TestTopoSort : public QObject
+{
+Q_OBJECT
+private slots:
+ void testTopoSort_data();
+ void testTopoSort();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp b/sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp
new file mode 100644
index 000000000..72dae8cc5
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp
@@ -0,0 +1,92 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testtyperevision.h"
+#include "testutil.h"
+#include <abstractmetaenum.h>
+#include <abstractmetalang.h>
+#include <complextypeentry.h>
+#include <enumtypeentry.h>
+#include <flagstypeentry.h>
+#include <typedatabase.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+void TestTypeRevision::testRevisionAttr()
+{
+ const char cppCode[] = "class Rev_0 {};"
+ "class Rev_1 {};"
+ "class Rev_2 { public: enum Rev_3 { X }; enum Rev_5 { Y }; };";
+ const char xmlCode[] = "<typesystem package=\"Foo\">"
+ "<value-type name=\"Rev_0\"/>"
+ "<value-type name=\"Rev_1\" revision=\"1\"/>"
+ "<object-type name=\"Rev_2\" revision=\"2\">"
+ " <enum-type name=\"Rev_3\" revision=\"3\" flags=\"Flag_4\" flags-revision=\"4\" />"
+ " <enum-type name=\"Rev_5\" revision=\"5\" flags=\"Flag_5\" />"
+ "</object-type>"
+ "</typesystem>";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto rev0 = AbstractMetaClass::findClass(classes, "Rev_0");
+ QCOMPARE(rev0->typeEntry()->revision(), 0);
+
+ const auto rev1 = AbstractMetaClass::findClass(classes, "Rev_1");
+ QCOMPARE(rev1->typeEntry()->revision(), 1);
+
+ const auto rev2 = AbstractMetaClass::findClass(classes, "Rev_2");
+ QCOMPARE(rev2->typeEntry()->revision(), 2);
+
+ auto rev3 = rev2->findEnum(u"Rev_3"_s);
+ QVERIFY(rev3.has_value());
+ QCOMPARE(rev3->typeEntry()->revision(), 3);
+ auto rev4 = rev3->typeEntry()->flags();
+ QCOMPARE(rev4->revision(), 4);
+ auto rev5 = rev2->findEnum(u"Rev_5"_s);
+ QVERIFY(rev5.has_value());
+ EnumTypeEntryCPtr revEnumTypeEntry = rev5->typeEntry();
+ QCOMPARE(revEnumTypeEntry->revision(), 5);
+ QCOMPARE(revEnumTypeEntry->flags()->revision(), 5);
+}
+
+
+void TestTypeRevision::testVersion_data()
+{
+ QTest::addColumn<QString>("version");
+ QTest::addColumn<int>("expectedClassCount");
+
+ QTest::newRow("none") << QString() << 2;
+ QTest::newRow("1.0") << QString::fromLatin1("1.0") << 1; // Bar20 excluded
+ QTest::newRow("2.0") << QString::fromLatin1("2.0") << 2;
+ QTest::newRow("3.0") << QString::fromLatin1("3.0") << 1; // Bar excluded by "until"
+}
+
+void TestTypeRevision::testVersion()
+{
+ QFETCH(QString, version);
+ QFETCH(int, expectedClassCount);
+
+ const char cppCode[] = R"CPP(
+class Bar {};
+class Bar20 {};
+)CPP";
+ const char xmlCode[] = R"XML(
+<typesystem package="Foo">
+ <value-type name="Bar" until="2.0"/>
+ <value-type name="Bar20" since="2.0"/>
+</typesystem>
+)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, version));
+ QVERIFY(builder);
+
+ QCOMPARE(builder->classes().size(), expectedClassCount);
+}
+
+QTEST_APPLESS_MAIN(TestTypeRevision)
+
+
diff --git a/sources/shiboken6/ApiExtractor/tests/testtyperevision.h b/sources/shiboken6/ApiExtractor/tests/testtyperevision.h
new file mode 100644
index 000000000..84af839d2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testtyperevision.h
@@ -0,0 +1,19 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTTYPEREVISION_H
+#define TESTTYPEREVISION_H
+
+#include <QtCore/QObject>
+
+class TestTypeRevision : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testRevisionAttr();
+ void testVersion_data();
+ void testVersion();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testutil.h b/sources/shiboken6/ApiExtractor/tests/testutil.h
new file mode 100644
index 000000000..dc4e3b2da
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testutil.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTUTIL_H
+#define TESTUTIL_H
+#include <QtCore/QBuffer>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QTemporaryFile>
+#include "abstractmetabuilder.h"
+#include "reporthandler.h"
+#include "typedatabase.h"
+
+#include <exception>
+#include <memory>
+
+namespace TestUtil
+{
+ static AbstractMetaBuilder *parse(const char *cppCode, const char *xmlCode,
+ bool silent = true,
+ const QString &apiVersion = {},
+ const QStringList &dropTypeEntries = {},
+ LanguageLevel languageLevel = LanguageLevel::Default)
+ {
+ ReportHandler::setSilent(silent);
+ ReportHandler::startTimer();
+ auto *td = TypeDatabase::instance(true);
+ if (apiVersion.isEmpty())
+ TypeDatabase::clearApiVersions();
+ else if (!TypeDatabase::setApiVersion(QLatin1StringView("*"), apiVersion))
+ return nullptr;
+ td->setDropTypeEntries(dropTypeEntries);
+ QBuffer buffer;
+ // parse typesystem
+ buffer.setData(xmlCode);
+ if (!buffer.open(QIODevice::ReadOnly))
+ return nullptr;
+ if (!td->parseFile(&buffer))
+ return nullptr;
+ buffer.close();
+ // parse C++ code
+ QTemporaryFile tempSource(QDir::tempPath() + QLatin1StringView("/st_XXXXXX_main.cpp"));
+ if (!tempSource.open()) {
+ qWarning().noquote().nospace() << "Creation of temporary file failed: "
+ << tempSource.errorString();
+ return nullptr;
+ }
+ QByteArrayList arguments;
+ arguments.append(QFile::encodeName(tempSource.fileName()));
+ tempSource.write(cppCode, qint64(strlen(cppCode)));
+ tempSource.close();
+
+ auto builder = std::make_unique<AbstractMetaBuilder>();
+ try {
+ if (!builder->build(arguments, {}, true, languageLevel))
+ return nullptr;
+ } catch (const std::exception &e) {
+ qWarning("%s", e.what());
+ return nullptr;
+ }
+ return builder.release();
+ }
+} // namespace TestUtil
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp
new file mode 100644
index 000000000..98e30eac2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp
@@ -0,0 +1,39 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testvaluetypedefaultctortag.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetalang.h>
+#include <complextypeentry.h>
+
+void TestValueTypeDefaultCtorTag::testValueTypeDefaultCtorTagArgument()
+{
+ const char cppCode[] = "\n\
+ struct A {\n\
+ A(int,int);\n\
+ };\n\
+ struct B {};\n\
+ ";
+ const char xmlCode[] = "\n\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int' />\n\
+ <value-type name='A' default-constructor='A(0, 0)' />\n\
+ <value-type name='B' />\n\
+ </typesystem>";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+
+ AbstractMetaClassList classes = builder->classes();
+
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QVERIFY(classA->typeEntry()->hasDefaultConstructor());
+ QCOMPARE(classA->typeEntry()->defaultConstructor(), u"A(0, 0)");
+
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ QVERIFY(classB);
+ QVERIFY(!classB->typeEntry()->hasDefaultConstructor());
+}
+
+QTEST_APPLESS_MAIN(TestValueTypeDefaultCtorTag)
diff --git a/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h
new file mode 100644
index 000000000..192c07c1d
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTVALUETYPEDEFAULTCTORTAG_H
+#define TESTVALUETYPEDEFAULTCTORTAG_H
+
+#include <QtCore/QObject>
+
+class TestValueTypeDefaultCtorTag : public QObject
+{
+ Q_OBJECT
+ private slots:
+ void testValueTypeDefaultCtorTagArgument();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp b/sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp
new file mode 100644
index 000000000..a600181a5
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp
@@ -0,0 +1,67 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testvoidarg.h"
+#include <QtTest/QTest>
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <typesystem.h>
+
+void TestVoidArg::testVoidParsedFunction()
+{
+ const char cppCode[] = "struct A { void a(void); };";
+ const char xmlCode[] = "\n\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A'/>\n\
+ </typesystem>";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("a");
+ QVERIFY(addedFunc);
+ QCOMPARE(addedFunc->arguments().size(), 0);
+}
+
+void TestVoidArg::testVoidAddedFunction()
+{
+ const char cppCode[] = "struct A { };";
+ const char xmlCode[] = "\n\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A' >\n\
+ <add-function signature=\"a(void)\"/>\n\
+ </value-type>\n\
+ </typesystem>";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("a");
+ QVERIFY(addedFunc);
+ QCOMPARE(addedFunc->arguments().size(), 0);
+
+}
+
+void TestVoidArg::testVoidPointerParsedFunction()
+{
+ const char cppCode[] = "struct A { void a(void*); };";
+ const char xmlCode[] = "\n\
+ <typesystem package=\"Foo\">\n\
+ <value-type name='A' />\n\
+ </typesystem>";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("a");
+ QVERIFY(addedFunc);
+ QCOMPARE(addedFunc->arguments().size(), 1);
+
+}
+
+QTEST_APPLESS_MAIN(TestVoidArg)
diff --git a/sources/shiboken6/ApiExtractor/tests/testvoidarg.h b/sources/shiboken6/ApiExtractor/tests/testvoidarg.h
new file mode 100644
index 000000000..191b9cfb2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testvoidarg.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TESTVOIDARG_H
+#define TESTVOIDARG_H
+#include <QtCore/QObject>
+
+class TestVoidArg : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testVoidParsedFunction();
+ void testVoidPointerParsedFunction();
+ void testVoidAddedFunction();
+};
+
+#endif
diff --git a/sources/shiboken6/ApiExtractor/tests/utf8code.txt b/sources/shiboken6/ApiExtractor/tests/utf8code.txt
new file mode 100644
index 000000000..6d5fa9dcf
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/utf8code.txt
@@ -0,0 +1 @@
+áéíóú \ No newline at end of file
diff --git a/sources/shiboken6/ApiExtractor/textstream.cpp b/sources/shiboken6/ApiExtractor/textstream.cpp
new file mode 100644
index 000000000..83d981b2b
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/textstream.cpp
@@ -0,0 +1,263 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "textstream.h"
+
+#include <cstring>
+
+TextStream::TextStream(QIODevice *device, Language l) :
+ m_str(device), m_language(l)
+{
+}
+
+TextStream::TextStream(QString *string, Language l) :
+ m_str(string), m_language(l)
+{
+}
+
+TextStream::TextStream(QByteArray *array, Language l) :
+ m_str(array), m_language(l)
+{
+}
+
+TextStream::~TextStream() = default;
+
+QChar TextStream::lastChar() const
+{
+ auto s = m_str.string();
+ return s != nullptr && !s->isEmpty() ? *(s->crbegin()) : QChar();
+}
+
+void TextStream::setIndentation(int i)
+{
+ Q_ASSERT(i >= 0);
+ m_indentation = i;
+}
+
+void TextStream::outdent(int n)
+{
+ m_indentation -= n;
+ Q_ASSERT(m_indentation >= 0);
+}
+
+qint64 TextStream::pos() const
+{
+ // QTextStream::pos() only works for QIODevice, be a bit smarter
+ if (auto s = m_str.string())
+ return s->size();
+ // QIODevices need to flushed to tell the correct position.
+ const_cast<QTextStream &>(m_str).flush();
+ return m_str.pos();
+}
+
+void TextStream::setString(QString *string, QIODeviceBase::OpenMode openMode)
+{
+ m_str.setString(string, openMode);
+ m_rstFormattingEnd = false;
+}
+
+void TextStream::putRepetitiveChars(char c, int count)
+{
+ if (count > 0) {
+ for (int i = 0; i < count; ++i) {
+ const int ofw = m_str.fieldWidth();
+ m_str.setFieldWidth(0);
+ m_str << c;
+ m_str.setFieldWidth(ofw);
+ }
+ }
+}
+
+void TextStream::_setRstFormattingEnd()
+{
+ m_rstFormattingEnd = true;
+}
+
+void TextStream::setLastCharClass(CharClass c)
+{
+ m_lastCharClass = c;
+}
+
+void TextStream::writeIndent()
+{
+ putRepetitiveChars(' ', m_indentation * m_tabWidth);
+}
+
+// Indent handling: If the last character was a new line
+// and the upcoming one is none, indent the stream
+// Special case for C++ : If the upcoming char is a '#', we don't
+// indent (preprocessor directive).
+
+template <class Char>
+static TextStream::CharClass charClassHelper(Char c)
+{
+ switch (c) {
+ case '\n':
+ return TextStream::CharClass::NewLine;
+ case '#':
+ return TextStream::CharClass::Hash;
+ case ' ':
+ case '\t':
+ return TextStream::CharClass::Space;
+ case '\\':
+ return TextStream::CharClass::BackSlash;
+ default:
+ break;
+ }
+ return TextStream::CharClass::Other;
+}
+
+static inline TextStream::CharClass charClass(char c)
+{ return charClassHelper(c); }
+
+static inline TextStream::CharClass charClass(QChar c)
+{ return charClassHelper(c.unicode()); }
+
+void TextStream::checkIndent(CharClass upComingCharClass)
+{
+ if (m_rstFormattingEnd) {
+ if (upComingCharClass != CharClass::Space && upComingCharClass != CharClass::NewLine
+ && upComingCharClass != CharClass::BackSlash) {
+ m_str << '\\';
+ }
+ m_rstFormattingEnd = false;
+ }
+ if (m_indentationEnabled && m_lastCharClass == CharClass::NewLine
+ && (upComingCharClass != CharClass::NewLine
+ && (m_language != Language::Cpp || upComingCharClass != CharClass::Hash))) {
+ writeIndent();
+ }
+ m_lastCharClass = upComingCharClass;
+}
+
+template <class Char>
+void TextStream::putCharHelper(Char c)
+{
+ const auto klass = charClass(c);
+ checkIndent(klass);
+ m_str << c;
+}
+
+void TextStream::putString(QStringView v)
+{
+ if (v.isEmpty())
+ return;
+ if (v.contains(u'\n')) {
+ for (auto c : v)
+ putCharHelper(c);
+ } else {
+ // If there is no newline, write as a blob. This is important to make
+ // field formatting (alignment/width) working, else each char will be
+ // considered a field.
+ const auto klass = charClass(*v.cbegin());
+ checkIndent(klass);
+ m_str << v;
+ m_lastCharClass = CharClass::Other;
+ }
+}
+
+void TextStream::putChar(QChar c)
+{
+ putCharHelper(c);
+}
+
+void TextStream::putString(const char *s)
+{
+ const char firstChar = *s;
+ if (firstChar == '\0')
+ return;
+ if (std::strchr(s, '\n') != nullptr) { // See above
+ for ( ; *s; ++s)
+ putCharHelper(*s);
+ } else {
+ checkIndent(charClass(firstChar));
+ m_str << s;
+ m_lastCharClass = CharClass::Other;
+ }
+}
+
+void TextStream::putChar(char c)
+{
+ putCharHelper(c);
+}
+
+void TextStream::putInt(int t)
+{
+ checkIndent(CharClass::Other);
+ m_str << t;
+}
+
+void TextStream::putSizeType(qsizetype t)
+{
+ checkIndent(CharClass::Other);
+ m_str << t;
+}
+
+StringStream::StringStream(Language l) : TextStream(&m_buffer, l)
+{
+}
+
+void StringStream::clear()
+{
+ m_buffer.clear();
+ setLastCharClass(CharClass::NewLine);
+}
+
+void indent(TextStream &s)
+{
+ s.indent();
+}
+
+void outdent(TextStream &s)
+{
+ s.outdent();
+}
+
+void enableIndent(TextStream &s)
+{
+ s.setIndentationEnabled(true);
+}
+
+void disableIndent(TextStream &s)
+{
+ s.setIndentationEnabled(false);
+}
+
+void ensureEndl(TextStream &s)
+{
+ if (s.lastChar() != u'\n')
+ s << '\n';
+}
+
+void rstBold(TextStream &s)
+{
+ s.putRawString("**");
+}
+
+void rstBoldOff(TextStream &s)
+{
+ s.putRawString("**");
+ s._setRstFormattingEnd();
+}
+
+void rstItalic(TextStream &s)
+{
+ s.putRawChar('*');
+}
+
+void rstItalicOff(TextStream &s)
+{
+ s.putRawChar('*');
+ s._setRstFormattingEnd();
+}
+
+void rstCode(TextStream &s)
+{
+ s.putRawString("``");
+}
+
+void rstCodeOff(TextStream &s)
+{
+ s.putRawString("``");
+ s._setRstFormattingEnd();
+}
diff --git a/sources/shiboken6/ApiExtractor/textstream.h b/sources/shiboken6/ApiExtractor/textstream.h
new file mode 100644
index 000000000..228f36405
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/textstream.h
@@ -0,0 +1,227 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TEXTSTREAM_H
+#define TEXTSTREAM_H
+
+#include <QtCore/QTextStream>
+#include <QtCore/QString>
+
+/// A text stream based on QTextStream with built-in indent.
+class TextStream
+{
+public:
+ Q_DISABLE_COPY_MOVE(TextStream)
+
+ using ManipulatorFunc = void(TextStream &);
+
+ enum class Language
+ {
+ None, Cpp
+ };
+
+ enum class CharClass
+ {
+ Other, NewLine, Space, Hash, BackSlash
+ };
+
+ explicit TextStream(QIODevice *device, Language l = Language::None);
+ explicit TextStream(QString *string, Language l = Language::None);
+ explicit TextStream(QByteArray *array, Language l = Language::None);
+ virtual ~TextStream();
+
+ Language language() const { return m_language; }
+ void setLanguage(Language language) { m_language = language; }
+
+ bool isIndentationEnabled() const { return m_indentationEnabled; }
+ void setIndentationEnabled(bool m)
+ { m_indentationEnabled = m; }
+
+ int tabWidth() const { return m_tabWidth; }
+ void setTabWidth(int tabWidth) { m_tabWidth = tabWidth; }
+
+ void setFieldWidth(int f) { m_str.setFieldWidth(f); }
+ int fieldWidth() const { return m_str.fieldWidth(); }
+
+ int indentation() const { return m_indentation; }
+ void setIndentation(int i);
+
+ void indent(int n = 1) { m_indentation += n; }
+ void outdent(int n = 1);
+
+ // QTextStream API
+ qint64 pos() const;
+ QTextStream::FieldAlignment fieldAlignment() const
+ { return m_str.fieldAlignment(); }
+ void setFieldAlignment(QTextStream::FieldAlignment al)
+ { m_str.setFieldAlignment(al); }
+ void setString(QString *string, QIODeviceBase::OpenMode openMode = QIODeviceBase::ReadWrite);
+ QString *string() const { return m_str.string(); }
+ void flush() { m_str.flush(); }
+ void setDevice(QIODevice *device) { m_str.setDevice(device); }
+ QIODevice *device() const { return m_str.device(); }
+ QTextStream &textStream() { return m_str; }
+
+ // Last character written, works only for streams on strings
+ QChar lastChar() const;
+
+ void putString(QStringView v);
+ void putChar(QChar c);
+ void putString(const char *s);
+ void putChar(char c);
+
+ void putInt(int t);
+ void putSizeType(qsizetype t);
+
+ void putRawString(const char *s) { m_str << s; }
+ void putRawChar(char c) { m_str << c; }
+
+ TextStream &operator<<(QStringView v) { putString(v); return *this; }
+ TextStream &operator<<(const QString &qs) { putString(QStringView{qs}); return *this; }
+ TextStream &operator<<(QLatin1StringView lv) { putString(lv.constData()); return *this; }
+ TextStream &operator<<(QUtf8StringView uv) { putString(uv.data()); return *this; }
+ TextStream &operator<<(const QByteArray &ba) { putString(ba.constData()); return *this; }
+ TextStream &operator<<(QChar c) { putChar(c); return *this; }
+ TextStream &operator<<(const char *s) { putString(s); return *this; }
+ TextStream &operator<<(char c) { putChar(c); return *this; }
+ TextStream &operator<<(int t) { putInt(t); return *this; }
+#if QT_POINTER_SIZE != 4
+ TextStream &operator<<(qsizetype t) { putSizeType(t); return *this; }
+#endif
+
+ inline TextStream &operator<<(const QTextStreamManipulator &m) { m_str << m; return *this; }
+ inline TextStream &operator<<(ManipulatorFunc f) { f(*this); return *this; }
+
+ void putRepetitiveChars(char c, int count);
+
+ void _setRstFormattingEnd();
+
+protected:
+ void setLastCharClass(CharClass c);
+
+private:
+ void writeIndent();
+ void checkIndent(CharClass upComingCharClass);
+ template <class Char>
+ void putCharHelper(Char c);
+
+ QTextStream m_str;
+ CharClass m_lastCharClass = CharClass::NewLine;
+ int m_tabWidth = 4;
+ int m_indentation = 0;
+ bool m_indentationEnabled = true;
+ bool m_rstFormattingEnd = false; // just past some **bla** where '\' needs to be enforced
+ Language m_language;
+};
+
+/// Stream into a string (cf std::ostringstream)
+class StringStream : public TextStream
+{
+public:
+ StringStream(Language l = Language::None);
+
+ qsizetype size() const { return m_buffer.size(); }
+ void clear();
+
+ const QString &toString() const { return m_buffer; }
+ operator const QString &() const { return m_buffer; }
+
+private:
+ QString m_buffer;
+};
+
+void indent(TextStream &s);
+void outdent(TextStream &s);
+void enableIndent(TextStream &s);
+void disableIndent(TextStream &s);
+// Works only for streams on strings
+void ensureEndl(TextStream &s);
+
+void rstBold(TextStream &s);
+void rstBoldOff(TextStream &s);
+void rstCode(TextStream &s);
+void rstCodeOff(TextStream &s);
+void rstItalic(TextStream &s);
+void rstItalicOff(TextStream &s);
+
+inline TextStream &operator<<(TextStream &str, QAnyStringView asv)
+{
+ asv.visit([&str](auto s) { str << s; });
+ return str;
+}
+
+/// Format an aligned field
+template <class T>
+class AlignedField
+{
+public:
+ explicit AlignedField(T value, int fieldWidth,
+ QTextStream::FieldAlignment a = QTextStream::AlignLeft) :
+ m_value(value), m_fieldWidth(fieldWidth), m_alignment(a)
+ {
+ }
+
+ void put(TextStream &s) const
+ {
+ const int oldFieldWidth = s.fieldWidth();
+ const auto oldFieldAlignment = s.fieldAlignment();
+ s.setFieldWidth(m_fieldWidth);
+ s.setFieldAlignment(m_alignment);
+ const auto oldPos = s.pos();
+ s << m_value;
+ // Ensure something is written when an empty string is encountered
+ if (oldPos == s.pos() && m_fieldWidth > 0)
+ s << ' ';
+ s.setFieldAlignment(oldFieldAlignment);
+ s.setFieldWidth(oldFieldWidth);
+ }
+
+private:
+ const T m_value;
+ const int m_fieldWidth;
+ const QTextStream::FieldAlignment m_alignment;
+};
+
+template <class T>
+TextStream &operator<<(TextStream &str, const AlignedField<T> &fa)
+{
+ fa.put(str);
+ return str;
+}
+
+class Pad
+{
+public:
+ explicit Pad(char c, int count) : m_char(c), m_count(count) {}
+
+ void write(TextStream &str) const
+ {
+ for (int i = 0; i < m_count; ++i)
+ str << m_char;
+ }
+
+private:
+ const char m_char;
+ const int m_count;
+};
+
+inline TextStream &operator<<(TextStream &str, const Pad &pad)
+{
+ pad.write(str);
+ return str;
+}
+
+class Indentation
+{
+public:
+ Q_DISABLE_COPY_MOVE(Indentation)
+
+ Indentation(TextStream &s, int n = 1) : m_s(s), m_n(n) { m_s.indent(m_n); }
+ ~Indentation() { m_s.outdent(m_n); }
+
+private:
+ TextStream &m_s;
+ const int m_n;
+};
+
+#endif // TEXTSTREAM_H
diff --git a/sources/shiboken6/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp
new file mode 100644
index 000000000..61fd22418
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp
@@ -0,0 +1,1661 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "typedatabase.h"
+#include "abstractmetatype.h"
+#include "addedfunction.h"
+#include "messages.h"
+#include "typesystemparser_p.h"
+#include "complextypeentry.h"
+#include "constantvaluetypeentry.h"
+#include "containertypeentry.h"
+#include "customtypenentry.h"
+#include "debughelpers_p.h"
+#include "exception.h"
+#include "flagstypeentry.h"
+#include "functiontypeentry.h"
+#include "namespacetypeentry.h"
+#include "objecttypeentry.h"
+#include "primitivetypeentry.h"
+#include "optionsparser.h"
+#include "pythontypeentry.h"
+#include "smartpointertypeentry.h"
+#include "typedefentry.h"
+#include "typesystemtypeentry.h"
+#include "varargstypeentry.h"
+#include "voidtypeentry.h"
+#include "conditionalstreamreader.h"
+#include "predefined_templates.h"
+#include "clangparser/compilersupport.h"
+#include "modifications.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QFile>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QList>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QVersionNumber>
+#include <QtCore/QXmlStreamReader>
+#include "reporthandler.h"
+
+#include <algorithm>
+#include <utility>
+
+using namespace Qt::StringLiterals;
+
+using TypeDatabaseParserContextPtr = std::shared_ptr<TypeDatabaseParserContext>;
+
+// package -> api-version
+
+static QString wildcardToRegExp(QString w)
+{
+ w.replace(u'?', u'.');
+ w.replace(u'*', ".*"_L1);
+ return w;
+}
+
+using ApiVersion = std::pair<QRegularExpression, QVersionNumber>;
+using ApiVersions = QList<ApiVersion>;
+
+Q_GLOBAL_STATIC(ApiVersions, apiVersions)
+
+struct PythonType
+{
+ QString name;
+ QString checkFunction;
+ TypeSystem::CPythonType type;
+};
+
+using PythonTypes = QList<PythonType>;
+
+static const PythonTypes &builtinPythonTypes()
+{
+ static const PythonTypes result{
+ // "Traditional" custom types
+ // numpy
+ {u"PyArrayObject"_s, u"PyArray_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyBuffer"_s, u"Shiboken::Buffer::checkType"_s, TypeSystem::CPythonType::Other},
+ {u"PyByteArray"_s, u"PyByteArray_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyBytes"_s, u"PyBytes_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyCallable"_s, u"PyCallable_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyDate"_s, u"PyDate_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyDateTime"_s, u"PyDateTime_Check_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyDict"_s, u"PyDict_Check"_s, TypeSystem::CPythonType::Other},
+ // Convenience macro in sbkconverter.h
+ {u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other},
+ // shiboken-specific
+ {u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other},
+ {u"PySequence"_s, u"Shiboken::String::checkIterableArgument"_s,
+ TypeSystem::CPythonType::Other},
+ {u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String},
+ {u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other},
+ {u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String},
+ // Types used as target lang API types for primitive types
+ {u"PyBool"_s, u"PyBool_Check"_s, TypeSystem::CPythonType::Bool},
+ {u"PyComplex"_s, u"PyComplex_Check"_s, TypeSystem::CPythonType::Other},
+ {u"PyLong"_s, u"PyLong_Check"_s, TypeSystem::CPythonType::Integer},
+ {u"PyFloat"_s, u"PyFloat_Check"_s, TypeSystem::CPythonType::Float},
+ // Single character strings to match C++ char types
+ {u"SbkChar"_s, u"SbkChar_Check"_s, TypeSystem::CPythonType::String}
+ };
+ return result;
+}
+
+struct SuppressedWarning
+{
+ QRegularExpression pattern;
+ QString rawText;
+ bool generate; // Current type system
+ mutable bool matched = false;
+};
+
+QList<OptionDescription> TypeDatabase::options()
+{
+ return {
+ {u"api-version=<\"package mask\">,<\"version\">"_s,
+ u"Specify the supported api version used to generate the bindings"_s},
+ {u"drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""_s,
+ u"Semicolon separated list of type system entries (classes, namespaces,\n"
+ "global functions and enums) to be dropped from generation."_s},
+ {u"-T<path>"_s, {} },
+ {u"typesystem-paths="_s + OptionsParser::pathSyntax(),
+ u"Paths used when searching for typesystems"_s},
+ {u"force-process-system-include-paths="_s + OptionsParser::pathSyntax(),
+ u"Include paths that are considered as system headers by the C++ parser, but should still "
+ "be processed to extract types (e.g. Qt include paths in a yocto sysroot)"_s},
+ {u"keywords=keyword1[,keyword2,...]"_s,
+ u"A comma-separated list of keywords for conditional typesystem parsing"_s},
+ };
+}
+
+struct TypeDatabaseOptions
+{
+ QStringList m_dropTypeEntries;
+ QStringList m_forceProcessSystemIncludes;
+ QStringList m_typesystemKeywords;
+ QStringList m_typesystemPaths;
+ bool m_suppressWarnings = true;
+};
+
+class TypeDatabaseOptionsParser : public OptionsParser
+{
+public:
+ explicit TypeDatabaseOptionsParser(TypeDatabaseOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value, OptionSource source) override;
+
+private:
+ TypeDatabaseOptions *m_options;
+};
+
+bool TypeDatabaseOptionsParser::handleBoolOption(const QString &key, OptionSource source)
+{
+ switch (source) {
+ case OptionSource::CommandLine:
+ case OptionSource::ProjectFile:
+ if (key == u"no-suppress-warnings") {
+ m_options->m_suppressWarnings = false;
+ return true;
+ }
+ break;
+ case OptionSource::CommandLineSingleDash:
+ if (key.startsWith(u'T')) { // "-T/path" ends up a bool option
+ m_options->m_typesystemPaths += key.sliced(1).split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool TypeDatabaseOptionsParser::handleOption(const QString &key, const QString &value,
+ OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
+ if (key == u"api-version") {
+ const auto fullVersions = QStringView{value}.split(u'|');
+ for (const auto &fullVersion : fullVersions) {
+ const auto parts = fullVersion.split(u',');
+ const QString package = parts.size() == 1
+ ? u"*"_s : parts.constFirst().toString();
+ const QString version = parts.constLast().toString();
+ if (!TypeDatabase::setApiVersion(package, version))
+ throw Exception(msgInvalidVersion(package, version));
+ }
+ return true;
+ }
+
+ if (key == u"drop-type-entries") {
+ m_options->m_dropTypeEntries = value.split(u';');
+ m_options->m_dropTypeEntries.sort();
+ return true;
+ }
+
+ if (key == u"keywords") {
+ m_options->m_typesystemKeywords = value.split(u',');
+ return true;
+ }
+
+ if (key == u"typesystem-paths") {
+ m_options->m_typesystemPaths += value.split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
+ return true;
+ }
+
+ if (key == u"force-process-system-include-paths") {
+ m_options->m_forceProcessSystemIncludes += value.split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
+ return true;
+ }
+
+ if (source == OptionSource::ProjectFile) {
+ if (key == u"typesystem-path") {
+ m_options->m_typesystemPaths += value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+struct TypeDatabasePrivate : public TypeDatabaseOptions
+{
+ TypeSystemTypeEntryCPtr defaultTypeSystemType() const;
+ TypeEntryPtr findType(const QString &name) const;
+ TypeEntryCList findCppTypes(const QString &name) const;
+ bool addType(TypeEntryPtr e, QString *errorMessage = nullptr);
+ bool parseFile(QIODevice *device, TypeDatabase *db, bool generate = true);
+ static bool parseFile(const TypeDatabaseParserContextPtr &context,
+ QIODevice *device, bool generate = true);
+ bool parseFile(const TypeDatabaseParserContextPtr &context,
+ const QString &filename, const QString &currentPath, bool generate);
+ bool prepareParsing(QFile &file, const QString &origFileName,
+ const QString &currentPath = {});
+
+ QString modifiedTypesystemFilepath(const QString& tsFile,
+ const QString &currentPath) const;
+ void addBuiltInType(const TypeEntryPtr &e);
+ PrimitiveTypeEntryPtr addBuiltInPrimitiveType(const QString &name,
+ const TypeSystemTypeEntryCPtr &root,
+ const QString &rootPackage,
+ const CustomTypeEntryPtr &targetLang);
+ void addBuiltInCppStringPrimitiveType(const QString &name,
+ const QString &viewName,
+ const TypeSystemTypeEntryCPtr &root,
+ const QString &rootPackage,
+ const CustomTypeEntryPtr &targetLang);
+ void addBuiltInPrimitiveTypes();
+ void addBuiltInContainerTypes(const TypeDatabaseParserContextPtr &context);
+ bool addOpaqueContainers(const TypeDatabaseParserContextPtr &context);
+ TypeEntryMultiMapConstIteratorRange findTypeRange(const QString &name) const;
+ template <class Predicate>
+ TypeEntryCList findTypesHelper(const QString &name, Predicate pred) const;
+ template <class Type, class Predicate>
+ QList<std::shared_ptr<const Type> > findTypesByTypeHelper(Predicate pred) const;
+ TypeEntryPtr resolveTypeDefEntry(const TypedefEntryPtr &typedefEntry, QString *errorMessage);
+ template <class String>
+ bool isSuppressedWarningHelper(const String &s) const;
+ bool resolveSmartPointerInstantiations(const TypeDatabaseParserContextPtr &context);
+ void formatDebug(QDebug &d) const;
+ void formatBuiltinTypes(QDebug &d) const;
+
+ TypeEntryMultiMap m_entries; // Contains duplicate entries (cf addInlineNamespaceLookups).
+ TypeEntryMap m_flagsEntries;
+ TypedefEntryMap m_typedefEntries;
+ TemplateEntryMap m_templates;
+ QList<SuppressedWarning> m_suppressedWarnings;
+ QList<TypeSystemTypeEntryCPtr > m_typeSystemEntries; // maintain order, default is first.
+
+ AddedFunctionList m_globalUserFunctions;
+ FunctionModificationList m_functionMods;
+
+ QStringList m_requiredTargetImports;
+
+ QHash<QString, bool> m_parsedTypesystemFiles;
+
+ QList<TypeRejection> m_rejections;
+};
+
+static const char ENV_TYPESYSTEMPATH[] = "TYPESYSTEMPATH";
+
+TypeDatabase::TypeDatabase() : d(new TypeDatabasePrivate)
+{
+ // Environment TYPESYSTEMPATH
+ if (qEnvironmentVariableIsSet(ENV_TYPESYSTEMPATH)) {
+ d->m_typesystemPaths
+ += qEnvironmentVariable(ENV_TYPESYSTEMPATH).split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
+ }
+
+ d->addBuiltInType(TypeEntryPtr(new VoidTypeEntry()));
+ d->addBuiltInType(TypeEntryPtr(new VarargsTypeEntry()));
+ for (const auto &pt : builtinPythonTypes())
+ d->addBuiltInType(TypeEntryPtr(new PythonTypeEntry(pt.name, pt.checkFunction, pt.type)));
+
+ for (const auto &p : predefinedTemplates())
+ addTemplate(p.name, p.content);
+}
+
+TypeDatabase::~TypeDatabase()
+{
+ delete d;
+}
+
+std::shared_ptr<OptionsParser> TypeDatabase::createOptionsParser()
+{
+ return std::make_shared<TypeDatabaseOptionsParser>(d);
+}
+
+TypeDatabase *TypeDatabase::instance(bool newInstance)
+{
+ static TypeDatabase *db = nullptr;
+ if (!db || newInstance) {
+ delete db;
+ db = new TypeDatabase;
+ }
+ return db;
+}
+
+// A list of regex/replacements to fix int types like "ushort" to "unsigned short"
+// unless present in TypeDatabase
+struct IntTypeNormalizationEntry
+{
+ QRegularExpression regex;
+ QString replacement;
+};
+
+using IntTypeNormalizationEntries = QList<IntTypeNormalizationEntry>;
+
+static const IntTypeNormalizationEntries &intTypeNormalizationEntries()
+{
+ static IntTypeNormalizationEntries result;
+ static bool firstTime = true;
+ if (firstTime) {
+ firstTime = false;
+ for (const auto &intType : {"char"_L1, "short"_L1, "int"_L1, "long"_L1}) {
+ if (!TypeDatabase::instance()->findType(u'u' + intType)) {
+ IntTypeNormalizationEntry entry;
+ entry.replacement = "unsigned "_L1 + intType;
+ entry.regex.setPattern("\\bu"_L1 + intType + "\\b"_L1);
+ Q_ASSERT(entry.regex.isValid());
+ result.append(entry);
+ }
+ }
+ }
+ return result;
+}
+
+// Normalization helpers
+enum CharCategory { Space, Identifier, Other };
+
+static CharCategory charCategory(QChar c)
+{
+ if (c.isSpace())
+ return Space;
+ if (c.isLetterOrNumber() || c == u'_')
+ return Identifier;
+ return Other;
+}
+
+// Normalize a C++ function signature:
+// Drop space except between identifiers ("unsigned int", "const int")
+static QString normalizeCppFunctionSignature(const QString &signatureIn)
+{
+ const QString signature = signatureIn.simplified();
+ QString result;
+ result.reserve(signature.size());
+
+ CharCategory lastNonSpaceCategory = Other;
+ bool pendingSpace = false;
+ for (QChar c : signature) {
+ if (c.isSpace()) {
+ pendingSpace = true;
+ } else {
+ const auto category = charCategory(c);
+ if (pendingSpace) {
+ if (lastNonSpaceCategory == Identifier && category == Identifier)
+ result.append(u' ');
+ pendingSpace = false;
+ }
+ lastNonSpaceCategory = category;
+ result.append(c);
+ }
+ }
+ return result;
+}
+
+// Normalize a signature for <add-function> by removing spaces
+QString TypeDatabase::normalizedAddedFunctionSignature(const QString &signature)
+{
+ return normalizeCppFunctionSignature(signature);
+}
+
+// Normalize a signature for matching by <modify-function>/<function>
+// by removing spaces and changing const-ref to value.
+// FIXME: PYSIDE7: Check whether the above simple normalization can be used
+// here as well. Note though that const-ref would then have to be spelled out
+// in typeystem XML.
+QString TypeDatabase::normalizedSignature(const QString &signature)
+{
+ // QMetaObject::normalizedSignature() changes const-ref to value and
+ // changes "unsigned int" to "uint" which is undone by the below code
+ QByteArray normalizedB = QMetaObject::normalizedSignature(signature.toUtf8().constData());
+ QString normalized = QLatin1StringView(normalizedB);
+
+ if (instance() && signature.contains(u"unsigned")) {
+ const IntTypeNormalizationEntries &entries = intTypeNormalizationEntries();
+ for (const auto &entry : entries)
+ normalized.replace(entry.regex, entry.replacement);
+ }
+
+ return normalized;
+}
+
+QStringList TypeDatabase::requiredTargetImports() const
+{
+ return d->m_requiredTargetImports;
+}
+
+void TypeDatabase::addRequiredTargetImport(const QString& moduleName)
+{
+ if (!d->m_requiredTargetImports.contains(moduleName))
+ d->m_requiredTargetImports << moduleName;
+}
+
+QStringList TypeDatabase::typesystemKeywords() const
+{
+ QStringList result = d->m_typesystemKeywords;
+ for (const auto &d : d->m_dropTypeEntries)
+ result.append("no_"_L1 + d);
+
+ switch (clang::emulatedCompilerLanguageLevel()) {
+ case LanguageLevel::Cpp11:
+ result.append(u"c++11"_s);
+ break;
+ case LanguageLevel::Cpp14:
+ result.append(u"c++14"_s);
+ break;
+ case LanguageLevel::Cpp17:
+ result.append(u"c++17"_s);
+ break;
+ case LanguageLevel::Cpp20:
+ result.append(u"c++20"_s);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+IncludeList TypeDatabase::extraIncludes(const QString& className) const
+{
+ auto typeEntry = findComplexType(className);
+ return typeEntry ? typeEntry->extraIncludes() : IncludeList();
+}
+
+const QStringList &TypeDatabase::forceProcessSystemIncludes() const
+{
+ return d->m_forceProcessSystemIncludes;
+}
+
+void TypeDatabase::addForceProcessSystemInclude(const QString &name)
+{
+ d->m_forceProcessSystemIncludes.append(name);
+}
+
+// Add a lookup for the short name excluding inline namespaces
+// so that "std::shared_ptr" finds "std::__1::shared_ptr" as well.
+// Note: This inserts duplicate TypeEntryPtr into m_entries.
+void TypeDatabase::addInlineNamespaceLookups(const NamespaceTypeEntryCPtr &n)
+{
+ TypeEntryList additionalEntries; // Store before modifying the hash
+ for (const auto &entry : std::as_const(d->m_entries)) {
+ if (entry->isChildOf(n))
+ additionalEntries.append(entry);
+ }
+ for (const auto &ae : std::as_const(additionalEntries))
+ d->m_entries.insert(ae->shortName(), ae);
+}
+
+ContainerTypeEntryPtr TypeDatabase::findContainerType(const QString &name) const
+{
+ QString template_name = name;
+
+ const auto pos = name.indexOf(u'<');
+ if (pos > 0)
+ template_name = name.left(pos);
+
+ auto type_entry = findType(template_name);
+ if (type_entry && type_entry->isContainer())
+ return std::static_pointer_cast<ContainerTypeEntry>(type_entry);
+ return {};
+}
+
+static bool inline useType(const TypeEntryCPtr &t)
+{
+ return !t->isPrimitive()
+ || std::static_pointer_cast<const PrimitiveTypeEntry>(t)->preferredTargetLangType();
+}
+
+FunctionTypeEntryPtr TypeDatabase::findFunctionType(const QString &name) const
+{
+ const auto entries = d->findTypeRange(name);
+ for (const TypeEntryPtr &entry : entries) {
+ if (entry->type() == TypeEntry::FunctionType && useType(entry))
+ return std::static_pointer_cast<FunctionTypeEntry>(entry);
+ }
+ return {};
+}
+
+void TypeDatabase::addTypeSystemType(const TypeSystemTypeEntryCPtr &e)
+{
+ d->m_typeSystemEntries.append(e);
+}
+
+TypeSystemTypeEntryCPtr TypeDatabase::findTypeSystemType(const QString &name) const
+{
+ for (auto entry : d->m_typeSystemEntries) {
+ if (entry->name() == name)
+ return entry;
+ }
+ return {};
+}
+
+TypeSystemTypeEntryCPtr TypeDatabase::defaultTypeSystemType() const
+{
+ return d->defaultTypeSystemType();
+}
+
+QString TypeDatabase::loadedTypeSystemNames() const
+{
+ QString result;
+ for (const auto &entry : d->m_typeSystemEntries) {
+ if (!result.isEmpty())
+ result += u", "_s;
+ result += entry->name();
+ }
+ return result;
+}
+
+TypeSystemTypeEntryCPtr TypeDatabasePrivate::defaultTypeSystemType() const
+{
+ return m_typeSystemEntries.value(0, nullptr);
+}
+
+QString TypeDatabase::defaultPackageName() const
+{
+ Q_ASSERT(!d->m_typeSystemEntries.isEmpty());
+ return d->m_typeSystemEntries.constFirst()->name();
+}
+
+TypeEntryPtr TypeDatabase::findType(const QString& name) const
+{
+ return d->findType(name);
+}
+
+TypeEntryPtr TypeDatabasePrivate::findType(const QString& name) const
+{
+ const auto entries = findTypeRange(name);
+ for (const auto &entry : entries) {
+ if (useType(entry))
+ return entry;
+ }
+ return {};
+}
+
+template <class Predicate>
+TypeEntryCList TypeDatabasePrivate::findTypesHelper(const QString &name, Predicate pred) const
+{
+ TypeEntryCList result;
+ const auto entries = findTypeRange(name);
+ for (const auto &entry : entries) {
+ if (pred(entry))
+ result.append(entry);
+ }
+ return result;
+}
+
+template<class Type, class Predicate>
+QList<std::shared_ptr<const Type> > TypeDatabasePrivate::findTypesByTypeHelper(Predicate pred) const
+{
+ QList<std::shared_ptr<const Type> > result;
+ for (const auto &entry : m_entries) {
+ if (pred(entry))
+ result.append(std::static_pointer_cast<const Type>(entry));
+ }
+ return result;
+}
+
+TypeEntryCList TypeDatabase::findTypes(const QString &name) const
+{
+ return d->findTypesHelper(name, useType);
+}
+
+static bool useCppType(const TypeEntryCPtr &t)
+{
+ bool result = false;
+ switch (t->type()) {
+ case TypeEntry::PrimitiveType:
+ case TypeEntry::VoidType:
+ case TypeEntry::FlagsType:
+ case TypeEntry::EnumType:
+ case TypeEntry::TemplateArgumentType:
+ case TypeEntry::BasicValueType:
+ case TypeEntry::ContainerType:
+ case TypeEntry::ObjectType:
+ case TypeEntry::ArrayType:
+ case TypeEntry::CustomType:
+ case TypeEntry::SmartPointerType:
+ case TypeEntry::TypedefType:
+ result = useType(t);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+TypeEntryCList TypeDatabase::findCppTypes(const QString &name) const
+{
+ return d->findCppTypes(name);
+}
+
+TypeEntryCList TypeDatabasePrivate::findCppTypes(const QString &name) const
+{
+ return findTypesHelper(name, useCppType);
+}
+
+const TypeEntryMultiMap &TypeDatabase::entries() const
+{
+ return d->m_entries;
+}
+
+const TypedefEntryMap &TypeDatabase::typedefEntries() const
+{
+ return d->m_typedefEntries;
+}
+
+TypeEntryMultiMapConstIteratorRange TypeDatabasePrivate::findTypeRange(const QString &name) const
+{
+ const auto range = m_entries.equal_range(name);
+ return {range.first, range.second};
+}
+
+PrimitiveTypeEntryCList TypeDatabase::primitiveTypes() const
+{
+ auto pred = [](const TypeEntryCPtr &t) { return t->isPrimitive(); };
+ return d->findTypesByTypeHelper<PrimitiveTypeEntry>(pred);
+}
+
+ContainerTypeEntryCList TypeDatabase::containerTypes() const
+{
+ auto pred = [](const TypeEntryCPtr &t) { return t->isContainer(); };
+ return d->findTypesByTypeHelper<ContainerTypeEntry>(pred);
+}
+
+SmartPointerTypeEntryList TypeDatabase::smartPointerTypes() const
+{
+ auto pred = [](const TypeEntryCPtr &t) { return t->isSmartPointer(); };
+ return d->findTypesByTypeHelper<SmartPointerTypeEntry>(pred);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const TypeRejection &r)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "TypeRejection(type=" << r.matchType << ", class="
+ << r.className.pattern() << ", pattern=" << r.pattern.pattern() << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+void TypeDatabase::addRejection(const TypeRejection &r)
+{
+ d->m_rejections << r;
+}
+
+// Match class name only
+bool TypeDatabase::isClassRejected(const QString& className, QString *reason) const
+{
+ for (const TypeRejection& r : d->m_rejections) {
+ if (r.matchType == TypeRejection::ExcludeClass && r.className.match(className).hasMatch()) {
+ r.matched = true;
+ if (reason)
+ *reason = msgRejectReason(r);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Match class name and function/enum/field
+static bool findRejection(const QList<TypeRejection> &rejections,
+ TypeRejection::MatchType matchType,
+ const QString& className, const QString& name,
+ QString *reason = nullptr)
+{
+ Q_ASSERT(matchType != TypeRejection::ExcludeClass);
+ for (const TypeRejection& r : rejections) {
+ if (r.matchType == matchType && r.pattern.match(name).hasMatch()
+ && r.className.match(className).hasMatch()) {
+ r.matched = true;
+ if (reason)
+ *reason = msgRejectReason(r, name);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumName, QString *reason) const
+{
+ return findRejection(d->m_rejections, TypeRejection::Enum, className, enumName, reason);
+}
+
+TypeEntryPtr TypeDatabasePrivate::resolveTypeDefEntry(const TypedefEntryPtr &typedefEntry,
+ QString *errorMessage)
+{
+ QString sourceName = typedefEntry->sourceType();
+ const auto lessThanPos = sourceName.indexOf(u'<');
+ if (lessThanPos != -1)
+ sourceName.truncate(lessThanPos);
+ ComplexTypeEntryPtr source;
+ for (const auto &e : findTypeRange(sourceName)) {
+ switch (e->type()) {
+ case TypeEntry::BasicValueType:
+ case TypeEntry::ContainerType:
+ case TypeEntry::ObjectType:
+ case TypeEntry::SmartPointerType:
+ source = std::dynamic_pointer_cast<ComplexTypeEntry>(e);
+ Q_ASSERT(source);
+ break;
+ default:
+ break;
+ }
+ }
+ if (!source) {
+ if (errorMessage)
+ *errorMessage = msgUnableToResolveTypedef(typedefEntry->sourceType(), sourceName);
+ return nullptr;
+ }
+
+ m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry);
+ return TypeDatabase::initializeTypeDefEntry(typedefEntry, source);
+}
+
+ComplexTypeEntryPtr
+ TypeDatabase::initializeTypeDefEntry(const TypedefEntryPtr &typedefEntry,
+ const ComplexTypeEntryCPtr &source)
+{
+ ComplexTypeEntryPtr result(static_cast<ComplexTypeEntry *>(source->clone()));
+ result->useAsTypedef(typedefEntry);
+ typedefEntry->setSource(source);
+ typedefEntry->setTarget(result);
+ return result;
+}
+
+bool TypeDatabase::addType(const TypeEntryPtr &e, QString *errorMessage)
+{
+ return d->addType(e, errorMessage);
+}
+
+bool TypeDatabasePrivate::addType(TypeEntryPtr e, QString *errorMessage)
+{
+ if (e->type() == TypeEntry::TypedefType) {
+ e = resolveTypeDefEntry(std::static_pointer_cast<TypedefEntry>(e), errorMessage);
+ if (Q_UNLIKELY(!e))
+ return false;
+ }
+ m_entries.insert(e->qualifiedCppName(), e);
+ return true;
+}
+
+// Add a dummy value entry for non-type template parameters
+ConstantValueTypeEntryPtr
+ TypeDatabase::addConstantValueTypeEntry(const QString &value,
+ const TypeEntryCPtr &parent)
+{
+ auto result = std::make_shared<ConstantValueTypeEntry>(value, parent);
+ result->setCodeGeneration(TypeEntry::GenerateNothing);
+ addType(result);
+ return result;
+}
+
+bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName,
+ QString *reason) const
+{
+ return findRejection(d->m_rejections, TypeRejection::Function, className, functionName, reason);
+}
+
+bool TypeDatabase::isFieldRejected(const QString& className, const QString& fieldName,
+ QString *reason) const
+{
+ return findRejection(d->m_rejections, TypeRejection::Field, className, fieldName, reason);
+}
+
+bool TypeDatabase::isArgumentTypeRejected(const QString& className, const QString& typeName,
+ QString *reason) const
+{
+ return findRejection(d->m_rejections, TypeRejection::ArgumentType, className, typeName, reason);
+}
+
+bool TypeDatabase::isReturnTypeRejected(const QString& className, const QString& typeName,
+ QString *reason) const
+{
+ return findRejection(d->m_rejections, TypeRejection::ReturnType, className, typeName, reason);
+}
+
+FlagsTypeEntryPtr TypeDatabase::findFlagsType(const QString &name) const
+{
+ TypeEntryPtr fte = findType(name);
+ if (!fte) {
+ fte = d->m_flagsEntries.value(name);
+ if (!fte) {
+ //last hope, search for flag without scope inside of flags hash
+ const auto end = d->m_flagsEntries.cend();
+ for (auto it = d->m_flagsEntries.cbegin(); it != end; ++it) {
+ if (it.key().endsWith(name)) {
+ fte = it.value();
+ break;
+ }
+ }
+ }
+ }
+ return std::static_pointer_cast<FlagsTypeEntry>(fte);
+}
+
+void TypeDatabase::addFlagsType(const FlagsTypeEntryPtr &fte)
+{
+ d->m_flagsEntries[fte->originalName()] = fte;
+}
+
+TemplateEntryPtr TypeDatabase::findTemplate(const QString &name) const
+{
+ return d->m_templates[name];
+}
+
+void TypeDatabase::addTemplate(const TemplateEntryPtr &t)
+{
+ d->m_templates[t->name()] = t;
+}
+
+void TypeDatabase::addTemplate(const QString &name, const QString &code)
+{
+ auto te = std::make_shared<TemplateEntry>(name);
+ te->addCode(code);
+ addTemplate(te);
+}
+
+AddedFunctionList TypeDatabase::globalUserFunctions() const
+{
+ return d->m_globalUserFunctions;
+}
+
+void TypeDatabase::addGlobalUserFunctions(const AddedFunctionList &functions)
+{
+ d->m_globalUserFunctions << functions;
+}
+
+AddedFunctionList TypeDatabase::findGlobalUserFunctions(const QString& name) const
+{
+ AddedFunctionList addedFunctions;
+ for (const AddedFunctionPtr &func : d->m_globalUserFunctions) {
+ if (func->name() == name)
+ addedFunctions.append(func);
+ }
+ return addedFunctions;
+}
+
+void TypeDatabase::addGlobalUserFunctionModifications(const FunctionModificationList &functionModifications)
+{
+ d->m_functionMods << functionModifications;
+}
+
+QString TypeDatabase::globalNamespaceClassName(const TypeEntryCPtr & /*entry*/)
+{
+ return u"Global"_s;
+}
+
+FunctionModificationList
+ TypeDatabase::globalFunctionModifications(const QStringList &signatures) const
+{
+ FunctionModificationList lst;
+ for (const auto &mod : d->m_functionMods) {
+ if (mod.matches(signatures))
+ lst << mod;
+ }
+
+ return lst;
+}
+
+bool TypeDatabase::addSuppressedWarning(const QString &warning, bool generate,
+ QString *errorMessage)
+{
+ QString pattern;
+ if (warning.startsWith(u'^') && warning.endsWith(u'$')) {
+ pattern = warning;
+ } else {
+ // Legacy syntax: Use wildcards '*' (unless escaped by '\')
+ QList<qsizetype> asteriskPositions;
+ const auto warningSize = warning.size();
+ for (qsizetype i = 0, warningSize = warning.size(); i < warningSize; ++i) {
+ if (warning.at(i) == u'\\')
+ ++i;
+ else if (warning.at(i) == u'*')
+ asteriskPositions.append(i);
+ }
+ asteriskPositions.append(warningSize);
+
+ pattern.append(u'^');
+ qsizetype lastPos = 0;
+ for (qsizetype a = 0, aSize = asteriskPositions.size(); a < aSize; ++a) {
+ if (a)
+ pattern.append(".*"_L1);
+ const auto nextPos = asteriskPositions.at(a);
+ if (nextPos > lastPos)
+ pattern.append(QRegularExpression::escape(warning.mid(lastPos, nextPos - lastPos)));
+ lastPos = nextPos + 1;
+ }
+ pattern.append(u'$');
+ }
+
+ QRegularExpression expression(pattern);
+ if (!expression.isValid()) {
+ *errorMessage = u"Invalid message pattern \""_s + warning
+ + u"\": "_s + expression.errorString();
+ return false;
+ }
+ expression.setPatternOptions(expression.patternOptions() | QRegularExpression::MultilineOption);
+
+ d->m_suppressedWarnings.append({expression, warning, generate});
+ return true;
+}
+
+bool TypeDatabase::isSuppressedWarning(QStringView s) const
+{
+ if (!d->m_suppressWarnings)
+ return false;
+ auto wit = std::find_if(d->m_suppressedWarnings.cbegin(), d->m_suppressedWarnings.cend(),
+ [&s] (const SuppressedWarning &e) {
+ return e.pattern.matchView(s).hasMatch();
+ });
+ const bool found = wit != d->m_suppressedWarnings.cend();
+ if (found)
+ wit->matched = true;
+ return found;
+}
+
+QString TypeDatabase::modifiedTypesystemFilepath(const QString& tsFile, const QString &currentPath) const
+{
+ return d->modifiedTypesystemFilepath(tsFile, currentPath);
+}
+
+void TypeDatabase::logUnmatched() const
+{
+ for (auto &sw : d->m_suppressedWarnings) {
+ if (sw.generate && !sw.matched)
+ qWarning("Unmatched suppressed warning: \"%s\"", qPrintable(sw.rawText));
+ }
+
+ for (auto &tr : d->m_rejections) {
+ if (tr.generate && !tr.matched) {
+ QDebug d = qWarning();
+ d.noquote();
+ d.nospace();
+ d << "Unmatched rejection: " << tr.matchType;
+ if (!tr.className.pattern().isEmpty())
+ d << " class " << tr.className.pattern();
+ if (!tr.pattern.pattern().isEmpty())
+ d << " \"" << tr.pattern.pattern() << '"';
+ }
+ }
+}
+
+QString TypeDatabasePrivate::modifiedTypesystemFilepath(const QString& tsFile,
+ const QString &currentPath) const
+{
+ const QFileInfo tsFi(tsFile);
+ if (tsFi.isAbsolute()) // No point in further lookups
+ return tsFi.absoluteFilePath();
+ if (tsFi.isFile()) // Make path absolute
+ return tsFi.absoluteFilePath();
+ if (!currentPath.isEmpty()) {
+ const QFileInfo fi(currentPath + u'/' + tsFile);
+ if (fi.isFile())
+ return fi.absoluteFilePath();
+ }
+ for (const QString &path : m_typesystemPaths) {
+ const QFileInfo fi(path + u'/' + tsFile);
+ if (fi.isFile())
+ return fi.absoluteFilePath();
+ }
+ return tsFile;
+}
+
+void TypeDatabasePrivate::addBuiltInContainerTypes(const TypeDatabaseParserContextPtr &context)
+{
+ // Unless the user has added the standard containers (potentially with
+ // some opaque types), add them by default.
+ const bool hasStdArray = findType(u"std::array"_s) != nullptr;
+ const bool hasStdPair = findType(u"std::pair"_s) != nullptr;
+ const bool hasStdList = findType(u"std::list"_s) != nullptr;
+ const bool hasStdVector = findType(u"std::vector"_s) != nullptr;
+ const bool hasStdMap = findType(u"std::map"_s) != nullptr;
+ const bool hasStdUnorderedMap = findType(u"std::unordered_map"_s) != nullptr;
+ const bool hasStdSpan = findType(u"std::span"_s) != nullptr;
+
+ if (hasStdPair && hasStdList && hasStdVector && hasStdMap && hasStdUnorderedMap)
+ return;
+
+ QByteArray ts = R"(<?xml version="1.0" encoding="UTF-8"?><typesystem>)";
+ if (!hasStdArray) {
+ ts += containerTypeSystemSnippet(
+ "std::array", "list", "array",
+ "shiboken_conversion_cppsequence_to_pylist",
+ "PySequence",
+ "shiboken_conversion_pyiterable_to_cpparray");
+ }
+ if (!hasStdPair) {
+ ts += containerTypeSystemSnippet(
+ "std::pair", "pair", "utility",
+ "shiboken_conversion_cpppair_to_pytuple",
+ "PySequence", "shiboken_conversion_pysequence_to_cpppair");
+ }
+ if (!hasStdList) {
+ ts += containerTypeSystemSnippet(
+ "std::list", "list", "list",
+ "shiboken_conversion_cppsequence_to_pylist",
+ "PySequence",
+ "shiboken_conversion_pyiterable_to_cppsequentialcontainer");
+ }
+ if (!hasStdVector) {
+ ts += containerTypeSystemSnippet(
+ "std::vector", "list", "vector",
+ "shiboken_conversion_cppsequence_to_pylist",
+ "PySequence",
+ "shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve");
+ }
+ if (!hasStdMap) {
+ ts += containerTypeSystemSnippet(
+ "std::map", "map", "map",
+ "shiboken_conversion_stdmap_to_pydict",
+ "PyDict", "shiboken_conversion_pydict_to_stdmap");
+ }
+ if (!hasStdUnorderedMap) {
+ ts += containerTypeSystemSnippet(
+ "std::unordered_map", "map", "unordered_map",
+ "shiboken_conversion_stdmap_to_pydict",
+ "PyDict", "shiboken_conversion_pydict_to_stdmap");
+ }
+ if (!hasStdSpan
+ && clang::emulatedCompilerLanguageLevel() >= LanguageLevel::Cpp20) {
+ auto spanSnip = containerTypeSystemSnippet(
+ "std::span", "span", "span",
+ "shiboken_conversion_cppsequence_to_pylist");
+ auto pos = spanSnip.indexOf('>');
+ spanSnip.insert(pos, R"( view-on="std::vector")");
+ ts += spanSnip;
+ }
+
+ ts += "</typesystem>";
+ QBuffer buffer(&ts);
+ buffer.open(QIODevice::ReadOnly);
+ const bool ok = parseFile(context, &buffer, true);
+ Q_ASSERT(ok);
+}
+
+bool TypeDatabasePrivate::addOpaqueContainers(const TypeDatabaseParserContextPtr &context)
+{
+ const auto &och = context->opaqueContainerHash;
+ for (auto it = och.cbegin(), end = och.cend(); it != end; ++it) {
+ const QString &name = it.key();
+ auto te = findType(name);
+ if (!te || !te->isContainer()) {
+ qCWarning(lcShiboken, "No container \"%s\" found.", qPrintable(name));
+ return false;
+ }
+ auto cte = std::static_pointer_cast<ContainerTypeEntry>(te);
+ cte->appendOpaqueContainers(it.value());
+ }
+ return true;
+}
+
+bool TypeDatabase::parseFile(const QString &filename, bool generate)
+{
+ QString filepath = modifiedTypesystemFilepath(filename, {});
+ QFile file(filepath);
+ return d->prepareParsing(file, filename) && d->parseFile(&file, this, generate);
+}
+
+bool TypeDatabase::parseFile(const TypeDatabaseParserContextPtr &context,
+ const QString &filename, const QString &currentPath,
+ bool generate)
+{
+ return d->parseFile(context, filename, currentPath, generate);
+}
+
+bool TypeDatabasePrivate::prepareParsing(QFile &file, const QString &origFileName,
+ const QString &currentPath)
+{
+ const QString &filepath = file.fileName();
+ if (!file.exists()) {
+ m_parsedTypesystemFiles[filepath] = false;
+ QString message = u"Can't find "_s + origFileName;
+ if (!currentPath.isEmpty())
+ message += u", current path: "_s + currentPath;
+ message += u", typesystem paths: "_s + m_typesystemPaths.join(u", "_s);
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ return false;
+ }
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ m_parsedTypesystemFiles[filepath] = false;
+ qCWarning(lcShiboken, "%s", qPrintable(msgCannotOpenForReading(file)));
+ return false;
+ }
+
+ m_parsedTypesystemFiles[filepath] = true;
+ return true;
+}
+
+bool TypeDatabasePrivate::parseFile(const TypeDatabaseParserContextPtr &context,
+ const QString &filename, const QString &currentPath,
+ bool generate)
+{
+ // Prevent recursion when including self.
+ QString filepath = modifiedTypesystemFilepath(filename, currentPath);
+ const auto it = m_parsedTypesystemFiles.constFind(filepath);
+ if (it != m_parsedTypesystemFiles.cend())
+ return it.value();
+
+ QFile file(filepath);
+ if (!prepareParsing(file, filename, currentPath))
+ return false;
+
+ const bool ok = parseFile(context, &file, generate);
+ m_parsedTypesystemFiles[filepath] = ok;
+ return ok;
+}
+
+bool TypeDatabase::parseFile(QIODevice *device, bool generate)
+{
+ return d->parseFile(device, this, generate);
+}
+
+bool TypeDatabasePrivate::parseFile(QIODevice *device, TypeDatabase *db, bool generate)
+{
+ const auto context = std::make_shared<TypeDatabaseParserContext>();
+ context->db = db;
+
+ if (!parseFile(context, device, generate))
+ return false;
+
+ addBuiltInPrimitiveTypes();
+ addBuiltInContainerTypes(context);
+ return addOpaqueContainers(context)
+ && resolveSmartPointerInstantiations(context);
+}
+
+bool TypeDatabase::parseFile(const TypeDatabaseParserContextPtr &context,
+ QIODevice *device, bool generate)
+{
+ return d->parseFile(context, device, generate);
+}
+
+bool TypeDatabasePrivate::parseFile(const TypeDatabaseParserContextPtr &context,
+ QIODevice *device, bool generate)
+{
+ ConditionalStreamReader reader(device);
+ reader.setConditions(context->db->typesystemKeywords());
+ TypeSystemParser handler(context, generate);
+ const bool result = handler.parse(reader);
+ if (!result) {
+ qCWarning(lcShiboken, "%s", qPrintable(handler.errorString()));
+ return false;
+ }
+ return result;
+}
+
+// Split a type list potentially with template types
+// "A<B,C>,D" -> ("A<B,C>", "D")
+static QStringList splitTypeList(const QString &s)
+{
+ QStringList result;
+ int templateDepth = 0;
+ qsizetype lastPos = 0;
+ const auto size = s.size();
+ for (qsizetype i = 0; i < size; ++i) {
+ switch (s.at(i).toLatin1()) {
+ case '<':
+ ++templateDepth;
+ break;
+ case '>':
+ --templateDepth;
+ break;
+ case ',':
+ if (templateDepth == 0) {
+ result.append(s.mid(lastPos, i - lastPos).trimmed());
+ lastPos = i + 1;
+ }
+ break;
+ }
+ }
+ if (lastPos < size)
+ result.append(s.mid(lastPos, size - lastPos).trimmed());
+ return result;
+}
+
+bool TypeDatabasePrivate::resolveSmartPointerInstantiations(const TypeDatabaseParserContextPtr &context)
+{
+ const auto &instantiations = context->smartPointerInstantiations;
+ for (auto it = instantiations.cbegin(), end = instantiations.cend(); it != end; ++it) {
+ auto smartPointerEntry = it.key();
+ const auto instantiationNames = splitTypeList(it.value());
+ SmartPointerTypeEntry::Instantiations instantiations;
+ instantiations.reserve(instantiationNames.size());
+ for (const auto &instantiation : instantiationNames) {
+ QString name;
+ QString type = instantiation;
+ const auto equalsPos = instantiation.indexOf(u'=');
+ if (equalsPos != -1) {
+ type.truncate(equalsPos);
+ name = instantiation.mid(equalsPos + 1);
+ }
+
+ const auto typeEntries = findCppTypes(type);
+ if (typeEntries.isEmpty()) {
+ const QString m = msgCannotFindTypeEntryForSmartPointer(type,
+ smartPointerEntry->name());
+ qCWarning(lcShiboken, "%s", qPrintable(m));
+ return false;
+ }
+ if (typeEntries.size() > 1) {
+ const QString m = msgAmbiguousTypesFound(type, typeEntries);
+ qCWarning(lcShiboken, "%s", qPrintable(m));
+ return false;
+ }
+ instantiations.append({name, typeEntries.constFirst()});
+ }
+ smartPointerEntry->setInstantiations(instantiations);
+ }
+ return true;
+}
+
+PrimitiveTypeEntryPtr TypeDatabase::findPrimitiveType(const QString& name) const
+{
+ const auto entries = d->findTypeRange(name);
+ for (const auto &entry : entries) {
+ if (entry->isPrimitive()) {
+ auto pe = std::static_pointer_cast<PrimitiveTypeEntry>(entry);
+ if (pe->preferredTargetLangType())
+ return pe;
+ }
+ }
+
+ return nullptr;
+}
+
+ComplexTypeEntryPtr TypeDatabase::findComplexType(const QString& name) const
+{
+ const auto entries = d->findTypeRange(name);
+ for (const auto &entry : entries) {
+ if (entry->isComplex() && useType(entry))
+ return std::static_pointer_cast<ComplexTypeEntry>(entry);
+ }
+ return nullptr;
+}
+
+ObjectTypeEntryPtr TypeDatabase::findObjectType(const QString& name) const
+{
+ const auto entries = d->findTypeRange(name);
+ for (const auto &entry : entries) {
+ if (entry && entry->isObject() && useType(entry))
+ return std::static_pointer_cast<ObjectTypeEntry>(entry);
+ }
+ return nullptr;
+}
+
+NamespaceTypeEntryList TypeDatabase::findNamespaceTypes(const QString& name) const
+{
+ NamespaceTypeEntryList result;
+ const auto entries = d->findTypeRange(name);
+ for (const auto &entry : entries) {
+ if (entry->isNamespace())
+ result.append(std::static_pointer_cast<NamespaceTypeEntry>(entry));
+ }
+ return result;
+}
+
+NamespaceTypeEntryPtr TypeDatabase::findNamespaceType(const QString& name,
+ const QString &fileName) const
+{
+ const auto entries = findNamespaceTypes(name);
+ // Preferably check on matching file name first, if a pattern was given.
+ if (!fileName.isEmpty()) {
+ for (const auto &entry : entries) {
+ if (entry->hasPattern() && entry->matchesFile(fileName))
+ return entry;
+ }
+ }
+ for (const auto &entry : entries) {
+ if (!entry->hasPattern())
+ return entry;
+ }
+ return nullptr;
+}
+
+bool TypeDatabase::shouldDropTypeEntry(const QString& fullTypeName) const
+{
+ return d->m_dropTypeEntries.contains(fullTypeName);
+}
+
+void TypeDatabase::setDropTypeEntries(const QStringList &dropTypeEntries)
+{
+ d->m_dropTypeEntries = dropTypeEntries;
+ d->m_dropTypeEntries.sort();
+}
+
+static bool computeTypeIndexes = true;
+static int maxTypeIndex;
+
+static bool typeEntryLessThan(const TypeEntryCPtr &t1, const TypeEntryCPtr &t2)
+{
+ if (t1->revision() < t2->revision())
+ return true;
+ return t1->revision() == t2->revision()
+ && t1->qualifiedCppName() < t2->qualifiedCppName();
+}
+
+static void _computeTypeIndexes()
+{
+ auto *tdb = TypeDatabase::instance();
+
+ TypeEntryList list;
+
+ // Group type entries by revision numbers
+ const auto &allEntries = tdb->entries();
+ list.reserve(allEntries.size());
+ for (auto tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) {
+ const TypeEntryPtr &entry = tit.value();
+ if (entry->isPrimitive()
+ || entry->isContainer()
+ || entry->isFunction()
+ || !entry->generateCode()
+ || entry->isEnumValue()
+ || entry->isVarargs()
+ || entry->isTypeSystem()
+ || entry->isVoid()
+ || entry->isCustom())
+ continue;
+ if (!list.contains(entry)) // Remove duplicates
+ list.append(entry);
+ }
+
+ // Sort the type entries by revision, name
+ std::sort(list.begin(), list.end(), typeEntryLessThan);
+
+ maxTypeIndex = 0;
+ for (const TypeEntryPtr &e : std::as_const(list))
+ e->setSbkIndex(maxTypeIndex++);
+ computeTypeIndexes = false;
+}
+
+void TypeEntry::setRevision(int r)
+{
+ if (setRevisionHelper(r))
+ computeTypeIndexes = true;
+}
+
+int TypeEntry::sbkIndex() const
+{
+ if (computeTypeIndexes)
+ _computeTypeIndexes();
+ return sbkIndexHelper();
+}
+
+int getMaxTypeIndex()
+{
+ if (computeTypeIndexes)
+ _computeTypeIndexes();
+ return maxTypeIndex;
+}
+
+void TypeDatabase::clearApiVersions()
+{
+ apiVersions()->clear();
+}
+
+bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QString &version)
+{
+ const QString packagePattern = wildcardToRegExp(packageWildcardPattern.trimmed());
+ const QVersionNumber versionNumber = QVersionNumber::fromString(version);
+ if (versionNumber.isNull())
+ return false;
+ ApiVersions &versions = *apiVersions();
+ for (qsizetype i = 0, size = versions.size(); i < size; ++i) {
+ if (versions.at(i).first.pattern() == packagePattern) {
+ versions[i].second = versionNumber;
+ return true;
+ }
+ }
+ const QRegularExpression packageRegex(packagePattern);
+ if (!packageRegex.isValid())
+ return false;
+ versions.append(std::make_pair(packageRegex, versionNumber));
+ return true;
+}
+
+bool TypeDatabase::checkApiVersion(const QString &package,
+ const VersionRange &vr)
+{
+ const ApiVersions &versions = *apiVersions();
+ if (versions.isEmpty()) // Nothing specified: use latest.
+ return true;
+ for (qsizetype i = 0, size = versions.size(); i < size; ++i) {
+ if (versions.at(i).first.match(package).hasMatch())
+ return versions.at(i).second >= vr.since
+ && versions.at(i).second <= vr.until;
+ }
+ return false;
+}
+
+bool TypeDatabase::hasDroppedTypeEntries() const
+{
+ return !d->m_dropTypeEntries.isEmpty();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void TypeDatabase::formatDebug(QDebug &debug) const
+{
+ d->formatDebug(debug);
+}
+
+void TypeDatabasePrivate::formatDebug(QDebug &d) const
+{
+ d << "TypeDatabase("
+ << "entries[" << m_entries.size() << "]=";
+ for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it)
+ d << " " << it.value() << '\n';
+ if (!m_typedefEntries.isEmpty()) {
+ d << "typedefs[" << m_typedefEntries.size() << "]=(";
+ const auto begin = m_typedefEntries.cbegin();
+ for (auto it = begin, end = m_typedefEntries.cend(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << " " << it.value() << '\n';
+ }
+ d << ")\n";
+ }
+ if (!m_templates.isEmpty()) {
+ d << "templates[" << m_templates.size() << "]=(";
+ const auto begin = m_templates.cbegin();
+ for (auto it = begin, end = m_templates.cend(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << it.value();
+ }
+ d << ")\n";
+ }
+ if (!m_flagsEntries.isEmpty()) {
+ d << "flags[" << m_flagsEntries.size() << "]=(";
+ const auto begin = m_flagsEntries.cbegin();
+ for (auto it = begin, end = m_flagsEntries.cend(); it != end; ++it) {
+ if (it != begin)
+ d << ", ";
+ d << it.value();
+ }
+ d << ")\n";
+ }
+ d <<"\nglobalUserFunctions=" << m_globalUserFunctions << '\n';
+ formatList(d, "globalFunctionMods", m_functionMods, "\n");
+ d << ')';
+}
+
+// Helpers for dumping out primitive type info
+
+struct formatPrimitiveEntry
+{
+ explicit formatPrimitiveEntry(const PrimitiveTypeEntryCPtr &e) : m_pe(e) {}
+
+ PrimitiveTypeEntryCPtr m_pe;
+};
+
+QDebug operator<<(QDebug debug, const formatPrimitiveEntry &fe)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ const QString &name = fe.m_pe->name();
+ const QString &targetLangName = fe.m_pe->targetLangApiName();
+ debug << '"' << name << '"';
+ if (name != targetLangName)
+ debug << " (\"" << targetLangName << "\")";
+ if (fe.m_pe->isBuiltIn())
+ debug << " [builtin]";
+ if (isExtendedCppPrimitive(fe.m_pe)) {
+ debug << " [";
+ if (!isCppPrimitive(fe.m_pe))
+ debug << "extended ";
+ debug << "C++]";
+ }
+ return debug;
+}
+
+// Sort primitive types for displaying; base type and typedef'ed types
+struct PrimitiveFormatListEntry
+{
+ PrimitiveTypeEntryCPtr baseType;
+ PrimitiveTypeEntryCList typedefs;
+};
+
+static bool operator<(const PrimitiveFormatListEntry &e1, const PrimitiveFormatListEntry &e2)
+{
+ return e1.baseType->name() < e2.baseType->name();
+}
+
+using PrimitiveFormatListEntries = QList<PrimitiveFormatListEntry>;
+
+static qsizetype indexOf(const PrimitiveFormatListEntries &e, const PrimitiveTypeEntryCPtr &needle)
+{
+ for (qsizetype i = 0, size = e.size(); i < size; ++i) {
+ if (e.at(i).baseType == needle)
+ return i;
+ }
+ return -1;
+}
+
+void TypeDatabase::formatBuiltinTypes(QDebug debug) const
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+
+ // Determine base types and their typedef'ed types
+ QList<PrimitiveFormatListEntry> primitiveEntries;
+ for (const auto &e : std::as_const(d->m_entries)) {
+ if (e->isPrimitive()) {
+ auto pe = std::static_pointer_cast<const PrimitiveTypeEntry>(e);
+ auto basic = basicReferencedTypeEntry(pe);
+ if (basic != pe) {
+ const auto idx = indexOf(primitiveEntries, basic);
+ if (idx != -1)
+ primitiveEntries[idx].typedefs.append(pe);
+ else
+ primitiveEntries.append(PrimitiveFormatListEntry{basic, {pe}});
+ } else {
+ primitiveEntries.append(PrimitiveFormatListEntry{pe, {}});
+ }
+ }
+ }
+
+ std::sort(primitiveEntries.begin(), primitiveEntries.end());
+
+ for (const auto &e : std::as_const(primitiveEntries)) {
+ debug << "Primitive: " << formatPrimitiveEntry(e.baseType) << '\n';
+ for (const auto &pe : e.typedefs)
+ debug << " " << formatPrimitiveEntry(pe) << '\n';
+ }
+}
+
+void TypeDatabasePrivate::addBuiltInType(const TypeEntryPtr &e)
+{
+ e->setBuiltIn(true);
+ addType(e);
+}
+
+PrimitiveTypeEntryPtr
+ TypeDatabasePrivate::addBuiltInPrimitiveType(const QString &name,
+ const TypeSystemTypeEntryCPtr &root,
+ const QString &rootPackage,
+ const CustomTypeEntryPtr &targetLang)
+{
+ auto result = std::make_shared<PrimitiveTypeEntry>(name, QVersionNumber{}, root);
+ result->setTargetLangApiType(targetLang);
+ result->setTargetLangPackage(rootPackage);
+ addBuiltInType(result);
+ return result;
+}
+
+void TypeDatabasePrivate::addBuiltInCppStringPrimitiveType(const QString &name,
+ const QString &viewName,
+ const TypeSystemTypeEntryCPtr &root,
+ const QString &rootPackage,
+ const CustomTypeEntryPtr &targetLang)
+
+{
+ auto stringType = addBuiltInPrimitiveType(name, root, rootPackage,
+ targetLang);
+ auto viewType = addBuiltInPrimitiveType(viewName, root, rootPackage,
+ nullptr);
+ viewType->setViewOn(stringType);
+}
+
+void TypeDatabasePrivate::addBuiltInPrimitiveTypes()
+{
+ auto root = defaultTypeSystemType();
+ const QString &rootPackage = root->name();
+
+ // C++ primitive types
+ auto pyLongEntry = findType(u"PyLong"_s);
+ Q_ASSERT(pyLongEntry && pyLongEntry->isCustom());
+ auto pyLongCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyLongEntry);
+ auto pyBoolEntry = findType(u"PyBool"_s);
+ Q_ASSERT(pyBoolEntry && pyBoolEntry->isCustom());
+ auto sbkCharEntry = findType(u"SbkChar"_s);
+ Q_ASSERT(sbkCharEntry && sbkCharEntry->isCustom());
+ auto sbkCharCustomEntry = std::static_pointer_cast<CustomTypeEntry>(sbkCharEntry);
+
+ auto pyBoolCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyBoolEntry);
+ for (const auto &t : AbstractMetaType::cppIntegralTypes()) {
+ if (!m_entries.contains(t)) {
+ CustomTypeEntryPtr targetLangApi = pyLongCustomEntry;
+ if (t == u"bool")
+ targetLangApi = pyBoolCustomEntry;
+ else if (AbstractMetaType::cppCharTypes().contains(t))
+ targetLangApi = sbkCharCustomEntry;
+ addBuiltInPrimitiveType(t, root, rootPackage, targetLangApi);
+ }
+ }
+
+ auto pyFloatEntry = findType(u"PyFloat"_s);
+ Q_ASSERT(pyFloatEntry && pyFloatEntry->isCustom());
+ auto pyFloatCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyFloatEntry);
+ for (const auto &t : AbstractMetaType::cppFloatTypes()) {
+ if (!m_entries.contains(t))
+ addBuiltInPrimitiveType(t, root, rootPackage, pyFloatCustomEntry);
+ }
+
+ auto pyUnicodeEntry = findType(u"PyUnicode"_s);
+ Q_ASSERT(pyUnicodeEntry && pyUnicodeEntry->isCustom());
+ auto pyUnicodeCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyUnicodeEntry);
+
+ constexpr auto stdString = "std::string"_L1;
+ if (!m_entries.contains(stdString)) {
+ addBuiltInCppStringPrimitiveType(stdString, u"std::string_view"_s,
+ root, rootPackage,
+ pyUnicodeCustomEntry);
+ }
+ constexpr auto stdWString = "std::wstring"_L1;
+ if (!m_entries.contains(stdWString)) {
+ addBuiltInCppStringPrimitiveType(stdWString, u"std::wstring_view"_s,
+ root, rootPackage,
+ pyUnicodeCustomEntry);
+ }
+}
+
+QDebug operator<<(QDebug d, const TypeDatabase &db)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ db.formatDebug(d);
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/typedatabase.h b/sources/shiboken6/ApiExtractor/typedatabase.h
new file mode 100644
index 000000000..d5adca324
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typedatabase.h
@@ -0,0 +1,208 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEDATABASE_H
+#define TYPEDATABASE_H
+
+#include "include.h"
+#include "modifications_typedefs.h"
+#include "typedatabase_typedefs.h"
+
+#include <QtCore/QRegularExpression>
+#include <QtCore/QStringList>
+#include <QtCore/QVersionNumber>
+
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QIODevice)
+
+struct OptionDescription;
+class OptionsParser;
+struct TypeDatabasePrivate;
+struct TypeDatabaseParserContext;
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+int getMaxTypeIndex();
+
+struct VersionRange
+{
+ bool isNull() const
+ {
+ return since.majorVersion() == 0 && since.minorVersion() == 0
+ && until.majorVersion() == 9999 && until.minorVersion() == 9999;
+ }
+
+ QVersionNumber since{0, 0};
+ QVersionNumber until{9999, 9999};
+};
+
+struct TypeRejection
+{
+ enum MatchType
+ {
+ ExcludeClass, // Match className only
+ Function, // Match className and function name
+ Field, // Match className and field name
+ Enum, // Match className and enum name
+ ArgumentType, // Match className and argument type
+ ReturnType // Match className and return type
+ };
+
+ QRegularExpression className;
+ QRegularExpression pattern;
+ MatchType matchType = ExcludeClass;
+ bool generate; // Current type system
+ mutable bool matched = false;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const TypeRejection &r);
+#endif
+
+class TypeDatabase
+{
+ TypeDatabase();
+public:
+ Q_DISABLE_COPY_MOVE(TypeDatabase)
+
+ ~TypeDatabase();
+
+ static QList<OptionDescription> options();
+ std::shared_ptr<OptionsParser> createOptionsParser();
+
+ /**
+ * Return the type system instance.
+ * \param newInstance This parameter is useful just for unit testing, because singletons causes
+ * too many side effects on unit testing.
+ */
+ static TypeDatabase *instance(bool newInstance = false);
+
+ static QString normalizedSignature(const QString &signature);
+ static QString normalizedAddedFunctionSignature(const QString &signature);
+
+ QStringList requiredTargetImports() const;
+
+ void addRequiredTargetImport(const QString &moduleName);
+
+ QStringList typesystemKeywords() const;
+
+ IncludeList extraIncludes(const QString &className) const;
+
+ const QStringList &forceProcessSystemIncludes() const;
+ void addForceProcessSystemInclude(const QString &name);
+
+ void addInlineNamespaceLookups(const NamespaceTypeEntryCPtr &n);
+
+ PrimitiveTypeEntryPtr findPrimitiveType(const QString &name) const;
+ ComplexTypeEntryPtr findComplexType(const QString &name) const;
+ ObjectTypeEntryPtr findObjectType(const QString &name) const;
+ NamespaceTypeEntryList findNamespaceTypes(const QString &name) const;
+ NamespaceTypeEntryPtr findNamespaceType(const QString &name, const QString &fileName = QString()) const;
+ ContainerTypeEntryPtr findContainerType(const QString &name) const;
+ FunctionTypeEntryPtr findFunctionType(const QString &name) const;
+ TypeSystemTypeEntryCPtr findTypeSystemType(const QString &name) const;
+ TypeSystemTypeEntryCPtr defaultTypeSystemType() const;
+ QString loadedTypeSystemNames() const;
+ QString defaultPackageName() const;
+
+ TypeEntryPtr findType(const QString &name) const;
+ TypeEntryCList findTypes(const QString &name) const;
+ TypeEntryCList findCppTypes(const QString &name) const;
+
+ const TypeEntryMultiMap &entries() const;
+ const TypedefEntryMap &typedefEntries() const;
+
+ PrimitiveTypeEntryCList primitiveTypes() const;
+
+ ContainerTypeEntryCList containerTypes() const;
+
+ SmartPointerTypeEntryList smartPointerTypes() const;
+
+ void addRejection(const TypeRejection &);
+ bool isClassRejected(const QString &className, QString *reason = nullptr) const;
+ bool isFunctionRejected(const QString &className, const QString &functionName,
+ QString *reason = nullptr) const;
+ bool isFieldRejected(const QString &className, const QString &fieldName,
+ QString *reason = nullptr) const;
+ bool isEnumRejected(const QString &className, const QString &enumName,
+ QString *reason = nullptr) const;
+ bool isArgumentTypeRejected(const QString &className, const QString &typeName,
+ QString *reason = nullptr) const;
+ bool isReturnTypeRejected(const QString &className, const QString &typeName,
+ QString *reason = nullptr) const;
+
+ bool addType(const TypeEntryPtr &e, QString *errorMessage = nullptr);
+ ConstantValueTypeEntryPtr addConstantValueTypeEntry(const QString &value,
+ const TypeEntryCPtr &parent);
+ void addTypeSystemType(const TypeSystemTypeEntryCPtr &e);
+
+ static ComplexTypeEntryPtr
+ initializeTypeDefEntry(const TypedefEntryPtr &typedefEntry,
+ const ComplexTypeEntryCPtr &source);
+
+ FlagsTypeEntryPtr findFlagsType(const QString &name) const;
+ void addFlagsType(const FlagsTypeEntryPtr &fte);
+
+ TemplateEntryPtr findTemplate(const QString &name) const;
+
+ void addTemplate(const TemplateEntryPtr &t);
+ void addTemplate(const QString &name, const QString &code);
+
+ AddedFunctionList globalUserFunctions() const;
+
+ void addGlobalUserFunctions(const AddedFunctionList &functions);
+
+ AddedFunctionList findGlobalUserFunctions(const QString &name) const;
+
+ void addGlobalUserFunctionModifications(const FunctionModificationList &functionModifications);
+
+ FunctionModificationList
+ globalFunctionModifications(const QStringList &signatures) const;
+
+ bool addSuppressedWarning(const QString &warning, bool generate, QString *errorMessage);
+
+ bool isSuppressedWarning(QStringView s) const;
+
+ static QString globalNamespaceClassName(const TypeEntryCPtr &te);
+
+ // Top level file parsing
+ bool parseFile(const QString &filename, bool generate = true);
+ bool parseFile(const std::shared_ptr<TypeDatabaseParserContext> &context,
+ const QString &filename, const QString &currentPath, bool generate);
+
+ // Top level QIODevice parsing for tests.
+ bool parseFile(QIODevice *device, bool generate = true);
+ bool parseFile(const std::shared_ptr<TypeDatabaseParserContext> &context,
+ QIODevice *device, bool generate = true);
+
+ static bool setApiVersion(const QString &package, const QString &version);
+ static void clearApiVersions();
+
+ static bool checkApiVersion(const QString &package, const VersionRange &vr);
+
+ bool hasDroppedTypeEntries() const;
+
+ bool shouldDropTypeEntry(const QString &fullTypeName) const;
+
+ void setDropTypeEntries(const QStringList &dropTypeEntries);
+
+ QString modifiedTypesystemFilepath(const QString &tsFile, const QString &currentPath = QString()) const;
+
+ void logUnmatched() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+ void formatBuiltinTypes(QDebug debug) const;
+
+private:
+ TypeDatabasePrivate *d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const TypeEntryCPtr &te);
+QDebug operator<<(QDebug d, const TypeEntry *te);
+QDebug operator<<(QDebug d, const TypeDatabase &db);
+#endif
+#endif // TYPEDATABASE_H
diff --git a/sources/shiboken6/ApiExtractor/typedatabase_p.h b/sources/shiboken6/ApiExtractor/typedatabase_p.h
new file mode 100644
index 000000000..fc56c7961
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typedatabase_p.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEDATABASE_P_H
+#define TYPEDATABASE_P_H
+
+#include "typesystem_typedefs.h"
+#include "containertypeentry.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QString>
+
+class TypeDatabase;
+
+struct TypeDatabaseParserContext
+{
+ using SmartPointerInstantiations = QHash<SmartPointerTypeEntryPtr, QString>;
+ using OpaqueContainerHash = QHash<QString, OpaqueContainers>;
+
+ TypeDatabase *db;
+ SmartPointerInstantiations smartPointerInstantiations;
+ OpaqueContainerHash opaqueContainerHash;
+};
+
+#endif // TYPEDATABASE_P_H
diff --git a/sources/shiboken6/ApiExtractor/typedatabase_typedefs.h b/sources/shiboken6/ApiExtractor/typedatabase_typedefs.h
new file mode 100644
index 000000000..f00c61570
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typedatabase_typedefs.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEDATABASE_TYPEDEFS_H
+#define TYPEDATABASE_TYPEDEFS_H
+
+#include "typesystem_typedefs.h"
+
+#include <QtCore/QMultiMap>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+using TemplateEntryMap =QMap<QString, TemplateEntryPtr>;
+
+template <class Key, class Value>
+struct QMultiMapConstIteratorRange // A range of iterator for a range-based for loop
+{
+ using ConstIterator = typename QMultiMap<Key, Value>::const_iterator;
+
+ ConstIterator begin() const { return m_begin; }
+ ConstIterator end() const { return m_end; }
+
+ ConstIterator m_begin;
+ ConstIterator m_end;
+};
+
+using TypeEntryMultiMap = QMultiMap<QString, TypeEntryPtr>;
+using TypeEntryMultiMapConstIteratorRange = QMultiMapConstIteratorRange<QString, TypeEntryPtr>;
+
+using TypeEntryMap = QMap<QString, TypeEntryPtr>;
+using TypedefEntryMap = QMap<QString, TypedefEntryPtr>;
+
+#endif // TYPEDATABASE_TYPEDEFS_H
diff --git a/sources/shiboken6/ApiExtractor/typedefentry.h b/sources/shiboken6/ApiExtractor/typedefentry.h
new file mode 100644
index 000000000..44646972c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typedefentry.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEDEFENTRY_H
+#define TYPEDEFENTRY_H
+
+#include "complextypeentry.h"
+
+class TypedefEntryPrivate;
+
+class TypedefEntry : public ComplexTypeEntry
+{
+public:
+ explicit TypedefEntry(const QString &entryName,
+ const QString &sourceType,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ QString sourceType() const;
+ void setSourceType(const QString &s);
+
+ TypeEntry *clone() const override;
+
+ ComplexTypeEntryCPtr source() const;
+ void setSource(const ComplexTypeEntryCPtr &source);
+
+ ComplexTypeEntryPtr target() const;
+ void setTarget(ComplexTypeEntryPtr target);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+protected:
+ explicit TypedefEntry(TypedefEntryPrivate *d);
+};
+
+#endif // TYPEDEFENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/typeparser.cpp b/sources/shiboken6/ApiExtractor/typeparser.cpp
new file mode 100644
index 000000000..11d7bf641
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typeparser.cpp
@@ -0,0 +1,292 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "typeparser.h"
+#include <typeinfo.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QStack>
+#include <QtCore/QTextStream>
+
+using namespace Qt::StringLiterals;
+
+class Scanner
+{
+public:
+ enum Token {
+ StarToken,
+ AmpersandToken,
+ LessThanToken,
+ ColonToken,
+ CommaToken,
+ OpenParenToken,
+ CloseParenToken,
+ SquareBegin,
+ SquareEnd,
+ GreaterThanToken,
+
+ ConstToken,
+ VolatileToken,
+ Identifier,
+ NoToken,
+ InvalidToken
+ };
+
+ Scanner(const QString &s)
+ : m_pos(0), m_length(s.length()), m_tokenStart(-1), m_chars(s.constData())
+ {
+ }
+
+ Token nextToken(QString *errorMessage = nullptr);
+ QString identifier() const;
+
+ QString msgParseError(const QString &why) const;
+
+private:
+ int m_pos;
+ int m_length;
+ int m_tokenStart;
+ const QChar *m_chars;
+};
+
+QString Scanner::identifier() const
+{
+ return QString(m_chars + m_tokenStart, m_pos - m_tokenStart);
+}
+
+Scanner::Token Scanner::nextToken(QString *errorMessage)
+{
+ Token tok = NoToken;
+
+ // remove whitespace
+ while (m_pos < m_length && m_chars[m_pos] == u' ')
+ ++m_pos;
+
+ m_tokenStart = m_pos;
+
+ while (m_pos < m_length) {
+
+ const QChar &c = m_chars[m_pos];
+
+ if (tok == NoToken) {
+ switch (c.toLatin1()) {
+ case '*': tok = StarToken; break;
+ case '&': tok = AmpersandToken; break;
+ case '<': tok = LessThanToken; break;
+ case '>': tok = GreaterThanToken; break;
+ case ',': tok = CommaToken; break;
+ case '(': tok = OpenParenToken; break;
+ case ')': tok = CloseParenToken; break;
+ case '[': tok = SquareBegin; break;
+ case ']' : tok = SquareEnd; break;
+ case ':':
+ tok = ColonToken;
+ Q_ASSERT(m_pos + 1 < m_length);
+ ++m_pos;
+ break;
+ default:
+ if (c.isLetterOrNumber() || c == u'_') {
+ tok = Identifier;
+ } else {
+ QString message;
+ QTextStream (&message) << ": Unrecognized character in lexer at "
+ << m_pos << " : '" << c << '\'';
+ message = msgParseError(message);
+ if (errorMessage)
+ *errorMessage = message;
+ else
+ qWarning().noquote().nospace() << message;
+ return InvalidToken;
+ }
+ break;
+ }
+ }
+
+ if (tok <= GreaterThanToken) {
+ ++m_pos;
+ break;
+ }
+
+ if (tok == Identifier) {
+ if (c.isLetterOrNumber() || c == u'_')
+ ++m_pos;
+ else
+ break;
+ }
+ }
+
+ if (tok == Identifier) {
+ switch (m_pos - m_tokenStart) {
+ case 5:
+ if (m_chars[m_tokenStart] == u'c'
+ && m_chars[m_tokenStart + 1] == u'o'
+ && m_chars[m_tokenStart + 2] == u'n'
+ && m_chars[m_tokenStart + 3] == u's'
+ && m_chars[m_tokenStart + 4] == u't') {
+ tok = ConstToken;
+ }
+ break;
+ case 8:
+ if (m_chars[m_tokenStart] == u'v'
+ && m_chars[m_tokenStart + 1] == u'o'
+ && m_chars[m_tokenStart + 2] == u'l'
+ && m_chars[m_tokenStart + 3] == u'a'
+ && m_chars[m_tokenStart + 4] == u't'
+ && m_chars[m_tokenStart + 5] == u'i'
+ && m_chars[m_tokenStart + 6] == u'l'
+ && m_chars[m_tokenStart + 7] == u'e') {
+ tok = VolatileToken;
+ }
+ break;
+ }
+ }
+
+ return tok;
+
+}
+
+QString Scanner::msgParseError(const QString &why) const
+{
+ return "TypeParser: Unable to parse \""_L1
+ + QString(m_chars, m_length) + "\": "_L1 + why;
+}
+
+TypeInfo TypeParser::parse(const QString &str, QString *errorMessage)
+{
+ Scanner scanner(str);
+
+ QStack<TypeInfo> stack;
+ stack.push(TypeInfo());
+
+ bool colon_prefix = false;
+ bool in_array = false;
+ QString array;
+ bool seenStar = false;
+
+ Scanner::Token tok = scanner.nextToken(errorMessage);
+ while (tok != Scanner::NoToken) {
+ if (tok == Scanner::InvalidToken)
+ return {};
+
+// switch (tok) {
+// case Scanner::StarToken: printf(" - *\n"); break;
+// case Scanner::AmpersandToken: printf(" - &\n"); break;
+// case Scanner::LessThanToken: printf(" - <\n"); break;
+// case Scanner::GreaterThanToken: printf(" - >\n"); break;
+// case Scanner::ColonToken: printf(" - ::\n"); break;
+// case Scanner::CommaToken: printf(" - ,\n"); break;
+// case Scanner::ConstToken: printf(" - const\n"); break;
+// case Scanner::SquareBegin: printf(" - [\n"); break;
+// case Scanner::SquareEnd: printf(" - ]\n"); break;
+// case Scanner::Identifier: printf(" - '%s'\n", qPrintable(scanner.identifier())); break;
+// default:
+// break;
+// }
+
+ switch (tok) {
+
+ case Scanner::StarToken:
+ seenStar = true;
+ stack.top().addIndirection(Indirection::Pointer);
+ break;
+
+ case Scanner::AmpersandToken:
+ switch (stack.top().referenceType()) {
+ case NoReference:
+ stack.top().setReferenceType(LValueReference);
+ break;
+ case LValueReference:
+ stack.top().setReferenceType(RValueReference);
+ break;
+ case RValueReference:
+ const QString message = scanner.msgParseError("Too many '&' qualifiers"_L1);
+ if (errorMessage)
+ *errorMessage = message;
+ else
+ qWarning().noquote().nospace() << message;
+ return {};
+ }
+ break;
+ case Scanner::LessThanToken:
+ stack.push(TypeInfo());
+ break;
+
+ case Scanner::CommaToken:
+ {
+ auto i = stack.pop();
+ stack.top().addInstantiation(i); // Add after populating to prevent detach
+ stack.push(TypeInfo());
+ }
+ break;
+
+ case Scanner::GreaterThanToken: {
+ auto i = stack.pop();
+ stack.top().addInstantiation(i); // Add after populating to prevent detach
+ }
+ break;
+
+ case Scanner::ColonToken:
+ colon_prefix = true;
+ break;
+
+ case Scanner::ConstToken:
+ if (seenStar) { // "int *const": Last indirection is const.
+ auto indirections = stack.top().indirectionsV();
+ Q_ASSERT(!indirections.isEmpty());
+ indirections[0] = Indirection::ConstPointer;
+ stack.top().setIndirectionsV(indirections);
+ } else {
+ stack.top().setConstant(true);
+ }
+ break;
+
+ case Scanner::VolatileToken:
+ stack.top().setVolatile(true);
+ break;
+
+ case Scanner::OpenParenToken: // function pointers not supported
+ case Scanner::CloseParenToken: {
+ const QString message = scanner.msgParseError("Function pointers are not supported"_L1);
+ if (errorMessage)
+ *errorMessage = message;
+ else
+ qWarning().noquote().nospace() << message;
+ return {};
+ }
+
+ case Scanner::Identifier:
+ if (in_array) {
+ array = scanner.identifier();
+ } else if (colon_prefix || stack.top().qualifiedName().isEmpty()) {
+ stack.top().addName(scanner.identifier());
+ colon_prefix = false;
+ } else {
+ QStringList qualifiedName = stack.top().qualifiedName();
+ qualifiedName.last().append(u' ' + scanner.identifier());
+ stack.top().setQualifiedName(qualifiedName);
+ }
+ break;
+
+ case Scanner::SquareBegin:
+ in_array = true;
+ break;
+
+ case Scanner::SquareEnd:
+ in_array = false;
+ stack.top().addArrayElement(array);
+ break;
+
+
+ default:
+ break;
+ }
+
+ tok = scanner.nextToken(errorMessage);
+ }
+
+ if (stack.isEmpty() || stack.constFirst().qualifiedName().isEmpty()) {
+ *errorMessage = u"Unable to parse type \""_s + str + u"\"."_s;
+ return {};
+ }
+ return stack.constFirst();
+}
diff --git a/sources/shiboken6/ApiExtractor/typeparser.h b/sources/shiboken6/ApiExtractor/typeparser.h
new file mode 100644
index 000000000..97634b5db
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typeparser.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPEPARSER_H
+#define TYPEPARSER_H
+
+#include <QtCore/QString>
+
+class TypeInfo;
+
+class TypeParser
+{
+public:
+ static TypeInfo parse(const QString &str, QString *errorMessage = nullptr);
+};
+
+#endif // TYPEPARSER_H
diff --git a/sources/shiboken6/ApiExtractor/typesystem.cpp b/sources/shiboken6/ApiExtractor/typesystem.cpp
new file mode 100644
index 000000000..99d42b668
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystem.cpp
@@ -0,0 +1,2649 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "typesystem.h"
+#include "arraytypeentry.h"
+#include "codesnip.h"
+#include "complextypeentry.h"
+#include "configurabletypeentry.h"
+#include "constantvaluetypeentry.h"
+#include "containertypeentry.h"
+#include "customtypenentry.h"
+#include "debughelpers_p.h"
+#include "enumtypeentry.h"
+#include "enumvaluetypeentry.h"
+#include "flagstypeentry.h"
+#include "functiontypeentry.h"
+#include "include.h"
+#include "namespacetypeentry.h"
+#include "objecttypeentry.h"
+#include "primitivetypeentry.h"
+#include "pythontypeentry.h"
+#include "smartpointertypeentry.h"
+#include "templateargumententry.h"
+#include "typedefentry.h"
+#include "typesystemtypeentry.h"
+#include "valuetypeentry.h"
+#include "varargstypeentry.h"
+#include "voidtypeentry.h"
+#include "abstractmetatype.h"
+#include "typedatabase.h"
+#include "modifications.h"
+#include "sourcelocation.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QSet>
+#include <QtCore/QVarLengthArray>
+
+using namespace Qt::StringLiterals;
+
+static QString buildName(const QString &entryName, const TypeEntryCPtr &parent)
+{
+ return parent == nullptr || parent->type() == TypeEntry::TypeSystemType
+ ? entryName : parent->name() + u"::"_s + entryName;
+}
+
+// Access private class as 'd', cf macro Q_D()
+#define S_D(Class) auto d = static_cast<Class##Private *>(d_func())
+
+class TypeEntryPrivate
+{
+public:
+ TypeEntryPrivate(const TypeEntryPrivate &) = default; // Enable copy for cloning.
+ TypeEntryPrivate &operator=(const TypeEntryPrivate &) = delete;
+ TypeEntryPrivate(TypeEntryPrivate &&) = delete;
+ TypeEntryPrivate &operator=(TypeEntryPrivate &&) = delete;
+
+ explicit TypeEntryPrivate(const QString &entryName, TypeEntry::Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+ virtual ~TypeEntryPrivate() = default;
+
+ QString shortName() const;
+
+ TypeEntryCPtr m_parent;
+ QString m_name; // C++ fully qualified
+ mutable QString m_cachedShortName; // C++ excluding inline namespaces
+ QString m_entryName;
+ QString m_targetLangPackage;
+ mutable QString m_cachedTargetLangName; // "Foo.Bar"
+ mutable QString m_cachedTargetLangEntryName; // "Bar"
+ IncludeList m_extraIncludes;
+ Include m_include;
+ QVersionNumber m_version;
+ SourceLocation m_sourceLocation; // XML file
+ TypeEntry::CodeGeneration m_codeGeneration = TypeEntry::GenerateCode;
+ TypeEntryPtr m_viewOn;
+ CustomTypeEntryPtr m_targetLangApiType;
+ int m_revision = 0;
+ int m_sbkIndex = 0;
+ TypeEntry::Type m_type;
+ bool m_stream = false;
+ bool m_private = false;
+ bool m_builtin = false;
+};
+
+TypeEntryPrivate::TypeEntryPrivate(const QString &entryName, TypeEntry::Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ m_parent(parent),
+ m_name(buildName(entryName, parent)),
+ m_entryName(entryName),
+ m_version(vr),
+ m_type(t)
+{
+}
+
+TypeEntry::TypeEntry(const QString &entryName, TypeEntry::Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new TypeEntryPrivate(entryName, t, vr, parent))
+{
+}
+
+TypeEntry::TypeEntry(TypeEntryPrivate *d) : m_d(d)
+{
+}
+
+TypeEntry::~TypeEntry() = default;
+
+const IncludeList &TypeEntry::extraIncludes() const
+{
+ return m_d->m_extraIncludes;
+}
+
+void TypeEntry::setExtraIncludes(const IncludeList &includes)
+{
+ m_d->m_extraIncludes = includes;
+}
+
+void TypeEntry::addExtraInclude(const Include &newInclude)
+{
+ if (!m_d->m_extraIncludes.contains(newInclude))
+ m_d->m_extraIncludes.append(newInclude);
+}
+
+Include TypeEntry::include() const
+{
+ return m_d->m_include;
+}
+
+void TypeEntry::setInclude(const Include &inc)
+{
+ // This is a workaround for preventing double inclusion of the QSharedPointer implementation
+ // header, which does not use header guards. In the previous parser this was not a problem
+ // because the Q_QDOC define was set, and the implementation header was never included.
+ if (inc.name().endsWith(u"qsharedpointer_impl.h")) {
+ QString path = inc.name();
+ path.remove(u"_impl"_s);
+ m_d->m_include = Include(inc.type(), path);
+ } else {
+ m_d->m_include = inc;
+ }
+}
+
+QVersionNumber TypeEntry::version() const
+{
+ return m_d->m_version;
+}
+
+bool isCppPrimitive(const TypeEntryCPtr &e)
+{
+ if (!e->isPrimitive())
+ return false;
+
+ if (e->type() == TypeEntry::VoidType)
+ return true;
+
+ PrimitiveTypeEntryCPtr referencedType = basicReferencedTypeEntry(e);
+ const QString &typeName = referencedType->name();
+ return AbstractMetaType::cppPrimitiveTypes().contains(typeName);
+}
+
+TypeEntry::Type TypeEntry::type() const
+{
+ return m_d->m_type;
+}
+
+TypeEntryCPtr TypeEntry::parent() const
+{
+ return m_d->m_parent;
+}
+
+void TypeEntry::setParent(const TypeEntryCPtr &p)
+{
+ m_d->m_parent = p;
+}
+
+bool TypeEntry::isChildOf(const TypeEntryCPtr &p) const
+{
+ for (auto e = m_d->m_parent; e; e = e->parent()) {
+ if (e == p)
+ return true;
+ }
+ return false;
+}
+
+TypeSystemTypeEntryCPtr typeSystemTypeEntry(TypeEntryCPtr e)
+{
+ for (; e; e = e->parent()) {
+ if (e->type() == TypeEntry::TypeSystemType)
+ return std::static_pointer_cast<const TypeSystemTypeEntry>(e);
+ }
+ return {};
+}
+
+TypeEntryCPtr targetLangEnclosingEntry(const TypeEntryCPtr &e)
+{
+ auto result = e->parent();
+ while (result && result->type() != TypeEntry::TypeSystemType
+ && !NamespaceTypeEntry::isVisibleScope(result)) {
+ result = result->parent();
+ }
+ return result;
+}
+
+bool TypeEntry::isPrimitive() const
+{
+ return m_d->m_type == PrimitiveType;
+}
+
+bool TypeEntry::isEnum() const
+{
+ return m_d->m_type == EnumType;
+}
+
+bool TypeEntry::isFlags() const
+{
+ return m_d->m_type == FlagsType;
+}
+
+bool TypeEntry::isObject() const
+{
+ return m_d->m_type == ObjectType;
+}
+
+bool TypeEntry::isNamespace() const
+{
+ return m_d->m_type == NamespaceType;
+}
+
+bool TypeEntry::isContainer() const
+{
+ return m_d->m_type == ContainerType;
+}
+
+bool TypeEntry::isSmartPointer() const
+{
+ return m_d->m_type == SmartPointerType;
+}
+
+bool TypeEntry::isUniquePointer() const
+{
+ if (m_d->m_type != SmartPointerType)
+ return false;
+ auto *ste = static_cast<const SmartPointerTypeEntry *>(this);
+ return ste->smartPointerType() == TypeSystem::SmartPointerType::Unique;
+}
+
+bool TypeEntry::isArray() const
+{
+ return m_d->m_type == ArrayType;
+}
+
+bool TypeEntry::isTemplateArgument() const
+{
+ return m_d->m_type == TemplateArgumentType;
+}
+
+bool TypeEntry::isVoid() const
+{
+ return m_d->m_type == VoidType;
+}
+
+bool TypeEntry::isVarargs() const
+{
+ return m_d->m_type == VarargsType;
+}
+
+bool TypeEntry::isCustom() const
+{
+ return m_d->m_type == CustomType || m_d->m_type == PythonType;
+}
+
+bool TypeEntry::isTypeSystem() const
+{
+ return m_d->m_type == TypeSystemType;
+}
+
+bool TypeEntry::isFunction() const
+{
+ return m_d->m_type == FunctionType;
+}
+
+bool TypeEntry::isEnumValue() const
+{
+ return m_d->m_type == EnumValue;
+}
+
+bool TypeEntry::stream() const
+{
+ return m_d->m_stream;
+}
+
+void TypeEntry::setStream(bool b)
+{
+ m_d->m_stream = b;
+}
+
+bool TypeEntry::isBuiltIn() const
+{
+ return m_d->m_builtin;
+}
+
+void TypeEntry::setBuiltIn(bool b)
+{
+ m_d->m_builtin = b;
+}
+
+bool TypeEntry::isPrivate() const
+{
+ return m_d->m_private;
+}
+
+void TypeEntry::setPrivate(bool b)
+{
+ m_d->m_private = b;
+}
+
+QString TypeEntry::name() const
+{
+ return m_d->m_name;
+}
+
+// Build the C++ name excluding any inline namespaces
+// ("std::__1::shared_ptr" -> "std::shared_ptr"
+QString TypeEntryPrivate::shortName() const
+{
+ if (m_cachedShortName.isEmpty()) {
+ QVarLengthArray<TypeEntryCPtr > parents;
+ bool foundInlineNamespace = false;
+ for (auto p = m_parent; p != nullptr && p->type() != TypeEntry::TypeSystemType; p = p->parent()) {
+ if (p->type() == TypeEntry::NamespaceType
+ && std::static_pointer_cast<const NamespaceTypeEntry>(p)->isInlineNamespace()) {
+ foundInlineNamespace = true;
+ } else {
+ parents.append(p);
+ }
+ }
+ if (foundInlineNamespace) {
+ m_cachedShortName.reserve(m_name.size());
+ for (auto i = parents.size() - 1; i >= 0; --i) {
+ m_cachedShortName.append(parents.at(i)->entryName());
+ m_cachedShortName.append(u"::"_s);
+ }
+ m_cachedShortName.append(m_entryName);
+ } else {
+ m_cachedShortName = m_name;
+ }
+ }
+ return m_cachedShortName;
+}
+
+QString TypeEntry::shortName() const
+{
+ return m_d->shortName();
+}
+
+QString TypeEntry::entryName() const
+{
+ return m_d->m_entryName;
+}
+
+TypeEntry::CodeGeneration TypeEntry::codeGeneration() const
+{
+ return m_d->m_codeGeneration;
+}
+
+void TypeEntry::setCodeGeneration(TypeEntry::CodeGeneration cg)
+{
+ m_d->m_codeGeneration = cg;
+}
+
+bool TypeEntry::generateCode() const
+{
+ return m_d->m_codeGeneration == GenerateCode;
+}
+
+bool TypeEntry::shouldGenerate() const
+{
+ return generateCode() && NamespaceTypeEntry::isVisibleScope(this);
+}
+
+int TypeEntry::revision() const
+{
+ return m_d->m_revision;
+}
+
+void TypeEntry::setSbkIndex(int i)
+{
+ m_d->m_sbkIndex = i;
+}
+
+QString TypeEntry::qualifiedCppName() const
+{
+ return m_d->m_name;
+}
+
+CustomTypeEntryCPtr TypeEntry::targetLangApiType() const
+{
+ return m_d->m_targetLangApiType;
+}
+
+bool TypeEntry::hasTargetLangApiType() const
+{
+ return m_d->m_targetLangApiType != nullptr;
+}
+
+void TypeEntry::setTargetLangApiType(const CustomTypeEntryPtr &cte)
+{
+ m_d->m_targetLangApiType = cte;
+}
+
+QString TypeEntry::targetLangApiName() const
+{
+ return m_d->m_targetLangApiType != nullptr
+ ? m_d->m_targetLangApiType->name() : m_d->m_name;
+}
+
+QString TypeEntry::targetLangName() const
+{
+ if (m_d->m_cachedTargetLangName.isEmpty())
+ m_d->m_cachedTargetLangName = buildTargetLangName();
+ return m_d->m_cachedTargetLangName;
+}
+
+void TypeEntry::setTargetLangName(const QString &n)
+{
+ m_d->m_cachedTargetLangName = n;
+}
+
+QString TypeEntry::buildTargetLangName() const
+{
+ QString result = m_d->m_entryName;
+ for (auto p = parent(); p && p->type() != TypeEntry::TypeSystemType; p = p->parent()) {
+ if (NamespaceTypeEntry::isVisibleScope(p)) {
+ if (!result.isEmpty())
+ result.prepend(u'.');
+ QString n = p->m_d->m_entryName;
+ n.replace(u"::"_s, u"."_s); // Primitive types may have "std::"
+ result.prepend(n);
+ }
+ }
+ return result;
+}
+
+bool TypeEntry::setRevisionHelper(int r)
+{
+ const bool changed = m_d->m_revision != r;
+ m_d->m_revision = r;
+ return changed;
+}
+
+int TypeEntry::sbkIndexHelper() const
+{
+ return m_d->m_sbkIndex;
+}
+
+SourceLocation TypeEntry::sourceLocation() const
+{
+ return m_d->m_sourceLocation;
+}
+
+void TypeEntry::setSourceLocation(const SourceLocation &sourceLocation)
+{
+ m_d->m_sourceLocation = sourceLocation;
+}
+
+bool isUserPrimitive(const TypeEntryCPtr &e)
+{
+ if (!e->isPrimitive())
+ return false;
+ const auto type = basicReferencedTypeEntry(e);
+ return !isCppPrimitive(type)
+ && type->qualifiedCppName() != u"std::string";
+}
+
+bool TypeEntry::isWrapperType() const
+{
+ return isObject() || isValue() || isSmartPointer();
+}
+
+bool isCppIntegralPrimitive(const TypeEntryCPtr &e)
+{
+ if (!isCppPrimitive(e))
+ return false;
+ const auto type = basicReferencedTypeEntry(e);
+ return AbstractMetaType::cppIntegralTypes().contains(type->qualifiedCppName());
+}
+
+bool isExtendedCppPrimitive(const TypeEntryCPtr &e)
+{
+ if (isCppPrimitive(e))
+ return true;
+ if (!e->isPrimitive())
+ return false;
+ const auto type = basicReferencedTypeEntry(e);
+ const QString &name = type->qualifiedCppName();
+ return name == u"std::string" || name == u"std::wstring";
+}
+
+const TypeEntryPrivate *TypeEntry::d_func() const
+{
+ return m_d.data();
+}
+
+TypeEntryPrivate *TypeEntry::d_func()
+{
+ return m_d.data();
+}
+
+QString TypeEntry::targetLangEntryName() const
+{
+ if (m_d->m_cachedTargetLangEntryName.isEmpty()) {
+ m_d->m_cachedTargetLangEntryName = targetLangName();
+ const int lastDot = m_d->m_cachedTargetLangEntryName.lastIndexOf(u'.');
+ if (lastDot != -1)
+ m_d->m_cachedTargetLangEntryName.remove(0, lastDot + 1);
+ }
+ return m_d->m_cachedTargetLangEntryName;
+}
+
+QString TypeEntry::targetLangPackage() const
+{
+ return m_d->m_targetLangPackage;
+}
+
+void TypeEntry::setTargetLangPackage(const QString &p)
+{
+ m_d->m_targetLangPackage = p;
+}
+
+QString TypeEntry::qualifiedTargetLangName() const
+{
+ return targetLangPackage() + u'.' + targetLangName();
+}
+
+bool TypeEntry::isValue() const
+{
+ return false;
+}
+
+bool TypeEntry::isComplex() const
+{
+ return false;
+}
+
+TypeEntryPtr TypeEntry::viewOn() const
+{
+ return m_d->m_viewOn;
+}
+
+void TypeEntry::setViewOn(const TypeEntryPtr &v)
+{
+ m_d->m_viewOn = v;
+}
+
+TypeEntry *TypeEntry::clone() const
+{
+ return new TypeEntry(new TypeEntryPrivate(*m_d.data()));
+}
+
+// Take over parameters relevant for typedefs
+void TypeEntry::useAsTypedef(const TypeEntryCPtr &source)
+{
+ // XML Typedefs are in the global namespace for now.
+ m_d->m_parent = typeSystemTypeEntry(source);
+ m_d->m_entryName = source->m_d->m_entryName;
+ m_d->m_name = source->m_d->m_name;
+ m_d->m_targetLangPackage = source->m_d->m_targetLangPackage;
+ m_d->m_cachedTargetLangName.clear(); // Clear cached names.
+ m_d->m_cachedTargetLangEntryName.clear();
+ m_d->m_codeGeneration = source->m_d->m_codeGeneration;
+ m_d->m_version = source->m_d->m_version;
+}
+
+// ----------------- CustomTypeEntry
+class CustomTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ using TypeEntryPrivate::TypeEntryPrivate;
+
+ QString m_checkFunction;
+};
+
+CustomTypeEntry::CustomTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new CustomTypeEntryPrivate(entryName, CustomType, vr, parent))
+{
+}
+
+CustomTypeEntry::CustomTypeEntry(TypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+TypeEntry *CustomTypeEntry::clone() const
+{
+ S_D(const CustomTypeEntry);
+ return new CustomTypeEntry(new CustomTypeEntryPrivate(*d));
+}
+
+bool CustomTypeEntry::hasCheckFunction() const
+{
+ S_D(const CustomTypeEntry);
+ return !d->m_checkFunction.isEmpty();
+}
+
+QString CustomTypeEntry::checkFunction() const
+{
+ S_D(const CustomTypeEntry);
+ return d->m_checkFunction;
+}
+
+void CustomTypeEntry::setCheckFunction(const QString &f)
+{
+ S_D(CustomTypeEntry);
+ d->m_checkFunction = f;
+}
+
+// ----------------- PythonTypeEntry
+class PythonTypeEntryPrivate : public CustomTypeEntryPrivate
+{
+public:
+ using CustomTypeEntryPrivate::CustomTypeEntryPrivate;
+ explicit PythonTypeEntryPrivate(const QString &entryName,
+ const QString &checkFunction,
+ TypeSystem::CPythonType type) :
+ CustomTypeEntryPrivate(entryName, TypeEntry::PythonType, {}, {}),
+ m_cPythonType(type)
+ {
+ m_checkFunction = checkFunction;
+ }
+
+ TypeSystem::CPythonType m_cPythonType;
+};
+
+PythonTypeEntry::PythonTypeEntry(const QString &entryName,
+ const QString &checkFunction,
+ TypeSystem::CPythonType type) :
+ CustomTypeEntry(new PythonTypeEntryPrivate(entryName, checkFunction, type))
+{
+}
+
+TypeEntry *PythonTypeEntry::clone() const
+{
+ S_D(const PythonTypeEntry);
+ return new PythonTypeEntry(new PythonTypeEntryPrivate(*d));
+}
+
+TypeSystem::CPythonType PythonTypeEntry::cPythonType() const
+{
+ S_D(const PythonTypeEntry);
+ return d->m_cPythonType;
+}
+
+PythonTypeEntry::PythonTypeEntry(TypeEntryPrivate *d) :
+ CustomTypeEntry(d)
+{
+}
+
+// ----------------- TypeSystemTypeEntry
+class TypeSystemTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ using TypeEntryPrivate::TypeEntryPrivate;
+
+ CodeSnipList m_codeSnips;
+ TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Disabled;
+ QString m_subModuleOf;
+ QString m_namespaceBegin;
+ QString m_namespaceEnd;
+};
+
+TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new TypeSystemTypeEntryPrivate(entryName, TypeSystemType, vr, parent))
+{
+}
+
+TypeSystemTypeEntry::TypeSystemTypeEntry(TypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+TypeEntry *TypeSystemTypeEntry::clone() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return new TypeSystemTypeEntry(new TypeSystemTypeEntryPrivate(*d));
+}
+
+const CodeSnipList &TypeSystemTypeEntry::codeSnips() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_codeSnips;
+}
+
+CodeSnipList &TypeSystemTypeEntry::codeSnips()
+{
+ S_D(TypeSystemTypeEntry);
+ return d->m_codeSnips;
+}
+
+void TypeSystemTypeEntry::addCodeSnip(const CodeSnip &codeSnip)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_codeSnips.append(codeSnip);
+}
+
+QString TypeSystemTypeEntry::subModuleOf() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_subModuleOf;
+}
+
+void TypeSystemTypeEntry::setSubModule(const QString &s)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_subModuleOf = s;
+}
+
+const QString &TypeSystemTypeEntry::namespaceBegin() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_namespaceBegin;
+}
+
+void TypeSystemTypeEntry::setNamespaceBegin(const QString &p)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_namespaceBegin = p;
+}
+
+const QString &TypeSystemTypeEntry::namespaceEnd() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_namespaceEnd;
+}
+
+void TypeSystemTypeEntry::setNamespaceEnd(const QString &n)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_namespaceEnd = n;
+}
+
+TypeSystem::SnakeCase TypeSystemTypeEntry::snakeCase() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_snakeCase;
+}
+
+void TypeSystemTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_snakeCase = sc;
+}
+
+// ----------------- VoidTypeEntry
+VoidTypeEntry::VoidTypeEntry() :
+ TypeEntry(u"void"_s, VoidType, QVersionNumber(0, 0), nullptr)
+{
+}
+
+VoidTypeEntry::VoidTypeEntry(TypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+TypeEntry *VoidTypeEntry::clone() const
+{
+ return new VoidTypeEntry(new TypeEntryPrivate(*d_func()));
+}
+
+VarargsTypeEntry::VarargsTypeEntry() :
+ TypeEntry(u"..."_s, VarargsType, QVersionNumber(0, 0), nullptr)
+{
+}
+
+// ----------------- VarargsTypeEntry
+TypeEntry *VarargsTypeEntry::clone() const
+{
+ return new VarargsTypeEntry(new TypeEntryPrivate(*d_func()));
+}
+
+VarargsTypeEntry::VarargsTypeEntry(TypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+// ----------------- TemplateArgumentEntry
+class TemplateArgumentEntryPrivate : public TypeEntryPrivate
+{
+public:
+ using TypeEntryPrivate::TypeEntryPrivate;
+
+ int m_ordinal = 0;
+};
+
+TemplateArgumentEntry::TemplateArgumentEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new TemplateArgumentEntryPrivate(entryName, TemplateArgumentType, vr, parent))
+{
+}
+
+int TemplateArgumentEntry::ordinal() const
+{
+ S_D(const TemplateArgumentEntry);
+ return d->m_ordinal;
+}
+
+void TemplateArgumentEntry::setOrdinal(int o)
+{
+ S_D(TemplateArgumentEntry);
+ d->m_ordinal = o;
+}
+
+TypeEntry *TemplateArgumentEntry::clone() const
+{
+ S_D(const TemplateArgumentEntry);
+ return new TemplateArgumentEntry(new TemplateArgumentEntryPrivate(*d));
+}
+
+TemplateArgumentEntry::TemplateArgumentEntry(TemplateArgumentEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+// ----------------- ArrayTypeEntry
+class ArrayTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ explicit ArrayTypeEntryPrivate(const TypeEntryCPtr &nested_type, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntryPrivate(u"Array"_s, TypeEntry::ArrayType, vr, parent),
+ m_nestedType(nested_type)
+ {
+ }
+
+ TypeEntryCPtr m_nestedType;
+};
+
+ArrayTypeEntry::ArrayTypeEntry(const TypeEntryCPtr &nested_type, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new ArrayTypeEntryPrivate(nested_type, vr, parent))
+{
+ Q_ASSERT(nested_type);
+}
+
+void ArrayTypeEntry::setNestedTypeEntry(const TypeEntryPtr &nested)
+{
+ S_D(ArrayTypeEntry);
+ d->m_nestedType = nested;
+}
+
+TypeEntryCPtr ArrayTypeEntry::nestedTypeEntry() const
+{
+ S_D(const ArrayTypeEntry);
+ return d->m_nestedType;
+}
+
+QString ArrayTypeEntry::buildTargetLangName() const
+{
+ S_D(const ArrayTypeEntry);
+ return d->m_nestedType->targetLangName() + u"[]"_s;
+}
+
+TypeEntry *ArrayTypeEntry::clone() const
+{
+ S_D(const ArrayTypeEntry);
+ return new ArrayTypeEntry(new ArrayTypeEntryPrivate(*d));
+}
+
+ArrayTypeEntry::ArrayTypeEntry(ArrayTypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+// ----------------- PrimitiveTypeEntry
+class PrimitiveTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ PrimitiveTypeEntryPrivate(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntryPrivate(entryName, TypeEntry::PrimitiveType, vr, parent),
+ m_preferredTargetLangType(true)
+ {
+ }
+
+ QString m_defaultConstructor;
+ CustomConversionPtr m_customConversion;
+ PrimitiveTypeEntryPtr m_referencedTypeEntry;
+ uint m_preferredTargetLangType : 1;
+};
+
+PrimitiveTypeEntry::PrimitiveTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new PrimitiveTypeEntryPrivate(entryName, vr, parent))
+{
+}
+
+QString PrimitiveTypeEntry::defaultConstructor() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return d->m_defaultConstructor;
+}
+
+void PrimitiveTypeEntry::setDefaultConstructor(const QString &defaultConstructor)
+{
+ S_D(PrimitiveTypeEntry);
+ d->m_defaultConstructor = defaultConstructor;
+}
+
+bool PrimitiveTypeEntry::hasDefaultConstructor() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return !d->m_defaultConstructor.isEmpty();
+}
+
+PrimitiveTypeEntryPtr PrimitiveTypeEntry::referencedTypeEntry() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return d->m_referencedTypeEntry;
+}
+
+void PrimitiveTypeEntry::setReferencedTypeEntry(PrimitiveTypeEntryPtr referencedTypeEntry)
+{
+ S_D(PrimitiveTypeEntry);
+ d->m_referencedTypeEntry = referencedTypeEntry;
+}
+
+PrimitiveTypeEntryCPtr basicReferencedTypeEntry(const PrimitiveTypeEntryCPtr &e)
+{
+ auto result = e;
+ while (auto referenced = result->referencedTypeEntry())
+ result = referenced;
+ return result;
+}
+
+PrimitiveTypeEntryCPtr basicReferencedTypeEntry(const TypeEntryCPtr &e)
+{
+ Q_ASSERT(e->isPrimitive());
+ return basicReferencedTypeEntry(std::static_pointer_cast<const PrimitiveTypeEntry>(e));
+}
+
+PrimitiveTypeEntryCPtr basicReferencedNonBuiltinTypeEntry(const PrimitiveTypeEntryCPtr &e)
+{
+ auto result = e;
+ for (; result->referencedTypeEntry() ; result = result->referencedTypeEntry()) {
+ if (!result->isBuiltIn())
+ break;
+ }
+ return result;
+}
+
+bool PrimitiveTypeEntry::referencesType() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return d->m_referencedTypeEntry != nullptr;
+}
+
+bool PrimitiveTypeEntry::preferredTargetLangType() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return d->m_preferredTargetLangType;
+}
+
+void PrimitiveTypeEntry::setPreferredTargetLangType(bool b)
+{
+ S_D(PrimitiveTypeEntry);
+ d->m_preferredTargetLangType = b;
+}
+
+bool PrimitiveTypeEntry::hasCustomConversion() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return bool(d->m_customConversion);
+}
+
+void PrimitiveTypeEntry::setCustomConversion(const CustomConversionPtr &customConversion)
+{
+ S_D(PrimitiveTypeEntry);
+ d->m_customConversion = customConversion;
+}
+
+CustomConversionPtr PrimitiveTypeEntry::customConversion() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return d->m_customConversion;
+}
+
+TypeEntry *PrimitiveTypeEntry::clone() const
+{
+ S_D(const PrimitiveTypeEntry);
+ return new PrimitiveTypeEntry(new PrimitiveTypeEntryPrivate(*d));
+}
+
+PrimitiveTypeEntry::PrimitiveTypeEntry(PrimitiveTypeEntryPrivate *d)
+ : TypeEntry(d)
+{
+}
+
+// ----------------- ConfigurableTypeEntry
+
+class ConfigurableTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ using TypeEntryPrivate::TypeEntryPrivate;
+
+ QString m_configCondition;
+};
+
+ConfigurableTypeEntry::ConfigurableTypeEntry(const QString &entryName, Type t,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new ConfigurableTypeEntryPrivate(entryName, t, vr, parent))
+{
+}
+
+ConfigurableTypeEntry::ConfigurableTypeEntry(ConfigurableTypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+TypeEntry *ConfigurableTypeEntry::clone() const
+{
+ S_D(const ConfigurableTypeEntry);
+ return new ConfigurableTypeEntry(new ConfigurableTypeEntryPrivate(*d));
+}
+
+QString ConfigurableTypeEntry::configCondition() const
+{
+ S_D(const ConfigurableTypeEntry);
+ return d->m_configCondition;
+}
+
+void ConfigurableTypeEntry::setConfigCondition(const QString &c)
+{
+ S_D(ConfigurableTypeEntry);
+ d->m_configCondition = c;
+ if (!d->m_configCondition.startsWith(u'#'))
+ d->m_configCondition.prepend(u"#if ");
+}
+
+bool ConfigurableTypeEntry::hasConfigCondition() const
+{
+ S_D(const ConfigurableTypeEntry);
+ return !d->m_configCondition.isEmpty();
+}
+
+// ----------------- EnumTypeEntry
+class EnumTypeEntryPrivate : public ConfigurableTypeEntryPrivate
+{
+public:
+ using ConfigurableTypeEntryPrivate::ConfigurableTypeEntryPrivate;
+
+ EnumValueTypeEntryCPtr m_nullValue;
+ QStringList m_rejectedEnums;
+ FlagsTypeEntryPtr m_flags;
+ QString m_cppType;
+ QString m_docFile;
+ TypeSystem::PythonEnumType m_pythonEnumType = TypeSystem::PythonEnumType::Unspecified;
+};
+
+EnumTypeEntry::EnumTypeEntry(const QString &entryName,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ConfigurableTypeEntry(new EnumTypeEntryPrivate(entryName, EnumType, vr, parent))
+{
+}
+
+TypeSystem::PythonEnumType EnumTypeEntry::pythonEnumType() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_pythonEnumType;
+}
+
+void EnumTypeEntry::setPythonEnumType(TypeSystem::PythonEnumType t)
+{
+ S_D(EnumTypeEntry);
+ d->m_pythonEnumType = t;
+}
+
+QString EnumTypeEntry::targetLangQualifier() const
+{
+ const QString q = qualifier();
+ if (!q.isEmpty()) {
+ if (auto te = TypeDatabase::instance()->findType(q))
+ return te->targetLangName();
+ }
+ return q;
+}
+
+QString EnumTypeEntry::qualifier() const
+{
+ auto parentEntry = parent();
+ return parentEntry && parentEntry->type() != TypeEntry::TypeSystemType ?
+ parentEntry->name() : QString();
+}
+
+EnumValueTypeEntryCPtr EnumTypeEntry::nullValue() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_nullValue;
+}
+
+void EnumTypeEntry::setNullValue(const EnumValueTypeEntryCPtr &n)
+{
+ S_D(EnumTypeEntry);
+ d->m_nullValue = n;
+}
+
+void EnumTypeEntry::setFlags(const FlagsTypeEntryPtr &flags)
+{
+ S_D(EnumTypeEntry);
+ d->m_flags = flags;
+}
+
+FlagsTypeEntryPtr EnumTypeEntry::flags() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_flags;
+}
+
+QString EnumTypeEntry::cppType() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_cppType;
+}
+
+void EnumTypeEntry::setCppType(const QString &t)
+{
+ S_D(EnumTypeEntry);
+ d->m_cppType = t;
+}
+
+bool EnumTypeEntry::isEnumValueRejected(const QString &name) const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_rejectedEnums.contains(name);
+}
+
+void EnumTypeEntry::addEnumValueRejection(const QString &name)
+{
+ S_D(EnumTypeEntry);
+ d->m_rejectedEnums << name;
+}
+
+QStringList EnumTypeEntry::enumValueRejections() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_rejectedEnums;
+}
+
+QString EnumTypeEntry::docFile() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_docFile;
+}
+
+void EnumTypeEntry::setDocFile(const QString &df)
+{
+ S_D(EnumTypeEntry);
+ d->m_docFile = df;
+}
+
+TypeEntry *EnumTypeEntry::clone() const
+{
+ S_D(const EnumTypeEntry);
+ return new EnumTypeEntry(new EnumTypeEntryPrivate(*d));
+}
+
+EnumTypeEntry::EnumTypeEntry(EnumTypeEntryPrivate *d) :
+ ConfigurableTypeEntry(d)
+{
+}
+
+// ----------------- EnumValueTypeEntryPrivate
+class EnumValueTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ EnumValueTypeEntryPrivate(const QString &name, const QString &value,
+ const EnumTypeEntryCPtr &enclosingEnum,
+ bool isScopedEnum,
+ const QVersionNumber &vr) :
+ TypeEntryPrivate(name, TypeEntry::EnumValue, vr,
+ isScopedEnum ? enclosingEnum : enclosingEnum->parent()),
+ m_value(value),
+ m_enclosingEnum(enclosingEnum)
+ {
+ }
+
+ QString m_value;
+ EnumTypeEntryCPtr m_enclosingEnum;
+};
+
+EnumValueTypeEntry::EnumValueTypeEntry(const QString &name, const QString &value,
+ const EnumTypeEntryCPtr &enclosingEnum,
+ bool isScopedEnum,
+ const QVersionNumber &vr) :
+ TypeEntry(new EnumValueTypeEntryPrivate(name, value, enclosingEnum, isScopedEnum, vr))
+{
+}
+
+QString EnumValueTypeEntry::value() const
+{
+ S_D(const EnumValueTypeEntry);
+ return d->m_value;
+}
+
+EnumTypeEntryCPtr EnumValueTypeEntry::enclosingEnum() const
+{
+ S_D(const EnumValueTypeEntry);
+ return d->m_enclosingEnum;
+}
+
+TypeEntry *EnumValueTypeEntry::clone() const
+{
+ S_D(const EnumValueTypeEntry);
+ return new EnumValueTypeEntry(new EnumValueTypeEntryPrivate(*d));
+}
+
+EnumValueTypeEntry::EnumValueTypeEntry(EnumValueTypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+// ----------------- FlagsTypeEntry
+class FlagsTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ using TypeEntryPrivate::TypeEntryPrivate;
+
+ QString m_originalName;
+ QString m_flagsName;
+ EnumTypeEntryPtr m_enum;
+};
+
+FlagsTypeEntry::FlagsTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new FlagsTypeEntryPrivate(entryName, FlagsType, vr, parent))
+{
+}
+
+QString FlagsTypeEntry::buildTargetLangName() const
+{
+ S_D(const FlagsTypeEntry);
+ QString on = d->m_originalName;
+ on.replace(u"::"_s, u"."_s);
+ return on;
+}
+
+FlagsTypeEntry::FlagsTypeEntry(FlagsTypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+QString FlagsTypeEntry::originalName() const
+{
+ S_D(const FlagsTypeEntry);
+ return d->m_originalName;
+}
+
+void FlagsTypeEntry::setOriginalName(const QString &s)
+{
+ S_D(FlagsTypeEntry);
+ d->m_originalName = s;
+}
+
+QString FlagsTypeEntry::flagsName() const
+{
+ S_D(const FlagsTypeEntry);
+ return d->m_flagsName;
+}
+
+void FlagsTypeEntry::setFlagsName(const QString &name)
+{
+ S_D(FlagsTypeEntry);
+ d->m_flagsName = name;
+}
+
+EnumTypeEntryPtr FlagsTypeEntry::originator() const
+{
+ S_D(const FlagsTypeEntry);
+ return d->m_enum;
+}
+
+void FlagsTypeEntry::setOriginator(const EnumTypeEntryPtr &e)
+{
+ S_D(FlagsTypeEntry);
+ d->m_enum = e;
+}
+
+TypeEntry *FlagsTypeEntry::clone() const
+{
+ S_D(const FlagsTypeEntry);
+ return new FlagsTypeEntry(new FlagsTypeEntryPrivate(*d));
+}
+
+// ----------------- ConstantValueTypeEntry
+ConstantValueTypeEntry::ConstantValueTypeEntry(const QString& name,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(name, ConstantValueType, QVersionNumber(0, 0), parent)
+{
+}
+
+ConstantValueTypeEntry::ConstantValueTypeEntry(TypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+// ----------------- ComplexTypeEntry
+class ComplexTypeEntryPrivate : public ConfigurableTypeEntryPrivate
+{
+public:
+ ComplexTypeEntryPrivate(const QString &entryName, TypeEntry::Type t,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ConfigurableTypeEntryPrivate(entryName, t, vr, parent),
+ m_qualifiedCppName(buildName(entryName, parent)),
+ m_polymorphicBase(false),
+ m_genericClass(false),
+ m_deleteInMainThread(false)
+ {
+ }
+
+ AddedFunctionList m_addedFunctions;
+ FunctionModificationList m_functionMods;
+ CodeSnipList m_codeSnips;
+ DocModificationList m_docModifications;
+ DocModificationList m_functionDocModifications;
+ IncludeList m_argumentIncludes;
+ QSet<QString> m_generateFunctions;
+ FieldModificationList m_fieldMods;
+ QList<TypeSystemProperty> m_properties;
+ QList<TypeSystemPyMethodDefEntry> m_PyMethodDefEntrys;
+ QString m_defaultConstructor;
+ QString m_defaultSuperclass;
+ QString m_qualifiedCppName;
+
+ uint m_polymorphicBase : 1;
+ uint m_genericClass : 1;
+ uint m_deleteInMainThread : 1;
+
+ QString m_polymorphicIdValue;
+ QString m_polymorphicNameFunction;
+ QString m_targetType;
+ ComplexTypeEntry::TypeFlags m_typeFlags;
+ ComplexTypeEntry::CopyableFlag m_copyableFlag = ComplexTypeEntry::Unknown;
+ QString m_hashFunction;
+
+ ComplexTypeEntryCPtr m_baseContainerType;
+ // For class functions
+ TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified;
+ TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified;
+ TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Unspecified;
+ TypeSystem::BoolCast m_operatorBoolMode = TypeSystem::BoolCast::Unspecified;
+ TypeSystem::BoolCast m_isNullMode = TypeSystem::BoolCast::Unspecified;
+ TypeSystem::QtMetaTypeRegistration m_qtMetaTypeRegistration =
+ TypeSystem::QtMetaTypeRegistration::Unspecified;
+ // Determined by AbstractMetaBuilder from the code model.
+ bool m_isValueTypeWithCopyConstructorOnly = false;
+};
+
+ComplexTypeEntry::ComplexTypeEntry(const QString &entryName, TypeEntry::Type t,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ConfigurableTypeEntry(new ComplexTypeEntryPrivate(entryName, t, vr, parent))
+{
+}
+
+bool ComplexTypeEntry::isComplex() const
+{
+ return true;
+}
+
+void ComplexTypeEntry::setTypeFlags(TypeFlags flags)
+{
+ S_D(ComplexTypeEntry);
+ d->m_typeFlags = flags;
+}
+
+TypeSystem::BoolCast ComplexTypeEntry::operatorBoolMode() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_operatorBoolMode;
+}
+
+void ComplexTypeEntry::setOperatorBoolMode(TypeSystem::BoolCast b)
+{
+ S_D(ComplexTypeEntry);
+ d->m_operatorBoolMode = b;
+}
+
+TypeSystem::BoolCast ComplexTypeEntry::isNullMode() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_isNullMode;
+}
+
+void ComplexTypeEntry::setIsNullMode(TypeSystem::BoolCast b)
+{
+ S_D(ComplexTypeEntry);
+ d->m_isNullMode = b;
+}
+
+ComplexTypeEntry::TypeFlags ComplexTypeEntry::typeFlags() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_typeFlags;
+}
+
+FunctionModificationList ComplexTypeEntry::functionModifications() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_functionMods;
+}
+
+void ComplexTypeEntry::setFunctionModifications(const FunctionModificationList &functionModifications)
+{
+ S_D(ComplexTypeEntry);
+ d->m_functionMods = functionModifications;
+}
+
+void ComplexTypeEntry::addFunctionModification(const FunctionModification &functionModification)
+{
+ S_D(ComplexTypeEntry);
+ d->m_functionMods << functionModification;
+}
+
+FunctionModificationList
+ ComplexTypeEntry::functionModifications(const QStringList &signatures) const
+{
+ S_D(const ComplexTypeEntry);
+ FunctionModificationList lst;
+ for (const auto &mod : std::as_const(d->m_functionMods)) {
+ if (mod.matches(signatures))
+ lst << mod;
+ }
+ return lst;
+}
+
+const CodeSnipList &ComplexTypeEntry::codeSnips() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_codeSnips;
+}
+
+CodeSnipList &ComplexTypeEntry::codeSnips()
+{
+ S_D(ComplexTypeEntry);
+ return d->m_codeSnips;
+}
+
+void ComplexTypeEntry::setCodeSnips(const CodeSnipList &codeSnips)
+{
+ S_D(ComplexTypeEntry);
+ d->m_codeSnips = codeSnips;
+}
+
+void ComplexTypeEntry::addCodeSnip(const CodeSnip &codeSnip)
+{
+ S_D(ComplexTypeEntry);
+ d->m_codeSnips << codeSnip;
+}
+
+void ComplexTypeEntry::setDocModification(const DocModificationList &docMods)
+{
+ S_D(ComplexTypeEntry);
+ for (const auto &m : docMods) {
+ if (m.signature().isEmpty())
+ d->m_docModifications << m;
+ else
+ d->m_functionDocModifications << m;
+ }
+}
+
+DocModificationList ComplexTypeEntry::docModifications() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_docModifications;
+}
+
+DocModificationList ComplexTypeEntry::functionDocModifications() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_functionDocModifications;
+}
+
+const IncludeList &ComplexTypeEntry::argumentIncludes() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_argumentIncludes;
+}
+
+void ComplexTypeEntry::addArgumentInclude(const Include &newInclude)
+{
+ S_D(ComplexTypeEntry);
+ IncludeGroup::appendInclude(newInclude, &d->m_argumentIncludes);
+}
+
+AddedFunctionList ComplexTypeEntry::addedFunctions() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_addedFunctions;
+}
+
+void ComplexTypeEntry::setAddedFunctions(const AddedFunctionList &addedFunctions)
+{
+ S_D(ComplexTypeEntry);
+ d->m_addedFunctions = addedFunctions;
+}
+
+void ComplexTypeEntry::addNewFunction(const AddedFunctionPtr &addedFunction)
+{
+ S_D(ComplexTypeEntry);
+ d->m_addedFunctions << addedFunction;
+}
+
+const QList<TypeSystemPyMethodDefEntry> &ComplexTypeEntry::addedPyMethodDefEntrys() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_PyMethodDefEntrys;
+}
+
+void ComplexTypeEntry::addPyMethodDef(const TypeSystemPyMethodDefEntry &p)
+{
+ S_D(ComplexTypeEntry);
+ d->m_PyMethodDefEntrys.append(p);
+}
+
+const QSet<QString> &ComplexTypeEntry::generateFunctions() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_generateFunctions;
+}
+
+void ComplexTypeEntry::setGenerateFunctions(const QSet<QString> &f)
+{
+ S_D(ComplexTypeEntry);
+ d->m_generateFunctions = f;
+}
+
+void ComplexTypeEntry::setFieldModifications(const FieldModificationList &mods)
+{
+ S_D(ComplexTypeEntry);
+ d->m_fieldMods = mods;
+}
+
+FieldModificationList ComplexTypeEntry::fieldModifications() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_fieldMods;
+}
+
+const QList<TypeSystemProperty> &ComplexTypeEntry::properties() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_properties;
+}
+
+void ComplexTypeEntry::addProperty(const TypeSystemProperty &p)
+{
+ S_D(ComplexTypeEntry);
+ d->m_properties.append(p);
+}
+
+QString ComplexTypeEntry::defaultSuperclass() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_defaultSuperclass;
+}
+
+void ComplexTypeEntry::setDefaultSuperclass(const QString &sc)
+{
+ S_D(ComplexTypeEntry);
+ d->m_defaultSuperclass = sc;
+}
+
+QString ComplexTypeEntry::qualifiedCppName() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_qualifiedCppName;
+}
+
+void ComplexTypeEntry::setIsPolymorphicBase(bool on)
+{
+ S_D(ComplexTypeEntry);
+ d->m_polymorphicBase = on;
+}
+
+bool ComplexTypeEntry::isPolymorphicBase() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_polymorphicBase;
+}
+
+void ComplexTypeEntry::setPolymorphicIdValue(const QString &value)
+{
+ S_D(ComplexTypeEntry);
+ d->m_polymorphicIdValue = value;
+}
+
+QString ComplexTypeEntry::polymorphicIdValue() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_polymorphicIdValue;
+}
+
+QString ComplexTypeEntry::polymorphicNameFunction() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_polymorphicNameFunction;
+}
+
+void ComplexTypeEntry::setPolymorphicNameFunction(const QString &n)
+{
+ S_D(ComplexTypeEntry);
+ d->m_polymorphicNameFunction = n;
+}
+
+QString ComplexTypeEntry::targetType() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_targetType;
+}
+
+void ComplexTypeEntry::setTargetType(const QString &code)
+{
+ S_D(ComplexTypeEntry);
+ d->m_targetType = code;
+}
+
+bool ComplexTypeEntry::isGenericClass() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_genericClass;
+}
+
+void ComplexTypeEntry::setGenericClass(bool isGeneric)
+{
+ S_D(ComplexTypeEntry);
+ d->m_genericClass = isGeneric;
+}
+
+bool ComplexTypeEntry::deleteInMainThread() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_deleteInMainThread;
+}
+
+void ComplexTypeEntry::setDeleteInMainThread(bool dmt)
+{
+ S_D(ComplexTypeEntry);
+ d->m_deleteInMainThread = dmt;
+}
+
+ComplexTypeEntry::CopyableFlag ComplexTypeEntry::copyable() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_copyableFlag;
+}
+
+void ComplexTypeEntry::setCopyable(ComplexTypeEntry::CopyableFlag flag)
+{
+ S_D(ComplexTypeEntry);
+ d->m_copyableFlag = flag;
+}
+
+TypeSystem::QtMetaTypeRegistration ComplexTypeEntry::qtMetaTypeRegistration() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_qtMetaTypeRegistration;
+}
+
+void ComplexTypeEntry::setQtMetaTypeRegistration(TypeSystem::QtMetaTypeRegistration r)
+{
+ S_D(ComplexTypeEntry);
+ d->m_qtMetaTypeRegistration = r;
+}
+
+QString ComplexTypeEntry::hashFunction() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_hashFunction;
+}
+
+void ComplexTypeEntry::setHashFunction(const QString &hashFunction)
+{
+ S_D(ComplexTypeEntry);
+ d->m_hashFunction = hashFunction;
+}
+
+void ComplexTypeEntry::setBaseContainerType(const ComplexTypeEntryCPtr &baseContainer)
+{
+ S_D(ComplexTypeEntry);
+ d->m_baseContainerType = baseContainer;
+}
+
+ComplexTypeEntryCPtr ComplexTypeEntry::baseContainerType() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_baseContainerType;
+}
+
+TypeSystem::ExceptionHandling ComplexTypeEntry::exceptionHandling() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_exceptionHandling;
+}
+
+void ComplexTypeEntry::setExceptionHandling(TypeSystem::ExceptionHandling e)
+{
+ S_D(ComplexTypeEntry);
+ d->m_exceptionHandling = e;
+}
+
+TypeSystem::AllowThread ComplexTypeEntry::allowThread() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_allowThread;
+}
+
+void ComplexTypeEntry::setAllowThread(TypeSystem::AllowThread allowThread)
+{
+ S_D(ComplexTypeEntry);
+ d->m_allowThread = allowThread;
+}
+
+void ComplexTypeEntry::setDefaultConstructor(const QString& defaultConstructor)
+{
+ S_D(ComplexTypeEntry);
+ d->m_defaultConstructor = defaultConstructor;
+}
+
+QString ComplexTypeEntry::defaultConstructor() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_defaultConstructor;
+}
+
+bool ComplexTypeEntry::hasDefaultConstructor() const
+{
+ S_D(const ComplexTypeEntry);
+ return !d->m_defaultConstructor.isEmpty();
+}
+
+TypeSystem::SnakeCase ComplexTypeEntry::snakeCase() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_snakeCase;
+}
+
+void ComplexTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc)
+{
+ S_D(ComplexTypeEntry);
+ d->m_snakeCase = sc;
+}
+
+bool ComplexTypeEntry::isValueTypeWithCopyConstructorOnly() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_isValueTypeWithCopyConstructorOnly;
+}
+
+void ComplexTypeEntry::setValueTypeWithCopyConstructorOnly(bool v)
+{
+ S_D(ComplexTypeEntry);
+ d->m_isValueTypeWithCopyConstructorOnly = v;
+}
+
+// FIXME PYSIDE 7: Remove this and make "true" the default
+static bool parentManagementEnabled = false;
+
+bool ComplexTypeEntry::isParentManagementEnabled()
+{
+ return parentManagementEnabled;
+}
+
+void ComplexTypeEntry::setParentManagementEnabled(bool e)
+{
+ parentManagementEnabled = e;
+}
+
+TypeEntry *ComplexTypeEntry::clone() const
+{
+ S_D(const ComplexTypeEntry);
+ return new ComplexTypeEntry(new ComplexTypeEntryPrivate(*d));
+}
+
+// Take over parameters relevant for typedefs
+void ComplexTypeEntry::useAsTypedef(const ComplexTypeEntryCPtr &source)
+{
+ S_D(ComplexTypeEntry);
+ TypeEntry::useAsTypedef(source);
+ d->m_qualifiedCppName = source->qualifiedCppName();
+ d->m_targetType = source->targetType();
+ d->m_typeFlags.setFlag(ComplexTypeEntry::Typedef);
+}
+
+ComplexTypeEntry::ComplexTypeEntry(ComplexTypeEntryPrivate *d) :
+ ConfigurableTypeEntry(d)
+{
+}
+
+TypeEntry *ConstantValueTypeEntry::clone() const
+{
+ return new ConstantValueTypeEntry(new TypeEntryPrivate(*d_func()));
+}
+
+
+// ----------------- TypedefEntry
+/* A typedef entry allows for specifying template specializations in the
+ * typesystem XML file. */
+class TypedefEntryPrivate : public ComplexTypeEntryPrivate
+{
+public:
+ TypedefEntryPrivate(const QString &entryName,
+ const QString &sourceType,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntryPrivate(entryName, TypeEntry::TypedefType,
+ vr, parent),
+ m_sourceType(sourceType)
+ {
+ }
+
+ QString m_sourceType;
+ ComplexTypeEntryCPtr m_source;
+ ComplexTypeEntryPtr m_target;
+};
+
+TypedefEntry::TypedefEntry(const QString &entryName, const QString &sourceType,
+ const QVersionNumber &vr, const TypeEntryCPtr &parent) :
+ ComplexTypeEntry(new TypedefEntryPrivate(entryName, sourceType, vr, parent))
+{
+}
+
+QString TypedefEntry::sourceType() const
+{
+ S_D(const TypedefEntry);
+ return d->m_sourceType;
+}
+
+void TypedefEntry::setSourceType(const QString &s)
+{
+ S_D(TypedefEntry);
+ d->m_sourceType =s;
+}
+
+TypeEntry *TypedefEntry::clone() const
+{
+ S_D(const TypedefEntry);
+ return new TypedefEntry(new TypedefEntryPrivate(*d));
+}
+
+ComplexTypeEntryCPtr TypedefEntry::source() const
+{
+ S_D(const TypedefEntry);
+ return d->m_source;
+}
+
+void TypedefEntry::setSource(const ComplexTypeEntryCPtr &source)
+{
+ S_D(TypedefEntry);
+ d->m_source = source;
+}
+
+ComplexTypeEntryPtr TypedefEntry::target() const
+{
+ S_D(const TypedefEntry);
+ return d->m_target;
+}
+
+void TypedefEntry::setTarget(ComplexTypeEntryPtr target)
+{
+ S_D(TypedefEntry);
+ d->m_target = target;
+}
+
+TypedefEntry::TypedefEntry(TypedefEntryPrivate *d) :
+ ComplexTypeEntry(d)
+{
+}
+
+// ----------------- ContainerTypeEntry
+class ContainerTypeEntryPrivate : public ComplexTypeEntryPrivate
+{
+public:
+ ContainerTypeEntryPrivate(const QString &entryName,
+ ContainerTypeEntry::ContainerKind containerKind,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntryPrivate(entryName, TypeEntry::ContainerType, vr, parent),
+ m_containerKind(containerKind)
+ {
+ }
+
+ OpaqueContainers::const_iterator findOpaqueContainer(const QStringList &instantiations) const
+ {
+ return std::find_if(m_opaqueContainers.cbegin(), m_opaqueContainers.cend(),
+ [&instantiations](const OpaqueContainer &r) {
+ return r.instantiations == instantiations;
+ });
+ }
+
+ OpaqueContainers m_opaqueContainers;
+ CustomConversionPtr m_customConversion;
+ ContainerTypeEntry::ContainerKind m_containerKind;
+};
+
+QString OpaqueContainer::templateParameters() const
+{
+ QString result;
+ result += u'<';
+ for (qsizetype i = 0, size = instantiations.size(); i < size; ++i) {
+ if (i)
+ result += u',';
+ result += instantiations.at(i);
+ }
+ result += u'>';
+ return result;
+}
+
+ContainerTypeEntry::ContainerTypeEntry(const QString &entryName, ContainerKind containerKind,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntry(new ContainerTypeEntryPrivate(entryName, containerKind, vr, parent))
+{
+ setCodeGeneration(GenerateForSubclass);
+}
+
+ContainerTypeEntry::ContainerKind ContainerTypeEntry::containerKind() const
+{
+ S_D(const ContainerTypeEntry);
+ return d->m_containerKind;
+}
+
+qsizetype ContainerTypeEntry::templateParameterCount() const
+{
+ S_D(const ContainerTypeEntry);
+ qsizetype result = 1;
+ switch (d->m_containerKind) {
+ case MapContainer:
+ case MultiMapContainer:
+ case PairContainer:
+ case SpanContainer:
+ result = 2;
+ break;
+ case ListContainer:
+ case SetContainer:
+ break;
+ }
+ return result;
+}
+
+const OpaqueContainers &ContainerTypeEntry::opaqueContainers() const
+{
+ S_D(const ContainerTypeEntry);
+ return d->m_opaqueContainers;
+}
+
+void ContainerTypeEntry::appendOpaqueContainers(const OpaqueContainers &l)
+{
+ S_D(ContainerTypeEntry);
+ d->m_opaqueContainers.append(l);
+}
+
+bool ContainerTypeEntry::generateOpaqueContainer(const QStringList &instantiations) const
+{
+ S_D(const ContainerTypeEntry);
+ return d->findOpaqueContainer(instantiations) != d->m_opaqueContainers.cend();
+}
+
+QString ContainerTypeEntry::opaqueContainerName(const QStringList &instantiations) const
+{
+ S_D(const ContainerTypeEntry);
+ const auto it = d->findOpaqueContainer(instantiations);
+ return it != d->m_opaqueContainers.cend() ? it->name : QString{};
+}
+
+bool ContainerTypeEntry::hasCustomConversion() const
+{
+ S_D(const ContainerTypeEntry);
+ return bool(d->m_customConversion);
+}
+
+void ContainerTypeEntry::setCustomConversion(const CustomConversionPtr &customConversion)
+{
+ S_D(ContainerTypeEntry);
+ d->m_customConversion = customConversion;
+}
+
+CustomConversionPtr ContainerTypeEntry::customConversion() const
+{
+ S_D(const ContainerTypeEntry);
+ return d->m_customConversion;
+}
+
+TypeEntry *ContainerTypeEntry::clone() const
+{
+ S_D(const ContainerTypeEntry);
+ return new ContainerTypeEntry(new ContainerTypeEntryPrivate(*d));
+}
+
+ContainerTypeEntry::ContainerTypeEntry(ContainerTypeEntryPrivate *d) :
+ ComplexTypeEntry(d)
+{
+}
+
+// ----------------- SmartPointerTypeEntry
+class SmartPointerTypeEntryPrivate : public ComplexTypeEntryPrivate
+{
+public:
+ SmartPointerTypeEntryPrivate(const QString &entryName,
+ const QString &getterName,
+ TypeSystem::SmartPointerType type,
+ const QString &refCountMethodName,
+ const QVersionNumber &vr, const TypeEntryCPtr &parent) :
+ ComplexTypeEntryPrivate(entryName, TypeEntry::SmartPointerType, vr, parent),
+ m_getterName(getterName),
+ m_refCountMethodName(refCountMethodName),
+ m_smartPointerType(type)
+ {
+ }
+
+ qsizetype instantiationIndex(const TypeEntryCPtr &t) const;
+
+ QString m_getterName;
+ QString m_refCountMethodName;
+ QString m_valueCheckMethod;
+ QString m_nullCheckMethod;
+ QString m_resetMethod;
+ SmartPointerTypeEntry::Instantiations m_instantiations;
+ TypeSystem::SmartPointerType m_smartPointerType;
+};
+
+qsizetype SmartPointerTypeEntryPrivate::instantiationIndex(const TypeEntryCPtr &t) const
+{
+ for (qsizetype i = 0, size = m_instantiations.size(); i < size; ++i) {
+ if (m_instantiations.at(i).typeEntry == t)
+ return i;
+ }
+ return -1;
+}
+
+SmartPointerTypeEntry::SmartPointerTypeEntry(const QString &entryName,
+ const QString &getterName,
+ TypeSystem::SmartPointerType smartPointerType,
+ const QString &refCountMethodName,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntry(new SmartPointerTypeEntryPrivate(entryName, getterName, smartPointerType,
+ refCountMethodName, vr, parent))
+{
+}
+
+TypeSystem::SmartPointerType SmartPointerTypeEntry::smartPointerType() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_smartPointerType;
+}
+
+QString SmartPointerTypeEntry::getter() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_getterName;
+}
+
+QString SmartPointerTypeEntry::refCountMethodName() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_refCountMethodName;
+}
+
+QString SmartPointerTypeEntry::valueCheckMethod() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_valueCheckMethod;
+}
+
+void SmartPointerTypeEntry::setValueCheckMethod(const QString &m)
+{
+ S_D(SmartPointerTypeEntry);
+ d->m_valueCheckMethod = m;
+}
+
+QString SmartPointerTypeEntry::nullCheckMethod() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_nullCheckMethod;
+}
+
+void SmartPointerTypeEntry::setNullCheckMethod(const QString &f)
+{
+ S_D(SmartPointerTypeEntry);
+ d->m_nullCheckMethod = f;
+}
+
+QString SmartPointerTypeEntry::resetMethod() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_resetMethod;
+}
+
+void SmartPointerTypeEntry::setResetMethod(const QString &f)
+{
+ S_D(SmartPointerTypeEntry);
+ d->m_resetMethod = f;
+}
+
+TypeEntry *SmartPointerTypeEntry::clone() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return new SmartPointerTypeEntry(new SmartPointerTypeEntryPrivate(*d));
+}
+
+const SmartPointerTypeEntry::Instantiations &SmartPointerTypeEntry::instantiations() const
+{
+ S_D(const SmartPointerTypeEntry);
+ return d->m_instantiations;
+}
+
+void SmartPointerTypeEntry::setInstantiations(const Instantiations &i)
+{
+ S_D(SmartPointerTypeEntry);
+ d->m_instantiations = i;
+}
+
+SmartPointerTypeEntry::SmartPointerTypeEntry(SmartPointerTypeEntryPrivate *d) :
+ ComplexTypeEntry(d)
+{
+}
+
+bool SmartPointerTypeEntry::matchesInstantiation(const TypeEntryCPtr &e) const
+{
+ S_D(const SmartPointerTypeEntry);
+ // No instantiations specified, or match
+ return d->m_instantiations.isEmpty() || d->instantiationIndex(e) != -1;
+}
+
+static QString fixSmartPointerName(QString name)
+{
+ name.replace(u"::"_s, u"_"_s);
+ name.replace(u'<', u'_');
+ name.remove(u'>');
+ name.remove(u' ');
+ return name;
+}
+
+QString SmartPointerTypeEntry::getTargetName(const AbstractMetaType &metaType) const
+{
+ S_D(const SmartPointerTypeEntry);
+ auto instantiatedTe = metaType.instantiations().constFirst().typeEntry();
+ const auto index = d->instantiationIndex(instantiatedTe);
+ if (index != -1 && !d->m_instantiations.at(index).name.isEmpty())
+ return d->m_instantiations.at(index).name;
+
+ QString name = metaType.cppSignature();
+ const auto templatePos = name.indexOf(u'<');
+ if (templatePos != -1) { // "std::shared_ptr<A::B>" -> "shared_ptr<A::B>"
+ const auto colonPos = name.lastIndexOf(u"::"_s, templatePos);
+ if (colonPos != -1)
+ name.remove(0, colonPos + 2);
+ }
+ return fixSmartPointerName(name);
+}
+
+// ----------------- NamespaceTypeEntry
+class NamespaceTypeEntryPrivate : public ComplexTypeEntryPrivate
+{
+public:
+ using ComplexTypeEntryPrivate::ComplexTypeEntryPrivate;
+
+ QRegularExpression m_filePattern;
+ NamespaceTypeEntryCPtr m_extends;
+ TypeSystem::Visibility m_visibility = TypeSystem::Visibility::Auto;
+ bool m_hasPattern = false;
+ bool m_inlineNamespace = false;
+ bool m_generateUsing = true; // Whether to generate "using namespace" into wrapper
+};
+
+NamespaceTypeEntry::NamespaceTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntry(new NamespaceTypeEntryPrivate(entryName, NamespaceType, vr, parent))
+{
+}
+
+TypeEntry *NamespaceTypeEntry::clone() const
+{
+ S_D(const NamespaceTypeEntry);
+ return new NamespaceTypeEntry(new NamespaceTypeEntryPrivate(*d));
+}
+
+NamespaceTypeEntryCPtr NamespaceTypeEntry::extends() const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_extends;
+}
+
+void NamespaceTypeEntry::setExtends(const NamespaceTypeEntryCPtr &e)
+{
+ S_D(NamespaceTypeEntry);
+ d->m_extends = e;
+}
+
+const QRegularExpression &NamespaceTypeEntry::filePattern() const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_filePattern;
+}
+
+void NamespaceTypeEntry::setFilePattern(const QRegularExpression &r)
+{
+ S_D(NamespaceTypeEntry);
+ d->m_filePattern = r;
+ d->m_hasPattern = !d->m_filePattern.pattern().isEmpty();
+ if (d->m_hasPattern)
+ d->m_filePattern.optimize();
+}
+
+bool NamespaceTypeEntry::hasPattern() const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_hasPattern;
+}
+
+NamespaceTypeEntry::NamespaceTypeEntry(NamespaceTypeEntryPrivate *d) :
+ ComplexTypeEntry(d)
+{
+}
+
+bool NamespaceTypeEntry::matchesFile(const QString &needle) const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_filePattern.match(needle).hasMatch();
+}
+
+bool NamespaceTypeEntry::isVisible() const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_visibility == TypeSystem::Visibility::Visible
+ || (d->m_visibility == TypeSystem::Visibility::Auto && !d->m_inlineNamespace);
+}
+
+void NamespaceTypeEntry::setVisibility(TypeSystem::Visibility v)
+{
+ S_D(NamespaceTypeEntry);
+ d->m_visibility = v;
+}
+
+bool NamespaceTypeEntry::isInlineNamespace() const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_inlineNamespace;
+}
+
+void NamespaceTypeEntry::setInlineNamespace(bool i)
+{
+ S_D(NamespaceTypeEntry);
+ d->m_inlineNamespace = i;
+}
+
+bool NamespaceTypeEntry::isVisibleScope(const TypeEntryCPtr &e)
+{
+ return isVisibleScope(e.get());
+}
+
+bool NamespaceTypeEntry::isVisibleScope(const TypeEntry *e)
+{
+ return e->type() != TypeEntry::NamespaceType
+ || static_cast<const NamespaceTypeEntry *>(e)->isVisible();
+}
+
+bool NamespaceTypeEntry::generateUsing() const
+{
+ S_D(const NamespaceTypeEntry);
+ return d->m_generateUsing;
+}
+
+void NamespaceTypeEntry::setGenerateUsing(bool generateUsing)
+{
+ S_D(NamespaceTypeEntry);
+ d->m_generateUsing = generateUsing;
+}
+
+// ----------------- ValueTypeEntry
+
+class ValueTypeEntryPrivate : public ComplexTypeEntryPrivate
+{
+public:
+ using ComplexTypeEntryPrivate::ComplexTypeEntryPrivate;
+
+ QString m_targetConversionRule;
+ CustomConversionPtr m_customConversion;
+};
+
+ValueTypeEntry::ValueTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntry(new ValueTypeEntryPrivate(entryName, BasicValueType, vr, parent))
+{
+}
+
+bool ValueTypeEntry::hasCustomConversion() const
+{
+ S_D(const ValueTypeEntry);
+ return bool(d->m_customConversion);
+}
+
+void ValueTypeEntry::setCustomConversion(const CustomConversionPtr &customConversion)
+{
+ S_D(ValueTypeEntry);
+ d->m_customConversion = customConversion;
+}
+
+CustomConversionPtr ValueTypeEntry::customConversion() const
+{
+ S_D(const ValueTypeEntry);
+ return d->m_customConversion;
+}
+
+void ValueTypeEntry::setTargetConversionRule(const QString &conversionRule)
+{
+ S_D(ValueTypeEntry);
+ d->m_targetConversionRule = conversionRule;
+}
+
+QString ValueTypeEntry::targetConversionRule() const
+{
+ S_D(const ValueTypeEntry);
+ return d->m_targetConversionRule;
+}
+
+bool ValueTypeEntry::hasTargetConversionRule() const
+{
+ S_D(const ValueTypeEntry);
+ return !d->m_targetConversionRule.isEmpty();
+}
+
+bool ValueTypeEntry::isValue() const
+{
+ return true;
+}
+
+TypeEntry *ValueTypeEntry::clone() const
+{
+ S_D(const ValueTypeEntry);
+ return new ValueTypeEntry(new ValueTypeEntryPrivate(*d));
+}
+
+ValueTypeEntry::ValueTypeEntry(ComplexTypeEntryPrivate *d) :
+ ComplexTypeEntry(d)
+{
+}
+
+ValueTypeEntry::ValueTypeEntry(const QString &entryName, Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ ComplexTypeEntry(entryName, t, vr, parent)
+{
+}
+
+// ----------------- FunctionTypeEntry
+class FunctionTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ FunctionTypeEntryPrivate(const QString &entryName, const QString &signature,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntryPrivate(entryName, TypeEntry::FunctionType, vr, parent),
+ m_signatures(signature)
+ {
+ }
+
+ QStringList m_signatures;
+ QString m_docFile;
+};
+
+FunctionTypeEntry::FunctionTypeEntry(const QString &entryName, const QString &signature,
+ const QVersionNumber &vr,
+ const TypeEntryCPtr &parent) :
+ TypeEntry(new FunctionTypeEntryPrivate(entryName, signature, vr, parent))
+{
+}
+
+void FunctionTypeEntry::addSignature(const QString &signature)
+{
+ S_D(FunctionTypeEntry);
+ d->m_signatures << signature;
+}
+
+const QStringList &FunctionTypeEntry::signatures() const
+{
+ S_D(const FunctionTypeEntry);
+ return d->m_signatures;
+}
+
+bool FunctionTypeEntry::hasSignature(const QString &signature) const
+{
+ S_D(const FunctionTypeEntry);
+ return d->m_signatures.contains(signature);
+}
+
+QString FunctionTypeEntry::docFile() const
+{
+ S_D(const FunctionTypeEntry);
+ return d->m_docFile;
+}
+
+void FunctionTypeEntry::setDocFile(const QString &df)
+{
+ S_D(FunctionTypeEntry);
+ d->m_docFile = df;
+}
+
+TypeEntry *FunctionTypeEntry::clone() const
+{
+ S_D(const FunctionTypeEntry);
+ return new FunctionTypeEntry(new FunctionTypeEntryPrivate(*d));
+}
+
+FunctionTypeEntry::FunctionTypeEntry(FunctionTypeEntryPrivate *d) :
+ TypeEntry(d)
+{
+}
+
+// ----------------- ObjectTypeEntry
+ObjectTypeEntry::ObjectTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent)
+ : ComplexTypeEntry(entryName, ObjectType, vr, parent)
+{
+}
+
+TypeEntry *ObjectTypeEntry::clone() const
+{
+ S_D(const ComplexTypeEntry);
+ return new ObjectTypeEntry(new ComplexTypeEntryPrivate(*d));
+}
+
+ObjectTypeEntry::ObjectTypeEntry(ComplexTypeEntryPrivate *d) :
+ ComplexTypeEntry(d)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+#define FORMAT_BOOL(name, var) \
+ if (var) \
+ debug << ", [" << name << ']';
+
+#define FORMAT_NONEMPTY_STRING(name, var) \
+ if (!var.isEmpty()) \
+ debug << ", " << name << "=\"" << var << '"';
+
+#define FORMAT_LIST_SIZE(name, var) \
+ if (!var.isEmpty()) \
+ debug << ", " << var.size() << ' ' << name;
+
+void TypeEntry::formatDebug(QDebug &debug) const
+{
+ const QString cppName = qualifiedCppName();
+ debug << '"' << m_d->m_name << '"';
+ if (m_d->m_name != cppName)
+ debug << "\", cppName=\"" << cppName << '"';
+ debug << ", type=" << m_d->m_type << ", codeGeneration="
+ << m_d->m_codeGeneration;
+ const QString &targetName = targetLangName();
+ if (m_d->m_name != targetName)
+ debug << ", target=\"" << targetLangName() << '"';
+ FORMAT_NONEMPTY_STRING("package", m_d->m_targetLangPackage)
+ FORMAT_BOOL("stream", m_d->m_stream)
+ FORMAT_BOOL("built-in", m_d->m_builtin)
+ if (m_d->m_viewOn)
+ debug << ", views=" << m_d->m_viewOn->name();
+ if (!m_d->m_version.isNull() && m_d->m_version > QVersionNumber(0, 0))
+ debug << ", version=" << m_d->m_version;
+ if (m_d->m_revision)
+ debug << ", revision=" << m_d->m_revision;
+ if (m_d->m_sbkIndex)
+ debug << ", sbkIndex=" << m_d->m_sbkIndex;
+ if (m_d->m_include.isValid())
+ debug << ", include=" << m_d->m_include;
+ if (m_d->m_private)
+ debug << ", [private]";
+ formatList(debug, "extraIncludes", m_d->m_extraIncludes, ", ");
+}
+
+void PrimitiveTypeEntry::formatDebug(QDebug &debug) const
+{
+ TypeEntry::formatDebug(debug);
+ if (auto e = referencedTypeEntry()) {
+ debug << ", references";
+ for (; e ; e = e->referencedTypeEntry())
+ debug << ":\"" << e->qualifiedCppName() <<'"';
+ }
+}
+
+void ComplexTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const ComplexTypeEntry);
+
+ TypeEntry::formatDebug(debug);
+ FORMAT_BOOL("polymorphicBase", d->m_polymorphicBase)
+ FORMAT_BOOL("genericClass", d->m_genericClass)
+ FORMAT_BOOL("deleteInMainThread", d->m_deleteInMainThread)
+ if (d->m_typeFlags != 0)
+ debug << ", typeFlags=" << d->m_typeFlags;
+ debug << ", copyableFlag=" << d->m_copyableFlag
+ << ", except=" << int(d->m_exceptionHandling)
+ << ", snakeCase=" << int(d->m_snakeCase);
+ FORMAT_NONEMPTY_STRING("defaultSuperclass", d->m_defaultSuperclass)
+ FORMAT_NONEMPTY_STRING("polymorphicIdValue", d->m_polymorphicIdValue)
+ FORMAT_NONEMPTY_STRING("targetType", d->m_targetType)
+ FORMAT_NONEMPTY_STRING("hash", d->m_hashFunction)
+ FORMAT_LIST_SIZE("addedFunctions", d->m_addedFunctions)
+ formatList(debug, "functionMods", d->m_functionMods, ", ");
+ FORMAT_LIST_SIZE("codeSnips", d->m_codeSnips)
+ FORMAT_LIST_SIZE("fieldMods", d->m_fieldMods)
+}
+
+void CustomTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const CustomTypeEntry);
+ TypeEntry::formatDebug(debug);
+ debug << ", checkFunction=" << d->m_checkFunction;
+}
+
+void PythonTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const PythonTypeEntry);
+
+ CustomTypeEntry::formatDebug(debug);
+ debug << ", type=" << int(d->m_cPythonType);
+}
+
+void FunctionTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const FunctionTypeEntry);
+
+ TypeEntry::formatDebug(debug);
+ debug << "signatures=" << d->m_signatures;
+}
+
+void TypedefEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const TypedefEntry);
+
+ ComplexTypeEntry::formatDebug(debug);
+ debug << ", sourceType=\"" << d->m_sourceType << '"'
+ << ", source=" << d->m_source << ", target=" << d->m_target;
+}
+
+void EnumTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const EnumTypeEntry);
+
+ TypeEntry::formatDebug(debug);
+ if (d->m_pythonEnumType != TypeSystem::PythonEnumType::Unspecified)
+ debug << ", python-type=" << int(d->m_pythonEnumType);
+ if (d->m_flags)
+ debug << ", flags=(" << d->m_flags << ')';
+}
+
+void NamespaceTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const NamespaceTypeEntry);
+
+ ComplexTypeEntry::formatDebug(debug);
+ auto pattern = d->m_filePattern.pattern();
+ FORMAT_NONEMPTY_STRING("pattern", pattern)
+ debug << ",visibility=" << d->m_visibility;
+ if (d->m_inlineNamespace)
+ debug << "[inline]";
+}
+
+QDebug operator<<(QDebug d, const OpaqueContainer &oc)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "OpaqueContainer(\"" << oc.name << "\": " << oc.templateParameters() << ')';
+ return d;
+}
+
+void ContainerTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const ContainerTypeEntry);
+
+ ComplexTypeEntry::formatDebug(debug);
+ debug << ", type=" << d->m_containerKind << '"';
+ if (!d->m_opaqueContainers.isEmpty())
+ debug << ", opaque-containers=[" << d->m_opaqueContainers << ']';
+}
+
+void SmartPointerTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const SmartPointerTypeEntry);
+
+ ComplexTypeEntry::formatDebug(debug);
+ if (!d->m_instantiations.isEmpty()) {
+ debug << "type=" << d->m_type << ", instantiations["
+ << d->m_instantiations.size() << "]=(";
+ for (const auto &i : d->m_instantiations) {
+ debug << i.typeEntry->name() << ',';
+ if (!i.name.isEmpty())
+ debug << "=\"" << i.name << '"';
+ }
+ debug << ')';
+ }
+}
+
+QDebug operator<<(QDebug d, const TypeEntry *te)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "TypeEntry(";
+ if (te)
+ te->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const TypeEntryCPtr &te)
+{
+ d << te.get();
+ return d;
+}
+
+QDebug operator<<(QDebug d, const TemplateEntry *te)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "TemplateEntry(";
+ if (te) {
+ d << '"' << te->name() << '"';
+ } else {
+ d << '0';
+ }
+ d << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const TemplateEntryCPtr &te)
+{
+ d << te.get();
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/typesystem.h b/sources/shiboken6/ApiExtractor/typesystem.h
new file mode 100644
index 000000000..a2e4debc8
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystem.h
@@ -0,0 +1,215 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPESYSTEM_H
+#define TYPESYSTEM_H
+
+#include "include.h"
+#include "typesystem_typedefs.h"
+
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/QString>
+#include <QtCore/QScopedPointer>
+
+class AbstractMetaType;
+class CustomTypeEntry;
+class PrimitiveTypeEntry;
+class SourceLocation;
+class TypeSystemTypeEntry;
+
+class TypeEntryPrivate;
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QVersionNumber;
+QT_END_NAMESPACE
+
+class TypeEntry
+{
+ Q_GADGET
+public:
+ Q_DISABLE_COPY_MOVE(TypeEntry)
+
+ enum Type {
+ PrimitiveType,
+ VoidType,
+ VarargsType,
+ FlagsType,
+ EnumType,
+ EnumValue,
+ ConstantValueType,
+ TemplateArgumentType,
+ BasicValueType,
+ ContainerType,
+ ObjectType,
+ NamespaceType,
+ ArrayType,
+ TypeSystemType,
+ CustomType,
+ PythonType,
+ FunctionType,
+ SmartPointerType,
+ TypedefType
+ };
+ Q_ENUM(Type)
+
+ enum CodeGeneration {
+ GenerateNothing, // Rejection, private type, ConstantValueTypeEntry or similar
+ GenerationDisabled, // generate='no' in type system
+ GenerateCode, // Generate code
+ GenerateForSubclass, // Inherited from a loaded dependent type system.
+ };
+ Q_ENUM(CodeGeneration)
+
+ explicit TypeEntry(const QString &entryName, Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+ virtual ~TypeEntry();
+
+ Type type() const;
+
+ TypeEntryCPtr parent() const;
+ void setParent(const TypeEntryCPtr &);
+ bool isChildOf(const TypeEntryCPtr &p) const;
+
+ bool isPrimitive() const;
+ bool isEnum() const;
+ bool isFlags() const;
+ bool isObject() const;
+ bool isNamespace() const;
+ bool isContainer() const;
+ bool isSmartPointer() const;
+ bool isUniquePointer() const;
+ bool isArray() const;
+ bool isTemplateArgument() const;
+ bool isVoid() const;
+ bool isVarargs() const;
+ bool isCustom() const;
+ bool isTypeSystem() const;
+ bool isFunction() const;
+ bool isEnumValue() const;
+
+ bool stream() const;
+ void setStream(bool b);
+
+ bool isBuiltIn() const;
+ void setBuiltIn(bool b);
+
+ bool isPrivate() const;
+ void setPrivate(bool b);
+
+ // The type's name in C++, fully qualified
+ QString name() const;
+ // C++ excluding inline namespaces
+ QString shortName() const;
+ // Name as specified in XML
+ QString entryName() const;
+
+ CodeGeneration codeGeneration() const;
+ void setCodeGeneration(CodeGeneration cg);
+
+ // Returns true if code must be generated for this entry,
+ // it will return false in case of types coming from typesystems
+ // included for reference only.
+ // NOTE: 'GenerateForSubclass' means 'generate="no"'
+ // on 'load-typesystem' tag
+ bool generateCode() const;
+
+ /// Returns whether the C++ generators should generate this entry
+ bool shouldGenerate() const;
+
+ int revision() const;
+ void setRevision(int r); // see typedatabase.cpp
+ int sbkIndex() const; // see typedatabase.cpp
+ void setSbkIndex(int i);
+
+ virtual QString qualifiedCppName() const;
+
+ /// Its type's name in target language API.
+ /// The target language API name represents how this type is referred on
+ /// low level code for the target language. Examples: for Java this would
+ /// be a JNI name, for Python it should represent the CPython type name.
+ /// \return string representing the target language API name
+ /// Currently used only for PrimitiveTypeEntry (attribute "target").
+ CustomTypeEntryCPtr targetLangApiType() const;
+ bool hasTargetLangApiType() const;
+ void setTargetLangApiType(const CustomTypeEntryPtr &cte);
+ QString targetLangApiName() const;
+
+ // The type's name in TargetLang
+ QString targetLangName() const; // "Foo.Bar"
+ void setTargetLangName(const QString &n);
+ QString targetLangEntryName() const; // "Bar"
+
+ // The package
+ QString targetLangPackage() const;
+ void setTargetLangPackage(const QString &p);
+
+ QString qualifiedTargetLangName() const;
+
+ virtual bool isValue() const;
+ virtual bool isComplex() const;
+
+ const IncludeList &extraIncludes() const;
+ void setExtraIncludes(const IncludeList &includes);
+ void addExtraInclude(const Include &newInclude);
+
+ Include include() const;
+ void setInclude(const Include &inc);
+
+ QVersionNumber version() const;
+
+ // View on: Type to use for function argument conversion, fex
+ // std::string_view -> std::string for foo(std::string_view).
+ // cf AbstractMetaType::viewOn()
+ TypeEntryPtr viewOn() const;
+ void setViewOn(const TypeEntryPtr &v);
+
+ virtual TypeEntry *clone() const;
+
+ void useAsTypedef(const TypeEntryCPtr &source);
+
+ SourceLocation sourceLocation() const;
+ void setSourceLocation(const SourceLocation &sourceLocation);
+
+ // Query functions for generators
+ /// Returns true if the type passed has a Python wrapper for it.
+ /// Although namespace has a Python wrapper, it's not considered a type.
+ bool isWrapperType() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ virtual void formatDebug(QDebug &d) const;
+#endif
+
+protected:
+ explicit TypeEntry(TypeEntryPrivate *d);
+
+ const TypeEntryPrivate *d_func() const;
+ TypeEntryPrivate *d_func();
+
+ virtual QString buildTargetLangName() const;
+
+private:
+ bool setRevisionHelper(int r);
+ int sbkIndexHelper() const;
+ QScopedPointer<TypeEntryPrivate> m_d;
+};
+
+TypeSystemTypeEntryCPtr typeSystemTypeEntry(TypeEntryCPtr e);
+
+// cf AbstractMetaClass::targetLangEnclosingClass()
+TypeEntryCPtr targetLangEnclosingEntry(const TypeEntryCPtr &e);
+
+bool isCppPrimitive(const TypeEntryCPtr &e);
+
+/// Returns true if the type is a primitive but not a C++ primitive.
+bool isUserPrimitive(const TypeEntryCPtr &e);
+
+/// Returns true if the type is a C++ integral primitive,
+/// i.e. bool, char, int, long, and their unsigned counterparts.
+bool isCppIntegralPrimitive(const TypeEntryCPtr &e);
+
+/// Returns true if the type is an extended C++ primitive, a void*,
+/// a const char*, or a std::string (cf isCppPrimitive()).
+bool isExtendedCppPrimitive(const TypeEntryCPtr &e);
+
+#endif // TYPESYSTEM_H
diff --git a/sources/shiboken6/ApiExtractor/typesystem_enums.h b/sources/shiboken6/ApiExtractor/typesystem_enums.h
new file mode 100644
index 000000000..9ecbb08a1
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystem_enums.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPESYSTEM_ENUMS_H
+#define TYPESYSTEM_ENUMS_H
+
+namespace TypeSystem
+{
+enum Language {
+ TargetLangCode = 0x0001,
+ NativeCode = 0x0002,
+ ShellCode = 0x0004,
+
+ // masks
+ All = TargetLangCode | NativeCode | ShellCode,
+
+ TargetLangAndNativeCode = TargetLangCode | NativeCode
+};
+
+enum class AllowThread {
+ Unspecified,
+ Allow,
+ Disallow,
+ Auto
+};
+
+enum Ownership {
+ UnspecifiedOwnership,
+ DefaultOwnership,
+ TargetLangOwnership,
+ CppOwnership
+};
+
+enum CodeSnipPosition {
+ CodeSnipPositionBeginning,
+ CodeSnipPositionEnd,
+ CodeSnipPositionDeclaration,
+ CodeSnipPositionPyOverride,
+ CodeSnipPositionAny
+};
+
+enum DocModificationMode {
+ DocModificationAppend,
+ DocModificationPrepend,
+ DocModificationReplace,
+ DocModificationXPathReplace
+};
+
+enum class ExceptionHandling {
+ Unspecified,
+ Off,
+ AutoDefaultToOff,
+ AutoDefaultToOn,
+ On
+};
+
+enum class SnakeCase {
+ Unspecified,
+ Disabled,
+ Enabled,
+ Both
+};
+
+enum Visibility { // For namespaces
+ Unspecified,
+ Visible,
+ Invisible,
+ Auto
+};
+
+enum class BoolCast { // Generate nb_bool (overriding command line)
+ Unspecified,
+ Disabled,
+ Enabled
+};
+
+enum class CPythonType
+{
+ Bool,
+ Float,
+ Integer,
+ String,
+ Other
+};
+
+enum class QtMetaTypeRegistration
+{
+ Unspecified,
+ Enabled,
+ BaseEnabled, // Registration only for the base class of a hierarchy
+ Disabled
+};
+
+enum class SmartPointerType {
+ Shared,
+ Unique,
+ Handle,
+ ValueHandle
+};
+
+enum class PythonEnumType {
+ Unspecified,
+ Enum,
+ IntEnum,
+ Flag,
+ IntFlag
+};
+
+enum : int { OverloadNumberUnset = -1, OverloadNumberDefault = 99999 };
+
+} // namespace TypeSystem
+
+#endif // TYPESYSTEM_ENUMS_H
diff --git a/sources/shiboken6/ApiExtractor/typesystem_typedefs.h b/sources/shiboken6/ApiExtractor/typesystem_typedefs.h
new file mode 100644
index 000000000..5a4e12ff2
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystem_typedefs.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPESYSTEM_TYPEDEFS_H
+#define TYPESYSTEM_TYPEDEFS_H
+
+#include <QtCore/QList>
+
+#include <memory>
+
+class ArrayTypeEntry;
+class ComplexTypeEntry;
+class ConfigurableTypeEntry;
+class ConstantValueTypeEntry;
+class ContainerTypeEntry;
+class CustomTypeEntry;
+class EnumTypeEntry;
+class EnumValueTypeEntry;
+class FlagsTypeEntry;
+class FunctionTypeEntry;
+class NamespaceTypeEntry;
+class ObjectTypeEntry;
+class PrimitiveTypeEntry;
+class SmartPointerTypeEntry;
+class TemplateEntry;
+class TypeEntry;
+class TypedefEntry;
+class TypeSystemTypeEntry;
+class ValueTypeEntry;
+
+using ArrayTypeEntryPtr = std::shared_ptr<ArrayTypeEntry>;
+using ComplexTypeEntryPtr = std::shared_ptr<ComplexTypeEntry>;
+using ConfigurableTypeEntryPtr = std::shared_ptr<ConfigurableTypeEntry>;
+using ConstantValueTypeEntryPtr = std::shared_ptr<ConstantValueTypeEntry>;
+using ContainerTypeEntryPtr = std::shared_ptr<ContainerTypeEntry>;
+using CustomTypeEntryPtr = std::shared_ptr<CustomTypeEntry>;
+using EnumTypeEntryPtr = std::shared_ptr<EnumTypeEntry>;
+using EnumValueTypeEntryPtr = std::shared_ptr<EnumValueTypeEntry>;
+using FlagsTypeEntryPtr = std::shared_ptr<FlagsTypeEntry>;
+using FunctionTypeEntryPtr = std::shared_ptr<FunctionTypeEntry>;
+using NamespaceTypeEntryPtr = std::shared_ptr<NamespaceTypeEntry>;
+using ObjectTypeEntryPtr = std::shared_ptr<ObjectTypeEntry>;
+using PrimitiveTypeEntryPtr = std::shared_ptr<PrimitiveTypeEntry>;
+using SmartPointerTypeEntryPtr = std::shared_ptr<SmartPointerTypeEntry>;
+using TemplateEntryPtr = std::shared_ptr<TemplateEntry>;
+using TypeEntryPtr = std::shared_ptr<TypeEntry>;
+using TypedefEntryPtr = std::shared_ptr<TypedefEntry>;
+using TypeSystemTypeEntryPtr = std::shared_ptr<TypeSystemTypeEntry>;
+using ValueTypeEntryPtr = std::shared_ptr<ValueTypeEntry>;
+
+using ArrayTypeEntryCPtr = std::shared_ptr<const ArrayTypeEntry>;
+using ComplexTypeEntryCPtr = std::shared_ptr<const ComplexTypeEntry>;
+using ConstantValueTypeEntryCPtr = std::shared_ptr<const ConstantValueTypeEntry>;
+using ConfigurableTypeEntryCPtr = std::shared_ptr<const ConfigurableTypeEntry>;
+using ContainerTypeEntryCPtr = std::shared_ptr<const ContainerTypeEntry>;
+using CustomTypeEntryCPtr = std::shared_ptr<const CustomTypeEntry>;
+using EnumTypeEntryCPtr = std::shared_ptr<const EnumTypeEntry>;
+using EnumValueTypeEntryCPtr = std::shared_ptr<const EnumValueTypeEntry>;
+using FlagsTypeEntryCPtr = std::shared_ptr<const FlagsTypeEntry>;
+using FunctionTypeEntryCPtr = std::shared_ptr<const FunctionTypeEntry>;
+using NamespaceTypeEntryCPtr = std::shared_ptr<const NamespaceTypeEntry>;
+using ObjectTypeEntryCPtr = std::shared_ptr<const ObjectTypeEntry>;
+using PrimitiveTypeEntryCPtr = std::shared_ptr<const PrimitiveTypeEntry>;
+using SmartPointerTypeEntryCPtr = std::shared_ptr<const SmartPointerTypeEntry>;
+using TemplateEntryCPtr = std::shared_ptr<const TemplateEntry>;
+using TypeEntryCPtr = std::shared_ptr<const TypeEntry>;
+using TypedefEntryCPtr = std::shared_ptr<const TypedefEntry>;
+using TypeSystemTypeEntryCPtr = std::shared_ptr<const TypeSystemTypeEntry>;
+using ValueTypeEntryCPtr = std::shared_ptr<const ValueTypeEntry>;
+
+using ComplexTypeEntryCList = QList<ComplexTypeEntryCPtr>;
+using ContainerTypeEntryCList = QList<ContainerTypeEntryCPtr>;
+using NamespaceTypeEntryList = QList<NamespaceTypeEntryPtr>;
+using PrimitiveTypeEntryCList = QList<PrimitiveTypeEntryCPtr>;
+using SmartPointerTypeEntryList = QList<SmartPointerTypeEntryCPtr>;
+using TypeEntryList = QList<TypeEntryPtr>;
+using TypeEntryCList = QList<TypeEntryCPtr>;
+
+#endif // TYPESYSTEM_TYPEDEFS_H
diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
new file mode 100644
index 000000000..2b686e997
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
@@ -0,0 +1,3648 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "typesystemparser_p.h"
+#include "anystringview_helpers.h"
+#include "addedfunction.h"
+#include "codesnip.h"
+#include "enumtypeentry.h"
+#include "containertypeentry.h"
+#include "customconversion.h"
+#include "customtypenentry.h"
+#include "flagstypeentry.h"
+#include "functiontypeentry.h"
+#include "namespacetypeentry.h"
+#include "objecttypeentry.h"
+#include "primitivetypeentry.h"
+#include "smartpointertypeentry.h"
+#include "typedefentry.h"
+#include "typesystemtypeentry.h"
+#include "valuetypeentry.h"
+#include "modifications.h"
+#include "typedatabase.h"
+#include "messages.h"
+#include "reporthandler.h"
+#include "sourcelocation.h"
+#include "conditionalstreamreader.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QSet>
+#include <QtCore/QStringView>
+#include <QtCore/QStringAlgorithms>
+#include <QtCore/QVersionNumber>
+#include <QtCore/QXmlStreamAttributes>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamEntityResolver>
+
+#include <algorithm>
+#include <optional>
+#include <memory>
+
+using namespace Qt::StringLiterals;
+
+constexpr auto allowThreadAttribute = "allow-thread"_L1;
+constexpr auto checkFunctionAttribute = "check-function"_L1;
+constexpr auto copyableAttribute = "copyable"_L1;
+constexpr auto accessAttribute = "access"_L1;
+constexpr auto actionAttribute = "action"_L1;
+constexpr auto quoteAfterLineAttribute = "quote-after-line"_L1;
+constexpr auto quoteBeforeLineAttribute = "quote-before-line"_L1;
+constexpr auto textAttribute = "text"_L1;
+constexpr auto nameAttribute = "name"_L1;
+constexpr auto sinceAttribute = "since"_L1;
+constexpr auto untilAttribute = "until"_L1;
+constexpr auto defaultSuperclassAttribute = "default-superclass"_L1;
+constexpr auto deleteInMainThreadAttribute = "delete-in-main-thread"_L1;
+constexpr auto deprecatedAttribute = "deprecated"_L1;
+constexpr auto disableWrapperAttribute = "disable-wrapper"_L1;
+constexpr auto docFileAttribute = "doc-file"_L1;
+constexpr auto exceptionHandlingAttribute = "exception-handling"_L1;
+constexpr auto extensibleAttribute = "extensible"_L1;
+constexpr auto fileNameAttribute = "file-name"_L1;
+constexpr auto fileAttribute = "file"_L1;
+constexpr auto flagsAttribute = "flags"_L1;
+constexpr auto forceAbstractAttribute = "force-abstract"_L1;
+constexpr auto forceIntegerAttribute = "force-integer"_L1;
+constexpr auto formatAttribute = "format"_L1;
+constexpr auto generateUsingAttribute = "generate-using"_L1;
+constexpr auto generateFunctionsAttribute = "generate-functions"_L1;
+constexpr auto classAttribute = "class"_L1;
+constexpr auto generateAttribute = "generate"_L1;
+constexpr auto generateGetSetDefAttribute = "generate-getsetdef"_L1;
+constexpr auto genericClassAttribute = "generic-class"_L1;
+constexpr auto indexAttribute = "index"_L1;
+constexpr auto invalidateAfterUseAttribute = "invalidate-after-use"_L1;
+constexpr auto isNullAttribute = "isNull"_L1;
+constexpr auto locationAttribute = "location"_L1;
+constexpr auto modifiedTypeAttribute = "modified-type"_L1;
+constexpr auto opaqueContainerAttribute = "opaque-containers"_L1;
+constexpr auto operatorBoolAttribute = "operator-bool"_L1;
+constexpr auto parentManagementAttribute = "parent-management"_L1;
+constexpr auto pyiTypeAttribute = "pyi-type"_L1;
+constexpr auto overloadNumberAttribute = "overload-number"_L1;
+constexpr auto ownershipAttribute = "owner"_L1;
+constexpr auto packageAttribute = "package"_L1;
+constexpr auto polymorphicBaseAttribute = "polymorphic-base"_L1;
+constexpr auto positionAttribute = "position"_L1;
+constexpr auto preferredConversionAttribute = "preferred-conversion"_L1;
+constexpr auto preferredTargetLangTypeAttribute = "preferred-target-lang-type"_L1;
+constexpr auto pythonEnumTypeAttribute = "python-type"_L1;
+constexpr auto pythonOverrideAttribute = "python-override"_L1;
+constexpr auto cppEnumTypeAttribute = "cpp-type"_L1;
+constexpr auto qtMetaObjectFunctionsAttribute = "qt-metaobject"_L1;
+constexpr auto qtMetaTypeAttribute = "qt-register-metatype"_L1;
+constexpr auto removeAttribute = "remove"_L1;
+constexpr auto renameAttribute = "rename"_L1;
+constexpr auto readAttribute = "read"_L1;
+constexpr auto targetLangNameAttribute = "target-lang-name"_L1;
+constexpr auto writeAttribute = "write"_L1;
+constexpr auto opaqueContainerFieldAttribute = "opaque-container"_L1;
+constexpr auto replaceAttribute = "replace"_L1;
+constexpr auto toAttribute = "to"_L1;
+constexpr auto signatureAttribute = "signature"_L1;
+constexpr auto snippetAttribute = "snippet"_L1;
+constexpr auto snakeCaseAttribute = "snake-case"_L1;
+constexpr auto staticAttribute = "static"_L1;
+constexpr auto classmethodAttribute = "classmethod"_L1;
+constexpr auto threadAttribute = "thread"_L1;
+constexpr auto sourceAttribute = "source"_L1;
+constexpr auto streamAttribute = "stream"_L1;
+constexpr auto privateAttribute = "private"_L1;
+constexpr auto xPathAttribute = "xpath"_L1;
+constexpr auto virtualSlotAttribute = "virtual-slot"_L1;
+constexpr auto visibleAttribute = "visible"_L1;
+constexpr auto enumIdentifiedByValueAttribute = "identified-by-value"_L1;
+constexpr auto subModuleOfAttribute = "submodule-of"_L1;
+
+constexpr auto noAttributeValue = "no"_L1;
+constexpr auto yesAttributeValue = "yes"_L1;
+constexpr auto trueAttributeValue = "true"_L1;
+constexpr auto falseAttributeValue = "false"_L1;
+
+static bool isTypeEntry(StackElement el)
+{
+ return el >= StackElement::FirstTypeEntry && el <= StackElement::LastTypeEntry;
+}
+
+static bool isComplexTypeEntry(StackElement el)
+{
+ return el >= StackElement::FirstTypeEntry && el <= StackElement::LastComplexTypeEntry;
+}
+
+static bool isDocumentation(StackElement el)
+{
+ return el >= StackElement::FirstDocumentation && el <= StackElement::LastDocumentation;
+}
+
+static QList<CustomConversionPtr> customConversionsForReview;
+
+// Set a regular expression for rejection from text. By legacy, those are fixed
+// strings, except for '*' meaning 'match all'. Enclosing in "^..$"
+// indicates regular expression.
+static bool setRejectionRegularExpression(const QString &patternIn,
+ QRegularExpression *re,
+ QString *errorMessage)
+{
+ QString pattern;
+ if (patternIn.startsWith(u'^') && patternIn.endsWith(u'$'))
+ pattern = patternIn;
+ else if (patternIn == u"*")
+ pattern = "^.*$"_L1;
+ else
+ pattern = u'^' + QRegularExpression::escape(patternIn) + u'$';
+ re->setPattern(pattern);
+ if (!re->isValid()) {
+ *errorMessage = msgInvalidRegularExpression(patternIn, re->errorString());
+ return false;
+ }
+ return true;
+}
+
+static inline bool hasFileSnippetAttributes(const QXmlStreamAttributes *attributes)
+{
+ return attributes->hasAttribute(fileAttribute);
+}
+
+// Extract a snippet from a file within annotation "// @snippet label".
+std::optional<QString>
+ extractSnippet(const QString &code, const QString &snippetLabel)
+{
+ if (snippetLabel.isEmpty())
+ return code;
+ const QString pattern = R"(^\s*//\s*@snippet\s+)"_L1
+ + QRegularExpression::escape(snippetLabel)
+ + R"(\s*$)"_L1;
+ const QRegularExpression snippetRe(pattern);
+ Q_ASSERT(snippetRe.isValid());
+
+ bool useLine = false;
+ bool foundLabel = false;
+ QString result;
+ const auto lines = QStringView{code}.split(u'\n');
+ for (const auto &line : lines) {
+ if (snippetRe.matchView(line).hasMatch()) {
+ foundLabel = true;
+ useLine = !useLine;
+ if (!useLine)
+ break; // End of snippet reached
+ } else if (useLine)
+ result += line.toString() + u'\n';
+ }
+ if (!foundLabel)
+ return {};
+ return CodeSnipAbstract::fixSpaces(result);
+}
+
+template <class EnumType>
+struct EnumLookup
+{
+ QStringView name;
+ EnumType value;
+};
+
+// Helper macros to define lookup functions that take a QStringView needle
+// and an optional default return value.
+#define ENUM_LOOKUP_BEGIN(EnumType, caseSensitivity, functionName) \
+static std::optional<EnumType> functionName(QStringView needle) \
+{ \
+ using HaystackEntry = EnumLookup<EnumType>; \
+ constexpr auto cs = caseSensitivity; \
+ static constexpr HaystackEntry haystack[] =
+
+#define ENUM_LOOKUP_LINEAR_SEARCH \
+ auto pred = [cs, needle](const HaystackEntry &he) { \
+ return he.name.compare(needle, cs) == 0; \
+ }; \
+ auto end = std::cend(haystack); \
+ auto it = std::find_if(std::cbegin(haystack), end, pred); \
+ if (it != end) \
+ return it->value; \
+ return std::nullopt; \
+}
+
+ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive,
+ allowThreadFromAttribute)
+ {
+ {u"yes", TypeSystem::AllowThread::Allow},
+ {u"true", TypeSystem::AllowThread::Allow},
+ {u"auto", TypeSystem::AllowThread::Auto},
+ {u"no", TypeSystem::AllowThread::Disallow},
+ {u"false", TypeSystem::AllowThread::Disallow},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+
+ENUM_LOOKUP_BEGIN(TypeSystem::BoolCast, Qt::CaseInsensitive,
+ boolCastFromAttribute)
+ {
+ {u"yes", TypeSystem::BoolCast::Enabled},
+ {u"true", TypeSystem::BoolCast::Enabled},
+ {u"no", TypeSystem::BoolCast::Disabled},
+ {u"false", TypeSystem::BoolCast::Disabled},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::PythonEnumType, Qt::CaseSensitive,
+ pythonEnumTypeFromAttribute)
+ {
+ {u"Enum", TypeSystem::PythonEnumType::Enum},
+ {u"IntEnum", TypeSystem::PythonEnumType::IntEnum},
+ {u"Flag", TypeSystem::PythonEnumType::Flag},
+ {u"IntFlag", TypeSystem::PythonEnumType::IntFlag},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::QtMetaTypeRegistration, Qt::CaseSensitive,
+ qtMetaTypeFromAttribute)
+ {
+ {u"yes", TypeSystem::QtMetaTypeRegistration::Enabled},
+ {u"true", TypeSystem::QtMetaTypeRegistration::Enabled},
+ {u"base", TypeSystem::QtMetaTypeRegistration::BaseEnabled},
+ {u"no", TypeSystem::QtMetaTypeRegistration::Disabled},
+ {u"false", TypeSystem::QtMetaTypeRegistration::Disabled},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive,
+ languageFromAttribute)
+ {
+ {u"all", TypeSystem::All}, // sorted!
+ {u"native", TypeSystem::NativeCode}, // em algum lugar do cpp
+ {u"shell", TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe
+ {u"target", TypeSystem::TargetLangCode} // em algum lugar do cpp
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive,
+ ownershipFromFromAttribute)
+ {
+ {u"target", TypeSystem::TargetLangOwnership},
+ {u"c++", TypeSystem::CppOwnership},
+ {u"default", TypeSystem::DefaultOwnership}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive,
+ addedFunctionAccessFromAttribute)
+ {
+ {u"public", AddedFunction::Public},
+ {u"protected", AddedFunction::Protected},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(FunctionModification::ModifierFlag, Qt::CaseSensitive,
+ modifierFromAttribute)
+ {
+ {u"private", FunctionModification::Private},
+ {u"public", FunctionModification::Public},
+ {u"protected", FunctionModification::Protected},
+ {u"rename", FunctionModification::Rename},
+ {u"final", FunctionModification::Final},
+ {u"non-final", FunctionModification::NonFinal}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive,
+ referenceCountFromAttribute)
+ {
+ {u"add", ReferenceCount::Add},
+ {u"add-all", ReferenceCount::AddAll},
+ {u"remove", ReferenceCount::Remove},
+ {u"set", ReferenceCount::Set},
+ {u"ignore", ReferenceCount::Ignore}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive,
+ argumentOwnerActionFromAttribute)
+ {
+ {u"add", ArgumentOwner::Add},
+ {u"remove", ArgumentOwner::Remove}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive,
+ codeSnipPositionFromAttribute)
+ {
+ {u"beginning", TypeSystem::CodeSnipPositionBeginning},
+ {u"end", TypeSystem::CodeSnipPositionEnd},
+ {u"declaration", TypeSystem::CodeSnipPositionDeclaration},
+ {u"override", TypeSystem::CodeSnipPositionPyOverride}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive,
+ locationFromAttribute)
+ {
+ {u"global", Include::IncludePath},
+ {u"local", Include::LocalPath},
+ {u"target", Include::TargetLangImport}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive,
+ docModificationFromAttribute)
+ {
+ {u"append", TypeSystem::DocModificationAppend},
+ {u"prepend", TypeSystem::DocModificationPrepend},
+ {u"replace", TypeSystem::DocModificationReplace}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(ContainerTypeEntry::ContainerKind, Qt::CaseSensitive,
+ containerTypeFromAttribute)
+ {
+ {u"list", ContainerTypeEntry::ListContainer},
+ {u"string-list", ContainerTypeEntry::ListContainer},
+ {u"linked-list", ContainerTypeEntry::ListContainer},
+ {u"vector", ContainerTypeEntry::ListContainer},
+ {u"stack", ContainerTypeEntry::ListContainer},
+ {u"queue", ContainerTypeEntry::ListContainer},
+ {u"set", ContainerTypeEntry::SetContainer},
+ {u"map", ContainerTypeEntry::MapContainer},
+ {u"multi-map", ContainerTypeEntry::MultiMapContainer},
+ {u"hash", ContainerTypeEntry::MapContainer},
+ {u"multi-hash", ContainerTypeEntry::MultiMapContainer},
+ {u"pair", ContainerTypeEntry::PairContainer},
+ {u"span", ContainerTypeEntry::SpanContainer}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive,
+ typeRejectionFromAttribute)
+ {
+ {u"class", TypeRejection::ExcludeClass},
+ {u"function-name", TypeRejection::Function},
+ {u"field-name", TypeRejection::Field},
+ {u"enum-name", TypeRejection::Enum },
+ {u"argument-type", TypeRejection::ArgumentType},
+ {u"return-type", TypeRejection::ReturnType}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive,
+ exceptionHandlingFromAttribute)
+{
+ {u"no", TypeSystem::ExceptionHandling::Off},
+ {u"false", TypeSystem::ExceptionHandling::Off},
+ {u"auto-off", TypeSystem::ExceptionHandling::AutoDefaultToOff},
+ {u"auto-on", TypeSystem::ExceptionHandling::AutoDefaultToOn},
+ {u"yes", TypeSystem::ExceptionHandling::On},
+ {u"true", TypeSystem::ExceptionHandling::On},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::SmartPointerType, Qt::CaseSensitive,
+ smartPointerTypeFromAttribute)
+{
+ {u"handle", TypeSystem::SmartPointerType::Handle},
+ {u"unique", TypeSystem::SmartPointerType::Unique},
+ {u"value-handle", TypeSystem::SmartPointerType::ValueHandle},
+ {u"shared", TypeSystem::SmartPointerType::Shared}
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+template <class EnumType>
+static std::optional<EnumType>
+ lookupHashElement(const QHash<QStringView, EnumType> &hash,
+ QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+{
+ auto end = hash.cend();
+ auto it = hash.constFind(needle);
+ if (it != end)
+ return it.value();
+ if (cs == Qt::CaseInsensitive) { // brute force search for the unlikely case mismatch
+ for (it = hash.cbegin(); it != end; ++it) {
+ if (it.key().compare(needle, cs) == 0)
+ return it.value();
+ }
+ }
+ return std::nullopt;
+}
+
+using StackElementHash = QHash<QStringView, StackElement>;
+
+static const StackElementHash &stackElementHash()
+{
+ static const StackElementHash result{
+ {u"add-conversion", StackElement::AddConversion},
+ {u"add-function", StackElement::AddFunction},
+ {u"add-pymethoddef", StackElement::AddPyMethodDef},
+ {u"array", StackElement::Array},
+ {u"configuration", StackElement::Configuration},
+ {u"container-type", StackElement::ContainerTypeEntry},
+ {u"conversion-rule", StackElement::ConversionRule},
+ {u"custom-constructor", StackElement::Unimplemented},
+ {u"custom-destructor", StackElement::Unimplemented},
+ {u"custom-type", StackElement::CustomTypeEntry},
+ {u"declare-function", StackElement::DeclareFunction},
+ {u"define-ownership", StackElement::DefineOwnership},
+ {u"enum-type", StackElement::EnumTypeEntry},
+ {u"extra-includes", StackElement::ExtraIncludes},
+ {u"function", StackElement::FunctionTypeEntry},
+ {u"import-file", StackElement::ImportFile},
+ {u"include", StackElement::Include},
+ {u"inject-code", StackElement::InjectCode},
+ {u"inject-documentation", StackElement::InjectDocumentation},
+ {u"insert-template", StackElement::InsertTemplate},
+ {u"interface-type", StackElement::InterfaceTypeEntry},
+ {u"load-typesystem", StackElement::LoadTypesystem},
+ {u"modify-argument", StackElement::ModifyArgument},
+ {u"modify-documentation", StackElement::ModifyDocumentation},
+ {u"modify-field", StackElement::ModifyField},
+ {u"modify-function", StackElement::ModifyFunction},
+ {u"namespace-type", StackElement::NamespaceTypeEntry},
+ {u"native-to-target", StackElement::NativeToTarget},
+ {u"no-null-pointer", StackElement::NoNullPointers},
+ {u"object-type", StackElement::ObjectTypeEntry},
+ {u"opaque-container", StackElement::OpaqueContainer},
+ {u"parent", StackElement::ParentOwner},
+ {u"primitive-type", StackElement::PrimitiveTypeEntry},
+ {u"property", StackElement::Property},
+ {u"reference-count", StackElement::ReferenceCount},
+ {u"reject-enum-value", StackElement::RejectEnumValue},
+ {u"rejection", StackElement::Rejection},
+ {u"remove-argument", StackElement::RemoveArgument},
+ {u"remove-default-expression", StackElement::RemoveDefaultExpression},
+ {u"rename", StackElement::Rename}, // ### fixme PySide7: remove
+ {u"replace", StackElement::Replace},
+ {u"replace-default-expression", StackElement::ReplaceDefaultExpression},
+ {u"replace-type", StackElement::ReplaceType},
+ {u"smart-pointer-type", StackElement::SmartPointerTypeEntry},
+ {u"suppress-warning", StackElement::SuppressedWarning},
+ {u"system-include", StackElement::SystemInclude},
+ {u"target-to-native", StackElement::TargetToNative},
+ {u"template", StackElement::Template},
+ {u"typedef-type", StackElement::TypedefTypeEntry},
+ {u"typesystem", StackElement::Root},
+ {u"value-type", StackElement::ValueTypeEntry},
+ };
+ return result;
+}
+
+static std::optional<StackElement> elementFromTag(QStringView needle)
+{
+ return lookupHashElement(stackElementHash(), needle,
+ Qt::CaseInsensitive); // FIXME PYSIDE-7: case sensitive
+}
+
+static QStringView tagFromElement(StackElement st)
+{
+ return stackElementHash().key(st);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, StackElement st)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << tagFromElement(st);
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+ENUM_LOOKUP_BEGIN(TypeSystem::SnakeCase, Qt::CaseSensitive,
+ snakeCaseFromAttribute)
+{
+ {u"no", TypeSystem::SnakeCase::Disabled},
+ {u"false", TypeSystem::SnakeCase::Disabled},
+ {u"yes", TypeSystem::SnakeCase::Enabled},
+ {u"true", TypeSystem::SnakeCase::Enabled},
+ {u"both", TypeSystem::SnakeCase::Both},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive,
+ visibilityFromAttribute)
+{
+ {u"no", TypeSystem::Visibility::Invisible},
+ {u"false", TypeSystem::Visibility::Invisible},
+ {u"auto", TypeSystem::Visibility::Auto},
+ {u"yes", TypeSystem::Visibility::Visible},
+ {u"true", TypeSystem::Visibility::Visible},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+static int indexOfAttribute(const QXmlStreamAttributes &atts,
+ QAnyStringView name)
+{
+ for (qsizetype i = 0, size = atts.size(); i < size; ++i) {
+ if (atts.at(i).qualifiedName() == name)
+ return i;
+ }
+ return -1;
+}
+
+static QString msgMissingAttribute(const QString &a)
+{
+ return u"Required attribute '"_s + a
+ + u"' missing."_s;
+}
+
+QTextStream &operator<<(QTextStream &str, const QXmlStreamAttribute &attribute)
+{
+ str << attribute.qualifiedName() << "=\"" << attribute.value() << '"';
+ return str;
+}
+
+static QString msgInvalidAttributeValue(const QXmlStreamAttribute &attribute)
+{
+ QString result;
+ QTextStream(&result) << "Invalid attribute value:" << attribute;
+ return result;
+}
+
+static QString msgUnusedAttributes(QStringView tag, const QXmlStreamAttributes &attributes)
+{
+ QString result;
+ QTextStream str(&result);
+ str << attributes.size() << " attributes(s) unused on <" << tag << ">: ";
+ for (qsizetype i = 0, size = attributes.size(); i < size; ++i) {
+ if (i)
+ str << ", ";
+ str << attributes.at(i);
+ }
+ return result;
+}
+
+// QXmlStreamEntityResolver::resolveEntity(publicId, systemId) is not
+// implemented; resolve via undeclared entities instead.
+class TypeSystemEntityResolver : public QXmlStreamEntityResolver
+{
+public:
+ explicit TypeSystemEntityResolver(const QString &currentPath) :
+ m_currentPath(currentPath) {}
+
+ QString resolveUndeclaredEntity(const QString &name) override;
+
+private:
+ QString readFile(const QString &entityName, QString *errorMessage) const;
+
+ const QString m_currentPath;
+};
+
+QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *errorMessage) const
+{
+ QString fileName = entityName;
+ if (!fileName.contains(u'.'))
+ fileName += u".xml"_s;
+ QString path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (!QFileInfo::exists(path)) // PySide6-specific hack
+ fileName.prepend(u"typesystem_"_s);
+ path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (!QFileInfo::exists(path)) {
+ *errorMessage = u"Unable to resolve: "_s + entityName;
+ return {};
+ }
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ *errorMessage = msgCannotOpenForReading(file);
+ return {};
+ }
+ QString result = QString::fromUtf8(file.readAll()).trimmed();
+ // Remove license header comments on which QXmlStreamReader chokes
+ if (result.startsWith(u"<!--")) {
+ const auto commentEnd = result.indexOf(u"-->");
+ if (commentEnd != -1) {
+ result.remove(0, commentEnd + 3);
+ result = result.trimmed();
+ }
+ }
+ return result;
+}
+
+QString TypeSystemEntityResolver::resolveUndeclaredEntity(const QString &name)
+{
+ QString errorMessage;
+ const QString result = readFile(name, &errorMessage);
+ if (result.isEmpty()) { // The parser will fail and display the line number.
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgCannotResolveEntity(name, errorMessage)));
+ }
+ return result;
+}
+
+// State depending on element stack
+enum class ParserState
+{
+ None,
+ PrimitiveTypeNativeToTargetConversion,
+ PrimitiveTypeTargetToNativeConversion,
+ ArgumentConversion, // Argument conversion rule with class attribute
+ ArgumentNativeToTargetConversion,
+ ArgumentTargetToNativeConversion,
+ FunctionCodeInjection,
+ TypeEntryCodeInjection,
+ TypeSystemCodeInjection,
+ Template
+};
+
+TypeSystemParser::TypeSystemParser(const std::shared_ptr<TypeDatabaseParserContext> &context,
+ bool generate) :
+ m_context(context),
+ m_generate(generate ? TypeEntry::GenerateCode : TypeEntry::GenerateForSubclass)
+{
+}
+
+TypeSystemParser::~TypeSystemParser() = default;
+
+static QString readerFileName(const ConditionalStreamReader &reader)
+{
+ const auto *file = qobject_cast<const QFile *>(reader.device());
+ return file != nullptr ? file->fileName() : QString();
+}
+
+static QString msgReaderMessage(const ConditionalStreamReader &reader,
+ const char *type,
+ const QString &what)
+{
+ QString message;
+ QTextStream str(&message);
+ const QString fileName = readerFileName(reader);
+ if (fileName.isEmpty())
+ str << "<stdin>:";
+ else
+ str << QDir::toNativeSeparators(fileName) << ':';
+ // Use a tab separator like SourceLocation for suppression detection
+ str << reader.lineNumber() << ':' << reader.columnNumber()
+ << ":\t" << type << ": " << what;
+ return message;
+}
+
+static QString msgReaderWarning(const ConditionalStreamReader &reader, const QString &what)
+{
+ return msgReaderMessage(reader, "Warning", what);
+}
+
+static QString msgReaderError(const ConditionalStreamReader &reader, const QString &what)
+{
+ return msgReaderMessage(reader, "Error", what);
+}
+
+static QString msgUnimplementedElementWarning(const ConditionalStreamReader &reader,
+ QAnyStringView name)
+{
+ QString message;
+ QTextStream(&message) << "The element \"" << name
+ << "\" is not implemented.";
+ return msgReaderMessage(reader, "Warning", message);
+}
+
+static QString msgUnimplementedAttributeWarning(const ConditionalStreamReader &reader,
+ QStringView name)
+{
+ QString message;
+ QTextStream(&message) << "The attribute \"" << name
+ << "\" is not implemented.";
+ return msgReaderMessage(reader, "Warning", message);
+}
+
+static inline QString msgUnimplementedAttributeWarning(const ConditionalStreamReader &reader,
+ const QXmlStreamAttribute &attribute)
+{
+ return msgUnimplementedAttributeWarning(reader, attribute.qualifiedName());
+}
+
+static QString
+ msgUnimplementedAttributeValueWarning(const ConditionalStreamReader &reader,
+ QAnyStringView name, QAnyStringView value)
+{
+ QString message;
+ QTextStream(&message) << "The value \"" << value
+ << "\" of the attribute \"" << name << "\" is not implemented.";
+ return msgReaderMessage(reader, "Warning", message);
+}
+
+static inline
+ QString msgUnimplementedAttributeValueWarning(const ConditionalStreamReader &reader,
+ const QXmlStreamAttribute &attribute)
+{
+ return msgUnimplementedAttributeValueWarning(reader,
+ attribute.qualifiedName(),
+ attribute.value());
+}
+
+static bool addRejection(TypeDatabase *database, bool generate, QXmlStreamAttributes *attributes,
+ QString *errorMessage)
+{
+ const auto classIndex = indexOfAttribute(*attributes, classAttribute);
+ if (classIndex == -1) {
+ *errorMessage = msgMissingAttribute(classAttribute);
+ return false;
+ }
+
+ TypeRejection rejection;
+ rejection.generate = generate;
+ const QString className = attributes->takeAt(classIndex).value().toString();
+ if (!setRejectionRegularExpression(className, &rejection.className, errorMessage))
+ return false;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto &attribute = attributes->at(i);
+ const auto name = attribute.qualifiedName();
+ const auto typeOpt = typeRejectionFromAttribute(name);
+ if (!typeOpt.has_value()) {
+ *errorMessage = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ switch (typeOpt.value()) {
+ case TypeRejection::Function:
+ case TypeRejection::Field:
+ case TypeRejection::Enum:
+ case TypeRejection::ArgumentType:
+ case TypeRejection::ReturnType: {
+ const QString pattern = attributes->takeAt(i).value().toString();
+ if (!setRejectionRegularExpression(pattern, &rejection.pattern, errorMessage))
+ return false;
+ rejection.matchType = typeOpt.value();
+ database->addRejection(rejection);
+ return true;
+ }
+ case TypeRejection::ExcludeClass:
+ break;
+ }
+ }
+
+ // Special case: When all fields except class are empty, completely exclude class
+ if (className == u"*") {
+ *errorMessage = u"bad reject entry, neither 'class', 'function-name'"
+ " nor 'field' specified"_s;
+ return false;
+ }
+ rejection.matchType = TypeRejection::ExcludeClass;
+ database->addRejection(rejection);
+ return true;
+}
+
+bool TypeSystemParser::parse(ConditionalStreamReader &reader)
+{
+ m_error.clear();
+ m_currentPath.clear();
+ m_currentFile.clear();
+ return parseXml(reader);
+}
+
+bool TypeSystemParser::parseXml(ConditionalStreamReader &reader)
+{
+ const QString fileName = readerFileName(reader);
+ if (!fileName.isEmpty()) {
+ QFileInfo fi(fileName);
+ m_currentPath = fi.absolutePath();
+ m_currentFile = fi.absoluteFilePath();
+ }
+ m_entityResolver.reset(new TypeSystemEntityResolver(m_currentPath));
+ reader.setEntityResolver(m_entityResolver.data());
+
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::NoToken:
+ case QXmlStreamReader::Invalid:
+ m_error = msgReaderError(reader, reader.errorString());
+ return false;
+ case QXmlStreamReader::StartElement: {
+ const auto elementTypeOpt = elementFromTag(reader.name());
+ if (!elementTypeOpt.has_value()) {
+ m_error = u"Unknown tag name: '"_s + reader.name().toString() + u'\'';
+ return false;
+ }
+ m_stack.push(elementTypeOpt.value());
+ if (!startElement(reader, m_stack.top())) {
+ m_error = msgReaderError(reader, m_error);
+ return false;
+ }
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (!endElement(m_stack.top())) {
+ m_error = msgReaderError(reader, m_error);
+ return false;
+ }
+ m_stack.pop();
+ break;
+ case QXmlStreamReader::Characters:
+ if (!characters(reader.text())) {
+ m_error = msgReaderError(reader, m_error);
+ return false;
+ }
+ break;
+ case QXmlStreamReader::StartDocument:
+ case QXmlStreamReader::EndDocument:
+ case QXmlStreamReader::Comment:
+ case QXmlStreamReader::DTD:
+ case QXmlStreamReader::EntityReference:
+ case QXmlStreamReader::ProcessingInstruction:
+ break;
+ }
+ }
+ return true;
+}
+
+bool TypeSystemParser::endElement(StackElement element)
+{
+ if (m_ignoreDepth) {
+ --m_ignoreDepth;
+ return true;
+ }
+
+ if (m_currentDroppedEntryDepth != 0) {
+ --m_currentDroppedEntryDepth;
+ return true;
+ }
+
+ if (element == StackElement::ImportFile)
+ return true;
+
+ if (m_contextStack.isEmpty())
+ return true;
+
+ const auto &top = m_contextStack.top();
+
+ switch (element) {
+ case StackElement::Unimplemented:
+ return true;
+ case StackElement::Root:
+ if (m_generate == TypeEntry::GenerateCode) {
+ TypeDatabase::instance()->addGlobalUserFunctions(top->addedFunctions);
+ TypeDatabase::instance()->addGlobalUserFunctionModifications(top->functionMods);
+ for (const auto &customConversion : std::as_const(customConversionsForReview)) {
+ TargetToNativeConversions &toNatives =
+ customConversion->targetToNativeConversions();
+ for (auto &toNative : toNatives)
+ toNative.setSourceType(m_context->db->findType(toNative.sourceTypeName()));
+ }
+ }
+ purgeEmptyCodeSnips(&std::static_pointer_cast<TypeSystemTypeEntry>(top->entry)->codeSnips());
+ break;
+ case StackElement::FunctionTypeEntry:
+ TypeDatabase::instance()->addGlobalUserFunctionModifications(top->functionMods);
+ break;
+ case StackElement::ObjectTypeEntry:
+ case StackElement::ValueTypeEntry:
+ case StackElement::InterfaceTypeEntry:
+ case StackElement::ContainerTypeEntry:
+ case StackElement::NamespaceTypeEntry: {
+ Q_ASSERT(top->entry);
+ Q_ASSERT(top->entry->isComplex());
+ auto centry = std::static_pointer_cast<ComplexTypeEntry>(top->entry);
+ purgeEmptyCodeSnips(&centry->codeSnips());
+ centry->setAddedFunctions(top->addedFunctions);
+ centry->setFunctionModifications(top->functionMods);
+ centry->setFieldModifications(top->fieldMods);
+ centry->setDocModification(top->docModifications);
+ }
+ break;
+
+ case StackElement::TypedefTypeEntry: {
+ auto centry = std::static_pointer_cast<TypedefEntry>(top->entry)->target();
+ centry->setAddedFunctions(centry->addedFunctions() + top->addedFunctions);
+ centry->setFunctionModifications(centry->functionModifications() + top->functionMods);
+ centry->setFieldModifications(centry->fieldModifications() + top->fieldMods);
+ centry->setDocModification(centry->docModifications() + top->docModifications);
+ if (top->entry->isComplex()) {
+ auto cte = std::static_pointer_cast<const ComplexTypeEntry>(top->entry);
+ centry->setCodeSnips(centry->codeSnips() + cte->codeSnips());
+ }
+ }
+ break;
+
+ case StackElement::AddFunction:
+ case StackElement::DeclareFunction: {
+ // Leaving add-function: Assign all modifications to the added function
+ const int modIndex = top->addedFunctionModificationIndex;
+ top->addedFunctionModificationIndex = -1;
+ Q_ASSERT(modIndex >= 0);
+ Q_ASSERT(!top->addedFunctions.isEmpty());
+ while (modIndex < top->functionMods.size())
+ top->addedFunctions.last()->modifications().append(top->functionMods.takeAt(modIndex));
+ }
+ break;
+ case StackElement::NativeToTarget:
+ case StackElement::AddConversion:
+ switch (parserState()) {
+ case ParserState::PrimitiveTypeNativeToTargetConversion:
+ case ParserState::PrimitiveTypeTargetToNativeConversion: {
+ auto customConversion = CustomConversion::getCustomConversion(top->entry);
+ if (!customConversion) {
+ m_error = msgMissingCustomConversion(top->entry);
+ return false;
+ }
+ QString code = top->conversionCodeSnips.constLast().code();
+ if (element == StackElement::AddConversion) {
+ if (customConversion->targetToNativeConversions().isEmpty()) {
+ m_error = u"CustomConversion's target to native conversions missing."_s;
+ return false;
+ }
+ customConversion->targetToNativeConversions().last().setConversion(code);
+ } else {
+ customConversion->setNativeToTargetConversion(code);
+ }
+ }
+ break;
+
+ case ParserState::ArgumentNativeToTargetConversion: {
+ top->conversionCodeSnips.last().language = TypeSystem::TargetLangCode;
+ auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ lastArgMod.conversionRules().append(top->conversionCodeSnips.constLast());
+ }
+ break;
+ case ParserState::ArgumentTargetToNativeConversion: {
+ top->conversionCodeSnips.last().language = TypeSystem::NativeCode;
+ auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ lastArgMod.conversionRules().append(top->conversionCodeSnips.constLast());
+ }
+ break;
+ default:
+ break;
+ }
+ top->conversionCodeSnips.clear();
+ break;
+
+ case StackElement::EnumTypeEntry:
+ m_currentEnum = nullptr;
+ break;
+ case StackElement::Template:
+ m_context->db->addTemplate(m_templateEntry);
+ m_templateEntry = nullptr;
+ break;
+ case StackElement::InsertTemplate:
+ if (auto *snip = injectCodeTarget(1))
+ snip->addTemplateInstance(m_templateInstance);
+ m_templateInstance.reset();
+ break;
+
+ case StackElement::ModifyArgument:
+ purgeEmptyCodeSnips(&top->functionMods.last().argument_mods().last().conversionRules());
+ break;
+
+ default:
+ break;
+ }
+
+ if (isTypeEntry(element) || element == StackElement::Root)
+ m_contextStack.pop();
+
+ return true;
+}
+
+ParserState TypeSystemParser::parserState(qsizetype offset) const
+{
+ const auto stackSize = m_stack.size() - offset;
+ if (stackSize <= 0 || m_contextStack.isEmpty())
+ return ParserState::None;
+
+ const auto last = stackSize - 1;
+
+ switch (m_stack.at(last)) {
+ // Primitive entry with conversion rule
+ case StackElement::NativeToTarget: // <conversion-rule><native-to-target>
+ if (stackSize > 2 && m_stack.at(last - 2) == StackElement::ModifyArgument)
+ return ParserState::ArgumentNativeToTargetConversion;
+ return ParserState::PrimitiveTypeNativeToTargetConversion;
+
+ case StackElement::AddConversion: // <conversion-rule><target-to-native><add-conversion>
+ if (stackSize > 3 && m_stack.at(last - 3) == StackElement::ModifyArgument)
+ return ParserState::ArgumentTargetToNativeConversion;
+ return ParserState::PrimitiveTypeTargetToNativeConversion;
+
+ case StackElement::ConversionRule:
+ if (stackSize > 1 && m_stack.at(last - 1) == StackElement::ModifyArgument)
+ return ParserState::ArgumentConversion;
+ break;
+
+ case StackElement::InjectCode:
+ switch (m_stack.value(last - 1, StackElement::None)) {
+ case StackElement::Root:
+ return ParserState::TypeSystemCodeInjection;
+ case StackElement::ModifyFunction:
+ case StackElement::AddFunction:
+ return ParserState::FunctionCodeInjection;
+ case StackElement::NamespaceTypeEntry:
+ case StackElement::ObjectTypeEntry:
+ case StackElement::ValueTypeEntry:
+ case StackElement::InterfaceTypeEntry:
+ return ParserState::TypeEntryCodeInjection;
+ default:
+ break;
+ }
+ break;
+
+ case StackElement::Template:
+ return ParserState::Template;
+
+ default:
+ break;
+ }
+
+ return ParserState::None;
+}
+
+// Return where to add injected code depending on elements.
+CodeSnipAbstract *TypeSystemParser::injectCodeTarget(qsizetype offset) const
+{
+ const auto state = parserState(offset);
+ if (state == ParserState::None)
+ return nullptr;
+
+ const auto &top = m_contextStack.top();
+ switch (state) {
+ case ParserState::PrimitiveTypeNativeToTargetConversion:
+ case ParserState::PrimitiveTypeTargetToNativeConversion:
+ case ParserState::ArgumentNativeToTargetConversion:
+ case ParserState::ArgumentTargetToNativeConversion:
+ return &top->conversionCodeSnips.last();
+ case ParserState::ArgumentConversion:
+ return &top->functionMods.last().argument_mods().last().conversionRules().last();
+ case ParserState::FunctionCodeInjection: {
+ auto &funcMod = top->functionMods.last();
+ funcMod.setModifierFlag(FunctionModification::CodeInjection);
+ return &funcMod.snips().last();
+ }
+ case ParserState::TypeEntryCodeInjection:
+ Q_ASSERT(top->entry->isComplex());
+ return &std::static_pointer_cast<ComplexTypeEntry>(top->entry)->codeSnips().last();
+ case ParserState::TypeSystemCodeInjection:
+ Q_ASSERT(top->entry->isTypeSystem());
+ return &std::static_pointer_cast<TypeSystemTypeEntry>(top->entry)->codeSnips().last();
+ case ParserState::Template:
+ return m_templateEntry.get();
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+template <class String> // QString/QStringRef
+bool TypeSystemParser::characters(const String &ch)
+{
+ const auto stackSize = m_stack.size();
+ if (m_currentDroppedEntryDepth != 0 || m_ignoreDepth != 0
+ || stackSize == 0 || m_stack.top() == StackElement::Unimplemented) {
+ return true;
+ }
+
+ const StackElement type = m_stack.top();
+
+ if (type == StackElement::Template) {
+ m_templateEntry->addCode(ch);
+ return true;
+ }
+
+ if (m_contextStack.isEmpty()) {
+ m_error = msgNoRootTypeSystemEntry();
+ return false;
+ }
+
+ if (auto *snip = injectCodeTarget()) {
+ snip->addCode(ch);
+ return true;
+ }
+
+ if (isDocumentation(type)) {
+ const bool isAddedFunction = m_stack.value(m_stack.size() - 2, StackElement::None)
+ == StackElement::AddFunction;
+ const auto &top = m_contextStack.top();
+ auto &docModifications = isAddedFunction
+ ? top->addedFunctions.last()->docModifications()
+ : top->docModifications;
+ docModifications.last().setCode(ch);
+ }
+
+ return true;
+}
+
+bool TypeSystemParser::importFileElement(const QXmlStreamAttributes &atts)
+{
+ const QString fileName = atts.value(nameAttribute).toString();
+ if (fileName.isEmpty()) {
+ m_error = u"Required attribute 'name' missing for include-file tag."_s;
+ return false;
+ }
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ file.setFileName(u":/trolltech/generator/"_s + fileName);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ m_error = msgCannotOpenForReading(file);
+ return false;
+ }
+ }
+
+ const auto quoteFrom = atts.value(quoteAfterLineAttribute);
+ bool foundFromOk = quoteFrom.isEmpty();
+ bool from = quoteFrom.isEmpty();
+
+ const auto quoteTo = atts.value(quoteBeforeLineAttribute);
+ bool foundToOk = quoteTo.isEmpty();
+ bool to = true;
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (from && to && line.contains(quoteTo)) {
+ to = false;
+ foundToOk = true;
+ break;
+ }
+ if (from && to)
+ characters(line + u'\n');
+ if (!from && line.contains(quoteFrom)) {
+ from = true;
+ foundFromOk = true;
+ }
+ }
+ if (!foundFromOk || !foundToOk) {
+ QString fromError = QString::fromLatin1("Could not find quote-after-line='%1' in file '%2'.")
+ .arg(quoteFrom.toString(), fileName);
+ QString toError = QString::fromLatin1("Could not find quote-before-line='%1' in file '%2'.")
+ .arg(quoteTo.toString(), fileName);
+
+ if (!foundToOk)
+ m_error = toError;
+ if (!foundFromOk)
+ m_error = fromError;
+ if (!foundFromOk && !foundToOk)
+ m_error = fromError + u' ' + toError;
+ return false;
+ }
+
+ return true;
+}
+
+static bool convertBoolean(QStringView value, QAnyStringView attributeName, bool defaultValue)
+{
+ if (value.compare(trueAttributeValue, Qt::CaseInsensitive) == 0
+ || value.compare(yesAttributeValue, Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ if (value.compare(falseAttributeValue, Qt::CaseInsensitive) == 0
+ || value.compare(noAttributeValue, Qt::CaseInsensitive) == 0) {
+ return false;
+ }
+ qCWarning(lcShiboken).noquote().nospace() << "Boolean value '" << value
+ << "' not supported in attribute '" << attributeName
+ << "'. Use 'yes' or 'no'. Defaulting to '"
+ << (defaultValue ? yesAttributeValue : noAttributeValue) << "'.";
+ return defaultValue;
+}
+
+static bool convertRemovalAttribute(QStringView value)
+{
+ return value == u"all" // Legacy
+ || convertBoolean(value, removeAttribute, false);
+}
+
+// Check whether an entry should be dropped, allowing for dropping the module
+// name (match 'Class' and 'Module.Class').
+static bool shouldDropTypeEntry(const TypeDatabase *db,
+ const TypeSystemParser::ContextStack &stack ,
+ QString name)
+{
+ for (auto i = stack.size() - 1; i >= 0; --i) {
+ if (auto entry = stack.at(i)->entry) {
+ if (entry->type() == TypeEntry::TypeSystemType) {
+ if (db->shouldDropTypeEntry(name)) // Unqualified
+ return true;
+ }
+ name.prepend(u'.');
+ name.prepend(entry->name());
+ }
+ }
+ return db->shouldDropTypeEntry(name);
+}
+
+// Returns empty string if there's no error.
+static QString checkSignatureError(const QString& signature, const QString& tag)
+{
+ QString funcName = signature.left(signature.indexOf(u'(')).trimmed();
+ static const QRegularExpression whiteSpace("\\s"_L1);
+ Q_ASSERT(whiteSpace.isValid());
+ if (!funcName.startsWith(u"operator ") && funcName.contains(whiteSpace)) {
+ return QString::fromLatin1("Error in <%1> tag signature attribute '%2'.\n"
+ "White spaces aren't allowed in function names, "
+ "and return types should not be part of the signature.")
+ .arg(tag, signature);
+ }
+ return QString();
+}
+
+inline TypeEntryCPtr TypeSystemParser::currentParentTypeEntry() const
+{
+ const auto size = m_contextStack.size();
+ return size > 1 ? m_contextStack.at(size - 2)->entry : nullptr;
+}
+
+bool TypeSystemParser::checkRootElement()
+{
+ for (auto i = m_contextStack.size() - 1; i >= 0; --i) {
+ auto e = m_contextStack.at(i)->entry;
+ if (e && e->isTypeSystem())
+ return true;
+ }
+ m_error = msgNoRootTypeSystemEntry();
+ return false;
+}
+
+static TypeEntryPtr findViewedType(const QString &name)
+{
+ const auto range = TypeDatabase::instance()->entries().equal_range(name);
+ for (auto i = range.first; i != range.second; ++i) {
+ switch (i.value()->type()) {
+ case TypeEntry::BasicValueType:
+ case TypeEntry::PrimitiveType:
+ case TypeEntry::ContainerType:
+ case TypeEntry::ObjectType:
+ return i.value();
+ default:
+ break;
+ }
+ }
+ return nullptr;
+}
+
+bool TypeSystemParser::applyCommonAttributes(const ConditionalStreamReader &reader,
+ const TypeEntryPtr &type,
+ QXmlStreamAttributes *attributes)
+{
+ type->setSourceLocation(SourceLocation(m_currentFile,
+ reader.lineNumber()));
+ type->setCodeGeneration(m_generate);
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"revision") {
+ type->setRevision(attributes->takeAt(i).value().toInt());
+ } else if (name == u"view-on") {
+ const QString name = attributes->takeAt(i).value().toString();
+ TypeEntryPtr views = findViewedType(name);
+ if (!views) {
+ m_error = msgCannotFindView(name, type->name());
+ return false;
+ }
+ type->setViewOn(views);
+ }
+ }
+ return true;
+}
+
+CustomTypeEntryPtr TypeSystemParser::parseCustomTypeEntry(const ConditionalStreamReader &,
+ const QString &name,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto result = std::make_shared<CustomTypeEntry>(name, since, m_contextStack.top()->entry);
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == checkFunctionAttribute)
+ result->setCheckFunction(attributes->takeAt(i).value().toString());
+ }
+ return result;
+}
+
+FlagsTypeEntryPtr
+ TypeSystemParser::parseFlagsEntry(const ConditionalStreamReader &reader,
+ const EnumTypeEntryPtr &enumEntry, QString flagName,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto ftype = std::make_shared<FlagsTypeEntry>(u"QFlags<"_s + enumEntry->name() + u'>',
+ since,
+ typeSystemTypeEntry(currentParentTypeEntry()));
+ ftype->setOriginator(enumEntry);
+ ftype->setTargetLangPackage(enumEntry->targetLangPackage());
+ // Try toenumEntry get the guess the qualified flag name
+ if (!flagName.contains(u"::"_s)) {
+ auto eq = enumEntry->qualifier();
+ if (!eq.isEmpty())
+ flagName.prepend(eq + u"::"_s);
+ }
+
+ ftype->setOriginalName(flagName);
+ if (!applyCommonAttributes(reader, ftype, attributes))
+ return nullptr;
+
+ QStringList lst = flagName.split(u"::"_s);
+ const QString name = lst.takeLast();
+ const QString targetLangFlagName = lst.join(u'.');
+ const QString &targetLangQualifier = enumEntry->targetLangQualifier();
+ if (targetLangFlagName != targetLangQualifier) {
+ qCWarning(lcShiboken, "enum %s and flags %s (%s) differ in qualifiers",
+ qPrintable(targetLangQualifier), qPrintable(lst.value(0)),
+ qPrintable(targetLangFlagName));
+ }
+
+ ftype->setFlagsName(name);
+ enumEntry->setFlags(ftype);
+
+ m_context->db->addFlagsType(ftype);
+ m_context->db->addType(ftype);
+
+ const int revisionIndex =
+ indexOfAttribute(*attributes, u"flags-revision");
+ ftype->setRevision(revisionIndex != -1
+ ? attributes->takeAt(revisionIndex).value().toInt()
+ : enumEntry->revision());
+ return ftype;
+}
+
+SmartPointerTypeEntryPtr
+ TypeSystemParser::parseSmartPointerEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ TypeSystem::SmartPointerType smartPointerType = TypeSystem::SmartPointerType::Shared;
+ QString getter;
+ QString refCountMethodName;
+ QString valueCheckMethod;
+ QString nullCheckMethod;
+ QString resetMethod;
+ QString instantiations;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"type") {
+ const auto attribute = attributes->takeAt(i);
+ const auto typeOpt = smartPointerTypeFromAttribute(attribute.value());
+ if (!typeOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return nullptr;
+ }
+ smartPointerType = typeOpt.value();
+ } else if (name == u"getter") {
+ getter = attributes->takeAt(i).value().toString();
+ } else if (name == u"ref-count-method") {
+ refCountMethodName = attributes->takeAt(i).value().toString();
+ } else if (name == u"instantiations") {
+ instantiations = attributes->takeAt(i).value().toString();
+ } else if (name == u"value-check-method") {
+ valueCheckMethod = attributes->takeAt(i).value().toString();
+ } else if (name == u"null-check-method") {
+ nullCheckMethod = attributes->takeAt(i).value().toString();
+ } else if (name == u"reset-method") {
+ resetMethod = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (getter.isEmpty()) {
+ m_error = u"No function getter name specified for getting the raw pointer held by the smart pointer."_s;
+ return nullptr;
+ }
+
+ QString signature = getter + u"()"_s;
+ signature = TypeDatabase::normalizedSignature(signature);
+ if (signature.isEmpty()) {
+ m_error = u"No signature for the smart pointer getter found."_s;
+ return nullptr;
+ }
+
+ QString errorString = checkSignatureError(signature,
+ u"smart-pointer-type"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return nullptr;
+ }
+
+ if (smartPointerType == TypeSystem::SmartPointerType::Unique && resetMethod.isEmpty()) {
+ m_error = u"Unique pointers require a reset() method."_s;
+ return nullptr;
+ }
+
+ auto type = std::make_shared<SmartPointerTypeEntry>(name, getter, smartPointerType,
+ refCountMethodName, since,
+ currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, type, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, type, attributes);
+ type->setNullCheckMethod(nullCheckMethod);
+ type->setValueCheckMethod(valueCheckMethod);
+ type->setResetMethod(resetMethod);
+ m_context->smartPointerInstantiations.insert(type, instantiations);
+ return type;
+}
+
+PrimitiveTypeEntryPtr
+ TypeSystemParser::parsePrimitiveTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto type = std::make_shared<PrimitiveTypeEntry>(name, since, currentParentTypeEntry());
+ QString targetLangApiName;
+ if (!applyCommonAttributes(reader, type, attributes))
+ return nullptr;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == targetLangNameAttribute) {
+ type->setTargetLangName(attributes->takeAt(i).value().toString());
+ } else if (name == u"target-lang-api-name") {
+ targetLangApiName = attributes->takeAt(i).value().toString();
+ } else if (name == preferredConversionAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == preferredTargetLangTypeAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ preferredTargetLangTypeAttribute, true);
+ type->setPreferredTargetLangType(v);
+ } else if (name == u"default-constructor") {
+ type->setDefaultConstructor(attributes->takeAt(i).value().toString());
+ }
+ }
+
+ if (!targetLangApiName.isEmpty()) {
+ auto e = m_context->db->findType(targetLangApiName);
+ if (!e || !e->isCustom()) {
+ m_error = msgInvalidTargetLanguageApiName(targetLangApiName);
+ return nullptr;
+ }
+ type->setTargetLangApiType(std::static_pointer_cast<CustomTypeEntry>(e));
+ }
+ type->setTargetLangPackage(m_defaultPackage);
+ return type;
+}
+
+// "int:QList_int;QString:QList_QString"
+bool TypeSystemParser::parseOpaqueContainers(QStringView s, OpaqueContainers *result)
+{
+ const auto entries = s.split(u';');
+ for (const auto &entry : entries) {
+ const auto values = entry.split(u':');
+ if (values.size() != 2) {
+ m_error = u"Error parsing the opaque container attribute: \""_s
+ + s.toString() + u"\"."_s;
+ return false;
+ }
+ OpaqueContainer oc;
+ oc.name = values.at(1).trimmed().toString();
+ const auto instantiations = values.at(0).split(u',', Qt::SkipEmptyParts);
+ for (const auto &instantiationV : instantiations) {
+ QString instantiation = instantiationV.trimmed().toString();
+ // Fix to match AbstractMetaType::signature() which is used for matching
+ // "Foo*" -> "Foo *"
+ const auto asteriskPos = instantiation.indexOf(u'*');
+ if (asteriskPos > 0 && !instantiation.at(asteriskPos - 1).isSpace())
+ instantiation.insert(asteriskPos, u' ');
+ oc.instantiations.append(instantiation);
+ }
+ result->append(oc);
+ }
+ return true;
+}
+
+ContainerTypeEntryPtr
+ TypeSystemParser::parseContainerTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ const auto typeIndex = indexOfAttribute(*attributes, u"type");
+ if (typeIndex == -1) {
+ m_error = u"no 'type' attribute specified"_s;
+ return nullptr;
+ }
+ const auto typeName = attributes->at(typeIndex).value();
+ const auto containerTypeOpt = containerTypeFromAttribute(typeName);
+ if (!containerTypeOpt.has_value()) {
+ m_error = u"there is no container of type "_s + typeName.toString();
+ return nullptr;
+ }
+ attributes->removeAt(typeIndex);
+ auto type = std::make_shared<ContainerTypeEntry>(name, containerTypeOpt.value(),
+ since, currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, type, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, type, attributes);
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == opaqueContainerAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ OpaqueContainers oc;
+ if (!parseOpaqueContainers(attribute.value(), &oc))
+ return nullptr;
+ type->appendOpaqueContainers(oc);
+ }
+ }
+
+ return type;
+}
+
+bool TypeSystemParser::parseOpaqueContainerElement(QXmlStreamAttributes *attributes)
+{
+ QString containerName;
+ OpaqueContainers oc;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ containerName = attributes->takeAt(i).value().toString();
+ } else if (name == opaqueContainerAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ if (!parseOpaqueContainers(attribute.value(), &oc))
+ return false;
+ }
+ }
+ if (containerName.isEmpty()) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_context->opaqueContainerHash[containerName].append(oc);
+ return true;
+}
+
+EnumTypeEntryPtr
+ TypeSystemParser::parseEnumTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto entry = std::make_shared<EnumTypeEntry>(name, since, currentParentTypeEntry());
+ applyCommonAttributes(reader, entry, attributes);
+ entry->setTargetLangPackage(m_defaultPackage);
+
+ QString flagNames;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"upper-bound") {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == u"lower-bound") {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == docFileAttribute) {
+ entry->setDocFile(attributes->takeAt(i).value().toString());
+ } else if (name == forceIntegerAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == pythonEnumTypeAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto typeOpt = pythonEnumTypeFromAttribute(attribute.value());
+ if (typeOpt.has_value()) {
+ entry->setPythonEnumType(typeOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == cppEnumTypeAttribute) {
+ entry->setCppType(attributes->takeAt(i).value().toString());
+ } else if (name == extensibleAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == flagsAttribute) {
+ flagNames = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ // put in the flags parallel...
+ if (!flagNames.isEmpty()) {
+ const QStringList &flagNameList = flagNames.split(u',');
+ for (const QString &flagName : flagNameList)
+ parseFlagsEntry(reader, entry, flagName.trimmed(), since, attributes);
+ }
+ return entry;
+}
+
+
+NamespaceTypeEntryPtr
+ TypeSystemParser::parseNamespaceTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto result = std::make_shared<NamespaceTypeEntry>(name, since, currentParentTypeEntry());
+ auto visibility = TypeSystem::Visibility::Unspecified;
+ applyCommonAttributes(reader, result, attributes);
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto attributeName = attributes->at(i).qualifiedName();
+ if (attributeName == u"files") {
+ const QString pattern = attributes->takeAt(i).value().toString();
+ QRegularExpression re(pattern);
+ if (!re.isValid()) {
+ m_error = msgInvalidRegularExpression(pattern, re.errorString());
+ return nullptr;
+ }
+ result->setFilePattern(re);
+ } else if (attributeName == u"extends") {
+ const auto extendsPackageName = attributes->at(i).value();
+ auto allEntries = TypeDatabase::instance()->findNamespaceTypes(name);
+ auto extendsIt = std::find_if(allEntries.cbegin(), allEntries.cend(),
+ [extendsPackageName] (const NamespaceTypeEntryCPtr &e) {
+ return e->targetLangPackage() == extendsPackageName;
+ });
+ if (extendsIt == allEntries.cend()) {
+ m_error = msgCannotFindNamespaceToExtend(name, extendsPackageName.toString());
+ return nullptr;
+ }
+ result->setExtends(*extendsIt);
+ attributes->removeAt(i);
+ } else if (attributeName == visibleAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto visibilityOpt = visibilityFromAttribute(attribute.value());
+ if (!visibilityOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return nullptr;
+ }
+ visibility = visibilityOpt.value();
+ } else if (attributeName == generateAttribute) {
+ if (!convertBoolean(attributes->takeAt(i).value(), generateAttribute, true))
+ visibility = TypeSystem::Visibility::Invisible;
+ } else if (attributeName == generateUsingAttribute) {
+ result->setGenerateUsing(convertBoolean(attributes->takeAt(i).value(),
+ generateUsingAttribute, true));
+ }
+ }
+
+ if (visibility != TypeSystem::Visibility::Unspecified)
+ result->setVisibility(visibility);
+ // Handle legacy "generate" before the common handling
+ applyComplexTypeAttributes(reader, result, attributes);
+
+ if (result->extends() && !result->hasPattern()) {
+ m_error = msgExtendingNamespaceRequiresPattern(name);
+ return {};
+ }
+
+ return result;
+}
+
+ValueTypeEntryPtr
+ TypeSystemParser::parseValueTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto typeEntry = std::make_shared<ValueTypeEntry>(name, since, currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, typeEntry, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, typeEntry, attributes);
+ const int defaultCtIndex =
+ indexOfAttribute(*attributes, u"default-constructor");
+ if (defaultCtIndex != -1)
+ typeEntry->setDefaultConstructor(attributes->takeAt(defaultCtIndex).value().toString());
+ return typeEntry;
+}
+
+FunctionTypeEntryPtr
+ TypeSystemParser::parseFunctionTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+
+ FunctionModification mod;
+ const auto oldAttributesSize = attributes->size();
+ if (!parseModifyFunctionAttributes(attributes, &mod))
+ return nullptr;
+ const bool hasModification = attributes->size() < oldAttributesSize;
+
+ QString originalSignature;
+ QString docFile;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute)
+ originalSignature = attributes->takeAt(i).value().toString().simplified();
+ else if (name == docFileAttribute)
+ docFile = attributes->takeAt(i).value().toString();
+ }
+
+ const QString signature = TypeDatabase::normalizedSignature(originalSignature);
+ if (signature.isEmpty()) {
+ m_error = msgMissingAttribute(signatureAttribute);
+ return nullptr;
+ }
+
+ if (hasModification) {
+ mod.setOriginalSignature(originalSignature);
+ mod.setSignature(signature);
+ m_contextStack.top()->functionMods << mod;
+ }
+
+ TypeEntryPtr existingType = m_context->db->findType(name);
+
+ if (!existingType) {
+ auto result = std::make_shared<FunctionTypeEntry>(name, signature, since,
+ currentParentTypeEntry());
+ result->setTargetLangPackage(m_defaultPackage);
+ result->setDocFile(docFile);
+ applyCommonAttributes(reader, result, attributes);
+ return result;
+ }
+
+ if (existingType->type() != TypeEntry::FunctionType) {
+ m_error = name + " expected to be a function, but isn't! Maybe it was already declared as a class or something else."_L1;
+ return nullptr;
+ }
+
+ auto result = std::static_pointer_cast<FunctionTypeEntry>(existingType);
+ result->addSignature(signature);
+ return result;
+}
+
+TypedefEntryPtr
+ TypeSystemParser::parseTypedefEntry(const ConditionalStreamReader &reader,
+ const QString &name, StackElement topElement,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ if (topElement != StackElement::Root
+ && topElement != StackElement::NamespaceTypeEntry) {
+ m_error = u"typedef entries must be nested in namespaces or type system."_s;
+ return nullptr;
+ }
+ const auto sourceIndex = indexOfAttribute(*attributes, sourceAttribute);
+ if (sourceIndex == -1) {
+ m_error = msgMissingAttribute(sourceAttribute);
+ return nullptr;
+ }
+ const QString sourceType = attributes->takeAt(sourceIndex).value().toString();
+ auto result = std::make_shared<TypedefEntry>(name, sourceType, since,
+ currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, result, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, result, attributes);
+ return result;
+}
+
+void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader &reader,
+ const ComplexTypeEntryPtr &ctype,
+ QXmlStreamAttributes *attributes) const
+{
+ bool generate = true;
+ ctype->setCopyable(ComplexTypeEntry::Unknown);
+ auto exceptionHandling = m_exceptionHandling;
+ auto allowThread = m_allowThread;
+
+ QString package = m_defaultPackage;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == streamAttribute) {
+ ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute, false));
+ } else if (name == privateAttribute) {
+ ctype->setPrivate(convertBoolean(attributes->takeAt(i).value(),
+ privateAttribute, false));
+ } else if (name == generateAttribute) {
+ generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true);
+ } else if (name ==packageAttribute) {
+ package = attributes->takeAt(i).value().toString();
+ } else if (name == defaultSuperclassAttribute) {
+ ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString());
+ } else if (name == genericClassAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ genericClassAttribute, false);
+ ctype->setGenericClass(v);
+ } else if (name == targetLangNameAttribute) {
+ ctype->setTargetLangName(attributes->takeAt(i).value().toString());
+ } else if (name == polymorphicBaseAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ polymorphicBaseAttribute, false);
+ ctype->setIsPolymorphicBase(v);
+ } else if (name == u"polymorphic-name-function") {
+ ctype->setPolymorphicNameFunction(attributes->takeAt(i).value().toString());
+ } else if (name == u"polymorphic-id-expression") {
+ ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString());
+ } else if (name == copyableAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ copyableAttribute, false);
+ ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet);
+ } else if (name == exceptionHandlingAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
+ if (exceptionOpt.has_value()) {
+ exceptionHandling = exceptionOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == allowThreadAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
+ if (allowThreadOpt.has_value()) {
+ allowThread = allowThreadOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == u"held-type") {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == u"hash-function") {
+ ctype->setHashFunction(attributes->takeAt(i).value().toString());
+ } else if (name == forceAbstractAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), forceAbstractAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
+ } else if (name == deprecatedAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
+ } else if (name == disableWrapperAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), disableWrapperAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DisableWrapper);
+ } else if (name == deleteInMainThreadAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute, false))
+ ctype->setDeleteInMainThread(true);
+ } else if (name == qtMetaObjectFunctionsAttribute) {
+ if (!convertBoolean(attributes->takeAt(i).value(),
+ qtMetaObjectFunctionsAttribute, true)) {
+ ctype->setTypeFlags(ctype->typeFlags()
+ | ComplexTypeEntry::DisableQtMetaObjectFunctions);
+ }
+ } else if (name == generateFunctionsAttribute) {
+ const auto names = attributes->takeAt(i).value();
+ const auto nameList = names.split(u';', Qt::SkipEmptyParts);
+ QSet<QString> nameSet;
+ for (const auto &name : nameList)
+ nameSet.insert(name.trimmed().toString());
+ ctype->setGenerateFunctions(nameSet);
+ } else if (name == u"target-type") {
+ ctype->setTargetType(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (snakeCaseOpt.has_value()) {
+ ctype->setSnakeCase(snakeCaseOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == isNullAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto boolCastOpt = boolCastFromAttribute(attribute.value());
+ if (boolCastOpt.has_value()) {
+ ctype->setIsNullMode(boolCastOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == operatorBoolAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto boolCastOpt = boolCastFromAttribute(attribute.value());
+ if (boolCastOpt.has_value()) {
+ ctype->setOperatorBoolMode(boolCastOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == qtMetaTypeAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto qtMetaTypeOpt = qtMetaTypeFromAttribute(attribute.value());
+ if (qtMetaTypeOpt.has_value()) {
+ ctype->setQtMetaTypeRegistration(qtMetaTypeOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == parentManagementAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ if (convertBoolean(attribute.value(), parentManagementAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ParentManagement);
+ ComplexTypeEntry::setParentManagementEnabled(true);
+ }
+ }
+
+ if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified)
+ ctype->setExceptionHandling(exceptionHandling);
+ if (allowThread != TypeSystem::AllowThread::Unspecified)
+ ctype->setAllowThread(allowThread);
+
+ // The generator code relies on container's package being empty.
+ if (ctype->type() != TypeEntry::ContainerType)
+ ctype->setTargetLangPackage(package);
+
+ if (generate)
+ ctype->setCodeGeneration(m_generate);
+ else
+ ctype->setCodeGeneration(TypeEntry::GenerationDisabled);
+}
+
+bool TypeSystemParser::parseConfiguration(StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)
+ && topElement != StackElement::EnumTypeEntry) {
+ m_error = u"<configuration> must be nested into a complex or enum type entry."_s;
+ return false;
+ }
+ QString condition;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"condition") {
+ condition = attributes->takeAt(i).value().toString();
+ }
+ }
+ if (condition.isEmpty()) {
+ m_error = u"<configuration> requires a \"condition\" attribute."_s;
+ return false;
+ }
+ const auto topEntry = m_contextStack.top()->entry;
+ const auto configurableEntry = std::dynamic_pointer_cast<ConfigurableTypeEntry>(topEntry);
+ Q_ASSERT(configurableEntry);
+ configurableEntry->setConfigCondition(condition);
+ return true;
+}
+
+bool TypeSystemParser::parseRenameFunction(const ConditionalStreamReader &,
+ QString *name, QXmlStreamAttributes *attributes)
+{
+ QString signature;
+ QString rename;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute) {
+ // Do not remove as it is needed for the type entry later on
+ signature = attributes->at(i).value().toString().simplified();
+ } else if (name == renameAttribute) {
+ rename = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (signature.isEmpty()) {
+ m_error = msgMissingAttribute(signatureAttribute);
+ return false;
+ }
+
+ *name = signature.left(signature.indexOf(u'(')).trimmed();
+
+ QString errorString = checkSignatureError(signature, u"function"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ if (!rename.isEmpty()) {
+ static const QRegularExpression functionNameRegExp(u"^[a-zA-Z_][a-zA-Z0-9_]*$"_s);
+ Q_ASSERT(functionNameRegExp.isValid());
+ if (!functionNameRegExp.match(rename).hasMatch()) {
+ m_error = u"can not rename '"_s + signature + u"', '"_s
+ + rename + u"' is not a valid function name"_s;
+ return false;
+ }
+ FunctionModification mod;
+ if (!mod.setSignature(signature, &m_error))
+ return false;
+ mod.setRenamedToName(rename);
+ mod.setModifierFlag(FunctionModification::Rename);
+ m_contextStack.top()->functionMods << mod;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ const bool isAddFunction = topElement == StackElement::AddFunction;
+ const bool validParent = isTypeEntry(topElement)
+ || topElement == StackElement::ModifyFunction
+ || topElement == StackElement::ModifyField
+ || isAddFunction;
+ if (!validParent) {
+ m_error = u"inject-documentation must be inside modify-function, add-function"
+ "modify-field or other tags that creates a type"_s;
+ return false;
+ }
+
+ TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace;
+ TypeSystem::Language lang = TypeSystem::NativeCode;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"mode") {
+ const auto attribute = attributes->takeAt(i);
+ const auto modeOpt = docModificationFromAttribute(attribute.value());
+ if (!modeOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mode = modeOpt.value();
+ } else if (name == formatAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(attribute.value());
+ if (!langOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ lang = langOpt.value();
+ }
+ }
+
+ QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
+ DocModification mod(mode, signature);
+ mod.setFormat(lang);
+ if (hasFileSnippetAttributes(attributes)) {
+ const auto snippetOptional = readFileSnippet(attributes);
+ if (!snippetOptional.has_value())
+ return false;
+ mod.setCode(snippetOptional.value().content);
+ }
+ auto &top = m_contextStack.top();
+ if (isAddFunction)
+ top->addedFunctions.last()->addDocModification(mod);
+ else
+ top->docModifications << mod;
+ return true;
+}
+
+bool TypeSystemParser::parseModifyDocumentation(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ const bool validParent = isTypeEntry(topElement)
+ || topElement == StackElement::ModifyFunction
+ || topElement == StackElement::ModifyField;
+ if (!validParent) {
+ m_error = u"modify-documentation must be inside modify-function, "
+ "modify-field or other tags that creates a type"_s;
+ return false;
+ }
+
+ const auto xpathIndex = indexOfAttribute(*attributes, xPathAttribute);
+ if (xpathIndex == -1) {
+ m_error = msgMissingAttribute(xPathAttribute);
+ return false;
+ }
+
+ const QString xpath = attributes->takeAt(xpathIndex).value().toString();
+ QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
+ m_contextStack.top()->docModifications
+ << DocModification(xpath, signature);
+ return true;
+}
+
+// m_exceptionHandling
+TypeSystemTypeEntryPtr TypeSystemParser::parseRootElement(const ConditionalStreamReader &,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
+ QString subModuleOf;
+ QString namespaceBegin;
+ QString namespaceEnd;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == packageAttribute) {
+ m_defaultPackage = attributes->takeAt(i).value().toString();
+ } else if (name == defaultSuperclassAttribute) {
+ m_defaultSuperclass = attributes->takeAt(i).value().toString();
+ } else if (name == exceptionHandlingAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
+ if (exceptionOpt.has_value()) {
+ m_exceptionHandling = exceptionOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == allowThreadAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
+ if (allowThreadOpt.has_value()) {
+ m_allowThread = allowThreadOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (snakeCaseOpt.has_value()) {
+ snakeCase = snakeCaseOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == subModuleOfAttribute) {
+ subModuleOf = attributes->takeAt(i).value().toString();
+ } else if (name == "namespace-begin"_L1) {
+ namespaceBegin = attributes->takeAt(i).value().toString();
+ } else if (name == "namespace-end"_L1) {
+ namespaceEnd = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (m_defaultPackage.isEmpty()) { // Extending default, see addBuiltInContainerTypes()
+ auto moduleEntry = std::const_pointer_cast<TypeSystemTypeEntry>(m_context->db->defaultTypeSystemType());
+ Q_ASSERT(moduleEntry);
+ m_defaultPackage = moduleEntry->name();
+ return moduleEntry;
+ }
+
+ auto moduleEntry =
+ std::const_pointer_cast<TypeSystemTypeEntry>(m_context->db->findTypeSystemType(m_defaultPackage));
+ const bool add = !moduleEntry;
+ if (add) {
+ moduleEntry.reset(new TypeSystemTypeEntry(m_defaultPackage, since,
+ currentParentTypeEntry()));
+ moduleEntry->setSubModule(subModuleOf);
+ }
+ moduleEntry->setCodeGeneration(m_generate);
+ moduleEntry->setSnakeCase(snakeCase);
+ if (!namespaceBegin.isEmpty())
+ moduleEntry->setNamespaceBegin(namespaceBegin);
+ if (!namespaceEnd.isEmpty())
+ moduleEntry->setNamespaceEnd(namespaceEnd);
+
+ if ((m_generate == TypeEntry::GenerateForSubclass ||
+ m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty())
+ TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage);
+
+ if (add)
+ m_context->db->addTypeSystemType(moduleEntry);
+ return moduleEntry;
+}
+
+bool TypeSystemParser::loadTypesystem(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ QString typeSystemName;
+ bool generateChild = true;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute)
+ typeSystemName = attributes->takeAt(i).value().toString();
+ else if (name == generateAttribute)
+ generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true);
+ }
+ if (typeSystemName.isEmpty()) {
+ m_error = u"No typesystem name specified"_s;
+ return false;
+ }
+ const bool result =
+ m_context->db->parseFile(m_context, typeSystemName, m_currentPath,
+ generateChild && m_generate == TypeEntry::GenerateCode);
+ if (!result)
+ m_error = u"Failed to parse: '"_s + typeSystemName + u'\'';
+ return result;
+}
+
+bool TypeSystemParser::parseRejectEnumValue(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ if (!m_currentEnum) {
+ m_error = u"<reject-enum-value> node must be used inside a <enum-type> node"_s;
+ return false;
+ }
+ const auto nameIndex = indexOfAttribute(*attributes, nameAttribute);
+ if (nameIndex == -1) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString());
+ return true;
+}
+
+bool TypeSystemParser::parseReplaceArgumentType(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"Type replacement can only be specified for argument modifications"_s;
+ return false;
+ }
+ const auto modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute);
+ if (modifiedTypeIndex == -1) {
+ m_error = u"Type replacement requires 'modified-type' attribute"_s;
+ return false;
+ }
+ m_contextStack.top()->functionMods.last().argument_mods().last().setModifiedType(
+ attributes->takeAt(modifiedTypeIndex).value().toString());
+ return true;
+}
+
+bool TypeSystemParser::parseCustomConversion(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument
+ && topElement != StackElement::ValueTypeEntry
+ && topElement != StackElement::PrimitiveTypeEntry
+ && topElement != StackElement::ContainerTypeEntry) {
+ m_error = u"Conversion rules can only be specified for argument modification, "
+ "value-type, primitive-type or container-type conversion."_s;
+ return false;
+ }
+
+ QString sourceFile;
+ QString snippetLabel;
+ TypeSystem::Language lang = TypeSystem::NativeCode;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == classAttribute) {
+ const auto languageAttribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(languageAttribute.value());
+ if (!langOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(languageAttribute);
+ return false;
+ }
+ lang = langOpt.value();
+ } else if (name == u"file") {
+ sourceFile = attributes->takeAt(i).value().toString();
+ } else if (name == snippetAttribute) {
+ snippetLabel = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ const auto &top = m_contextStack.top();
+ if (topElement == StackElement::ModifyArgument) {
+ CodeSnip snip;
+ snip.language = lang;
+ top->functionMods.last().argument_mods().last().conversionRules().append(snip);
+ return true;
+ }
+
+ ValueTypeEntryPtr valueTypeEntry;
+ if (top->entry->isValue()) {
+ valueTypeEntry = std::static_pointer_cast<ValueTypeEntry>(top->entry);
+ if (valueTypeEntry->hasTargetConversionRule() || valueTypeEntry->hasCustomConversion()) {
+ m_error = u"Types can have only one conversion rule"_s;
+ return false;
+ }
+ }
+
+ // The old conversion rule tag that uses a file containing the conversion
+ // will be kept temporarily for compatibility reasons. FIXME PYSIDE7: Remove
+ if (valueTypeEntry != nullptr && !sourceFile.isEmpty()) {
+ if (m_generate != TypeEntry::GenerateForSubclass
+ && m_generate != TypeEntry::GenerateNothing) {
+ qWarning(lcShiboken, "Specifying conversion rules by \"file\" is deprecated.");
+ if (lang != TypeSystem::TargetLangCode)
+ return true;
+
+ QFile conversionSource(sourceFile);
+ if (!conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ m_error = msgCannotOpenForReading(conversionSource);
+ return false;
+ }
+ const auto conversionRuleOptional =
+ extractSnippet(QString::fromUtf8(conversionSource.readAll()), snippetLabel);
+ if (!conversionRuleOptional.has_value()) {
+ m_error = msgCannotFindSnippet(sourceFile, snippetLabel);
+ return false;
+ }
+ valueTypeEntry->setTargetConversionRule(conversionRuleOptional.value());
+ }
+ return true;
+ }
+
+ auto customConversion = std::make_shared<CustomConversion>(top->entry);
+ if (top->entry->isPrimitive())
+ std::static_pointer_cast<PrimitiveTypeEntry>(top->entry)->setCustomConversion(customConversion);
+ else if (top->entry->isContainer())
+ std::static_pointer_cast<ContainerTypeEntry>(top->entry)->setCustomConversion(customConversion);
+ else if (top->entry->isValue())
+ std::static_pointer_cast<ValueTypeEntry>(top->entry)->setCustomConversion(customConversion);
+ customConversionsForReview.append(customConversion);
+ return true;
+}
+
+bool TypeSystemParser::parseNativeToTarget(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ConversionRule) {
+ m_error = u"Native to Target conversion code can only be specified for custom conversion rules."_s;
+ return false;
+ }
+ CodeSnip snip;
+ if (!readCodeSnippet(attributes, &snip))
+ return false;
+ m_contextStack.top()->conversionCodeSnips.append(snip);
+ return true;
+}
+
+bool TypeSystemParser::parseAddConversion(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::TargetToNative) {
+ m_error = u"Target to Native conversions can only be added inside 'target-to-native' tags."_s;
+ return false;
+ }
+ QString sourceTypeName;
+ QString typeCheck;
+ CodeSnip snip;
+ if (!readCodeSnippet(attributes, &snip))
+ return false;
+
+ const auto &top = m_contextStack.top();
+ top->conversionCodeSnips.append(snip);
+
+ if (parserState() == ParserState::ArgumentTargetToNativeConversion)
+ return true;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"type")
+ sourceTypeName = attributes->takeAt(i).value().toString();
+ else if (name == u"check")
+ typeCheck = attributes->takeAt(i).value().toString();
+ }
+
+ if (sourceTypeName.isEmpty()) {
+ m_error = u"Target to Native conversions must specify the input type with the 'type' attribute."_s;
+ return false;
+ }
+ auto customConversion = CustomConversion::getCustomConversion(top->entry);
+ if (!customConversion) {
+ m_error = msgMissingCustomConversion(top->entry);
+ return false;
+ }
+ customConversion->addTargetToNativeConversion(sourceTypeName, typeCheck);
+ return true;
+}
+
+static bool parseIndex(const QString &index, int *result, QString *errorMessage)
+{
+ bool ok = false;
+ *result = index.toInt(&ok);
+ if (!ok)
+ *errorMessage = QString::fromLatin1("Cannot convert '%1' to integer").arg(index);
+ return ok;
+}
+
+static bool parseArgumentIndex(const QString &index, int *result, QString *errorMessage)
+{
+ if (index == u"return") {
+ *result = 0;
+ return true;
+ }
+ if (index == u"this") {
+ *result = -1;
+ return true;
+ }
+ return parseIndex(index, result, errorMessage);
+}
+
+bool TypeSystemParser::parseModifyArgument(const ConditionalStreamReader &,
+ StackElement topElement, QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyFunction
+ && topElement != StackElement::AddFunction
+ && topElement != StackElement::DeclareFunction) {
+ m_error = u"Argument modification requires <modify-function>,"
+ " <add-function> or <declare-function> as parent, was "_s
+ + tagFromElement(topElement).toString();
+ return false;
+ }
+
+ QString index;
+ QString renameTo;
+ QString pyiType;
+ bool resetAfterUse = false;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == indexAttribute) {
+ index = attributes->takeAt(i).value().toString();
+ } else if (name == invalidateAfterUseAttribute) {
+ resetAfterUse = convertBoolean(attributes->takeAt(i).value(),
+ invalidateAfterUseAttribute, false);
+ } else if (name == renameAttribute) {
+ renameTo = attributes->takeAt(i).value().toString();
+ } else if (name == pyiTypeAttribute) {
+ pyiType = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (index.isEmpty()) {
+ m_error = msgMissingAttribute(indexAttribute);
+ return false;
+ }
+
+ int idx;
+ if (!parseArgumentIndex(index, &idx, &m_error))
+ return false;
+
+ ArgumentModification argumentModification = ArgumentModification(idx);
+ argumentModification.setResetAfterUse(resetAfterUse);
+ argumentModification.setRenamedToName(renameTo);
+ argumentModification.setPyiType(pyiType);
+ m_contextStack.top()->functionMods.last().argument_mods().append(argumentModification);
+ return true;
+}
+
+bool TypeSystemParser::parseNoNullPointer(const ConditionalStreamReader &reader,
+ StackElement topElement, QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"no-null-pointer requires argument modification as parent"_s;
+ return false;
+ }
+
+ ArgumentModification &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ lastArgMod.setNoNullPointers(true);
+
+ const int defaultValueIndex =
+ indexOfAttribute(*attributes, u"default-value");
+ if (defaultValueIndex != -1) {
+ const QXmlStreamAttribute attribute = attributes->takeAt(defaultValueIndex);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, attribute)));
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseDefineOwnership(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"define-ownership requires argument modification as parent"_s;
+ return false;
+ }
+
+ TypeSystem::Language lang = TypeSystem::TargetLangCode;
+ std::optional<TypeSystem::Ownership> ownershipOpt;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == classAttribute) {
+ const auto classAttribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(classAttribute.value());
+ if (!langOpt.has_value() || langOpt.value() == TypeSystem::ShellCode) {
+ m_error = msgInvalidAttributeValue(classAttribute);
+ return false;
+ }
+ lang = langOpt.value();
+ } else if (name == ownershipAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ ownershipOpt = ownershipFromFromAttribute(attribute.value());
+ if (!ownershipOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ }
+ }
+
+ if (!ownershipOpt.has_value()) {
+ m_error = "unspecified ownership"_L1;
+ return false;
+ }
+ auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ switch (lang) {
+ case TypeSystem::TargetLangCode:
+ lastArgMod.setTargetOwnerShip(ownershipOpt.value());
+ break;
+ case TypeSystem::NativeCode:
+ lastArgMod.setNativeOwnership(ownershipOpt.value());
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+// ### fixme PySide7: remove (replaced by attribute).
+bool TypeSystemParser::parseRename(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"Argument modification parent required"_s;
+ return false;
+ }
+
+ const auto toIndex = indexOfAttribute(*attributes, toAttribute);
+ if (toIndex == -1) {
+ m_error = msgMissingAttribute(toAttribute);
+ return false;
+ }
+ const QString renamed_to = attributes->takeAt(toIndex).value().toString();
+ m_contextStack.top()->functionMods.last().argument_mods().last().setRenamedToName(renamed_to);
+ return true;
+}
+
+bool TypeSystemParser::parseModifyField(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ FieldModification fm;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ fm.setName(attributes->takeAt(i).value().toString());
+ } else if (name == removeAttribute) {
+ fm.setRemoved(convertRemovalAttribute(attributes->takeAt(i).value()));
+ } else if (name == opaqueContainerFieldAttribute) {
+ fm.setOpaqueContainer(convertBoolean(attributes->takeAt(i).value(),
+ opaqueContainerFieldAttribute, false));
+ } else if (name == readAttribute) {
+ fm.setReadable(convertBoolean(attributes->takeAt(i).value(), readAttribute, true));
+ } else if (name == writeAttribute) {
+ fm.setWritable(convertBoolean(attributes->takeAt(i).value(), writeAttribute, true));
+ } else if (name == renameAttribute) {
+ fm.setRenamedToName(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (snakeCaseOpt.has_value()) {
+ fm.setSnakeCase(snakeCaseOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ }
+ }
+ if (fm.name().isEmpty()) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_contextStack.top()->fieldMods << fm;
+ return true;
+}
+
+static bool parseOverloadNumber(const QXmlStreamAttribute &attribute, int *overloadNumber,
+ QString *errorMessage)
+{
+ bool ok;
+ *overloadNumber = attribute.value().toInt(&ok);
+ if (!ok || *overloadNumber < 0) {
+ *errorMessage = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseAddFunction(const ConditionalStreamReader &,
+ StackElement topElement,
+ StackElement t,
+ QXmlStreamAttributes *attributes)
+{
+ const bool validParent = isComplexTypeEntry(topElement)
+ || topElement == StackElement::Root
+ || topElement == StackElement::ContainerTypeEntry;
+ if (!validParent) {
+ m_error = QString::fromLatin1("Add/Declare function requires a complex/container type or a root tag as parent"
+ ", was=%1").arg(tagFromElement(topElement));
+ return false;
+ }
+
+ FunctionModification mod;
+ if (!(t == StackElement::AddFunction
+ ? parseBasicModifyFunctionAttributes(attributes, &mod)
+ : parseModifyFunctionAttributes(attributes, &mod))) {
+ return false;
+ }
+
+ QString originalSignature;
+ QString returnType;
+ bool staticFunction = false;
+ bool classMethod = false;
+ bool pythonOverride = false;
+ QString access;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute) {
+ originalSignature = attributes->takeAt(i).value().toString().simplified();
+ } else if (name == u"return-type") {
+ returnType = attributes->takeAt(i).value().toString();
+ } else if (name == staticAttribute) {
+ staticFunction = convertBoolean(attributes->takeAt(i).value(),
+ staticAttribute, false);
+ } else if (name == classmethodAttribute) {
+ classMethod = convertBoolean(attributes->takeAt(i).value(),
+ classmethodAttribute, false);
+ } else if (name == accessAttribute) {
+ access = attributes->takeAt(i).value().toString();
+ } else if (name == pythonOverrideAttribute) {
+ pythonOverride = convertBoolean(attributes->takeAt(i).value(),
+ pythonOverrideAttribute, false);
+ }
+ }
+
+ QString signature = TypeDatabase::normalizedAddedFunctionSignature(originalSignature);
+ if (signature.isEmpty()) {
+ m_error = u"No signature for the added function"_s;
+ return false;
+ }
+
+ QString errorString = checkSignatureError(signature, u"add-function"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ AddedFunctionPtr func = AddedFunction::createAddedFunction(signature, returnType, &errorString);
+ if (!func) {
+ m_error = errorString;
+ return false;
+ }
+
+ func->setStatic(staticFunction);
+ func->setClassMethod(classMethod);
+ func->setPythonOverride(pythonOverride);
+ func->setTargetLangPackage(m_defaultPackage);
+
+ // Create signature for matching modifications
+ signature = TypeDatabase::normalizedSignature(originalSignature);
+ if (!signature.contains(u'('))
+ signature += u"()"_s;
+ m_currentSignature = signature;
+
+ if (!access.isEmpty()) {
+ const auto acessOpt = addedFunctionAccessFromAttribute(access);
+ if (!acessOpt.has_value()) {
+ m_error = u"Bad access type '"_s + access + u'\'';
+ return false;
+ }
+ func->setAccess(acessOpt.value());
+ }
+ func->setDeclaration(t == StackElement::DeclareFunction);
+
+ m_contextStack.top()->addedFunctions << func;
+ m_contextStack.top()->addedFunctionModificationIndex =
+ m_contextStack.top()->functionMods.size();
+
+ if (!mod.setSignature(m_currentSignature, &m_error))
+ return false;
+ mod.setOriginalSignature(originalSignature);
+ m_contextStack.top()->functionMods << mod;
+ return true;
+}
+
+bool TypeSystemParser::parseAddPyMethodDef(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)) {
+ m_error = u"add-pymethoddef requires a complex type as parent, was="_s
+ + tagFromElement(topElement).toString();
+ return false;
+ }
+
+ TypeSystemPyMethodDefEntry def;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ def.name = attributes->takeAt(i).value().toString();
+ } else if (name == u"doc") {
+ def.doc = attributes->takeAt(i).value().toString();
+ } else if (name == u"function") {
+ def.function = attributes->takeAt(i).value().toString();
+ } else if (name == u"flags") {
+ auto attribute = attributes->takeAt(i);
+ const auto flags = attribute.value().split(u'|', Qt::SkipEmptyParts);
+ for (const auto &flag : flags)
+ def.methFlags.append(flag.toString().toUtf8());
+ } else if (name == u"signatures") {
+ auto attribute = attributes->takeAt(i);
+ const auto signatures = attribute.value().split(u';', Qt::SkipEmptyParts);
+ for (const auto &signature : signatures)
+ def.signatures.append(signature.toString());
+ }
+ }
+
+ if (def.name.isEmpty() || def.function.isEmpty()) {
+ m_error = u"add-pymethoddef requires at least a name and a function attribute"_s;
+ return false;
+ }
+ std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addPyMethodDef(def);
+ return true;
+}
+
+bool TypeSystemParser::parseProperty(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)) {
+ m_error = QString::fromLatin1("Add property requires a complex type as parent"
+ ", was=%1").arg(tagFromElement(topElement));
+ return false;
+ }
+
+ TypeSystemProperty property;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ property.name = attributes->takeAt(i).value().toString();
+ } else if (name == u"get") {
+ property.read = attributes->takeAt(i).value().toString();
+ } else if (name == u"type") {
+ property.type = attributes->takeAt(i).value().toString();
+ } else if (name == u"set") {
+ property.write = attributes->takeAt(i).value().toString();
+ } else if (name == generateGetSetDefAttribute) {
+ property.generateGetSetDef =
+ convertBoolean(attributes->takeAt(i).value(),
+ generateGetSetDefAttribute, false);
+ }
+ }
+ if (!property.isValid()) {
+ m_error = u"<property> element is missing required attibutes (name/type/get)."_s;
+ return false;
+ }
+ std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addProperty(property);
+ return true;
+}
+
+// Parse basic attributes applicable to <add-function>/<declare-function>/<function>
+// and <modify-function> (all that is not done by injected code).
+bool TypeSystemParser::parseBasicModifyFunctionAttributes(QXmlStreamAttributes *attributes,
+ FunctionModification *mod)
+{
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == overloadNumberAttribute) {
+ int overloadNumber = TypeSystem::OverloadNumberUnset;
+ if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error))
+ return false;
+ mod->setOverloadNumber(overloadNumber);
+ }
+ }
+ return true;
+}
+
+// Parse attributes applicable to <declare-function>/<function>
+// and <modify-function>.
+bool TypeSystemParser::parseModifyFunctionAttributes(QXmlStreamAttributes *attributes,
+ FunctionModification *mod)
+{
+ if (!parseBasicModifyFunctionAttributes(attributes, mod))
+ return false;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == allowThreadAttribute) {
+ const QXmlStreamAttribute attribute = attributes->takeAt(i);
+ const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
+ if (!allowThreadOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mod->setAllowThread(allowThreadOpt.value());
+ } else if (name == exceptionHandlingAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
+ if (!exceptionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mod->setExceptionHandling(exceptionOpt.value());
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (!snakeCaseOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mod->setSnakeCase(snakeCaseOpt.value());
+ } else if (name == deprecatedAttribute) {
+ const bool deprecated = convertBoolean(attributes->takeAt(i).value(),
+ deprecatedAttribute, false);
+ mod->setModifierFlag(deprecated ? FunctionModification::Deprecated
+ : FunctionModification::Undeprecated);
+ }
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseModifyFunction(const ConditionalStreamReader &reader,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ const bool validParent = isComplexTypeEntry(topElement)
+ || topElement == StackElement::TypedefTypeEntry
+ || topElement == StackElement::FunctionTypeEntry;
+ if (!validParent) {
+ m_error = QString::fromLatin1("Modify function requires complex type as parent"
+ ", was=%1").arg(tagFromElement(topElement));
+ return false;
+ }
+
+ QString originalSignature;
+ FunctionModification mod;
+ if (!parseModifyFunctionAttributes(attributes, &mod))
+ return false;
+
+ QString access;
+ bool removed = false;
+ QString rename;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute) {
+ originalSignature = attributes->takeAt(i).value().toString().simplified();
+ } else if (name == accessAttribute) {
+ access = attributes->takeAt(i).value().toString();
+ } else if (name == renameAttribute) {
+ rename = attributes->takeAt(i).value().toString();
+ } else if (name == removeAttribute) {
+ removed = convertRemovalAttribute(attributes->takeAt(i).value());
+ } else if (name == virtualSlotAttribute || name == threadAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ }
+ }
+
+ // Child of global <function>
+ const auto &top = m_contextStack.top();
+ if (originalSignature.isEmpty() && top->entry->isFunction()) {
+ auto f = std::static_pointer_cast<const FunctionTypeEntry>(top->entry);
+ originalSignature = f->signatures().value(0);
+ }
+
+ const QString signature = TypeDatabase::normalizedSignature(originalSignature);
+ if (signature.isEmpty()) {
+ m_error = u"No signature for modified function"_s;
+ return false;
+ }
+
+ QString errorString = checkSignatureError(signature, u"modify-function"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ if (!mod.setSignature(signature, &m_error))
+ return false;
+ mod.setOriginalSignature(originalSignature);
+ m_currentSignature = signature;
+
+ if (!access.isEmpty()) {
+ const auto modifierFlagOpt = modifierFromAttribute(access);
+ if (!modifierFlagOpt.has_value()) {
+ m_error = u"Bad access type '"_s + access + u'\'';
+ return false;
+ }
+ const FunctionModification::ModifierFlag m = modifierFlagOpt.value();
+ if (m == FunctionModification::NonFinal) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeValueWarning(reader,
+ accessAttribute, access)));
+ }
+ mod.setModifierFlag(m);
+ }
+
+ mod.setRemoved(removed);
+
+ if (!rename.isEmpty()) {
+ mod.setRenamedToName(rename);
+ mod.setModifierFlag(FunctionModification::Rename);
+ }
+
+ top->functionMods << mod;
+ return true;
+}
+
+bool TypeSystemParser::parseReplaceDefaultExpression(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!(topElement & StackElement::ModifyArgument)) {
+ m_error = u"Replace default expression only allowed as child of argument modification"_s;
+ return false;
+ }
+ const auto withIndex = indexOfAttribute(*attributes, u"with");
+ if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) {
+ m_error = u"Default expression replaced with empty string. Use remove-default-expression instead."_s;
+ return false;
+ }
+
+ m_contextStack.top()->functionMods.last().argument_mods().last().setReplacedDefaultExpression(
+ attributes->takeAt(withIndex).value().toString());
+ return true;
+}
+
+bool TypeSystemParser::parseReferenceCount(const ConditionalStreamReader &reader,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"reference-count must be child of modify-argument"_s;
+ return false;
+ }
+
+ ReferenceCount rc;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == actionAttribute) {
+ const QXmlStreamAttribute attribute = attributes->takeAt(i);
+ const auto actionOpt = referenceCountFromAttribute(attribute.value());
+ if (!actionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ rc.action = actionOpt.value();
+ switch (rc.action) {
+ case ReferenceCount::AddAll:
+ case ReferenceCount::Ignore:
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeValueWarning(reader, attribute)));
+ break;
+ default:
+ break;
+ }
+ } else if (name == u"variable-name") {
+ rc.varName = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ m_contextStack.top()->functionMods.last().argument_mods().last().addReferenceCount(rc);
+ return true;
+}
+
+bool TypeSystemParser::parseParentOwner(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"parent-policy must be child of modify-argument"_s;
+ return false;
+ }
+ ArgumentOwner ao;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == indexAttribute) {
+ const QString index = attributes->takeAt(i).value().toString();
+ if (!parseArgumentIndex(index, &ao.index, &m_error))
+ return false;
+ } else if (name == actionAttribute) {
+ const auto action = attributes->takeAt(i);
+ const auto actionOpt = argumentOwnerActionFromAttribute(action.value());
+ if (!actionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(action);
+ return false;
+ }
+ ao.action = actionOpt.value();
+ }
+ }
+ m_contextStack.top()->functionMods.last().argument_mods().last().setOwner(ao);
+ return true;
+}
+
+std::optional<TypeSystemParser::Snippet>
+ TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes)
+{
+ Snippet result;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == fileAttribute) {
+ result.fileName = attributes->takeAt(i).value().toString();
+ } else if (name == snippetAttribute) {
+ result.snippetLabel = attributes->takeAt(i).value().toString();
+ }
+ }
+ if (result.fileName.isEmpty()) {
+ m_error = "Snippet missing file name"_L1;
+ return std::nullopt;
+ }
+ const QString resolved = m_context->db->modifiedTypesystemFilepath(result.fileName,
+ m_currentPath);
+ if (!QFile::exists(resolved)) {
+ m_error = u"File for inject code not exist: "_s
+ + QDir::toNativeSeparators(result.fileName);
+ return std::nullopt;
+ }
+ QFile codeFile(resolved);
+ if (!codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ m_error = msgCannotOpenForReading(codeFile);
+ return std::nullopt;
+ }
+ const auto contentOptional = extractSnippet(QString::fromUtf8(codeFile.readAll()),
+ result.snippetLabel);
+ codeFile.close();
+ if (!contentOptional.has_value()) {
+ m_error = msgCannotFindSnippet(resolved, result.snippetLabel);
+ return std::nullopt;
+ }
+ result.content = contentOptional.value();
+ return result;
+}
+
+bool TypeSystemParser::readCodeSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip)
+{
+ if (!hasFileSnippetAttributes(attributes))
+ return true; // Expecting inline content.
+ const auto snippetOptional = readFileSnippet(attributes);
+ if (!snippetOptional.has_value())
+ return false;
+ const auto snippet = snippetOptional.value();
+
+ QString source = snippet.fileName;
+ if (!snippet.snippetLabel.isEmpty())
+ source += " ("_L1 + snippet.snippetLabel + u')';
+ QString content;
+ QTextStream str(&content);
+ str << "// ========================================================================\n"
+ "// START of custom code block [file: "
+ << source << "]\n" << snippet.content
+ << "// END of custom code block [file: " << source
+ << "]\n// ========================================================================\n";
+ snip->addCode(content);
+ return true;
+}
+
+bool TypeSystemParser::parseInjectCode(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)
+ && (topElement != StackElement::AddFunction)
+ && (topElement != StackElement::ModifyFunction)
+ && (topElement != StackElement::Root)) {
+ m_error = u"wrong parent type for code injection"_s;
+ return false;
+ }
+
+ TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning;
+ TypeSystem::Language lang = TypeSystem::TargetLangCode;
+ CodeSnip snip;
+ if (!readCodeSnippet(attributes, &snip))
+ return false;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == classAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(attribute.value());
+ if (!langOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ lang = langOpt.value();
+ } else if (name == positionAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto positionOpt = codeSnipPositionFromAttribute(attribute.value());
+ if (!positionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ position = positionOpt.value();
+ }
+ }
+
+ snip.position = position;
+ snip.language = lang;
+
+ switch (topElement) {
+ case StackElement::ModifyFunction:
+ case StackElement::AddFunction: {
+ FunctionModification &mod = m_contextStack.top()->functionMods.last();
+ mod.appendSnip(snip);
+ if (!snip.code().isEmpty())
+ mod.setModifierFlag(FunctionModification::CodeInjection);
+ }
+ break;
+ case StackElement::Root:
+ std::static_pointer_cast<TypeSystemTypeEntry>(m_contextStack.top()->entry)->addCodeSnip(snip);
+ break;
+ default:
+ std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addCodeSnip(snip);
+ break;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseInclude(const ConditionalStreamReader &,
+ StackElement topElement,
+ const TypeEntryPtr &entry, QXmlStreamAttributes *attributes)
+{
+ QString fileName;
+ Include::IncludeType location = Include::IncludePath;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == fileNameAttribute) {
+ fileName = attributes->takeAt(i).value().toString();
+ } else if (name == locationAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto locationOpt = locationFromAttribute(attribute.value());
+ if (!locationOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ location = locationOpt.value();
+ }
+ }
+
+ Include inc(location, fileName);
+ if (isComplexTypeEntry(topElement)
+ || topElement == StackElement::PrimitiveTypeEntry
+ || topElement == StackElement::ContainerTypeEntry
+ || topElement == StackElement::SmartPointerTypeEntry
+ || topElement == StackElement::TypedefTypeEntry) {
+ entry->setInclude(inc);
+ } else if (topElement == StackElement::ExtraIncludes) {
+ entry->addExtraInclude(inc);
+ } else {
+ m_error = u"Only supported parent tags are primitive-type, complex types or extra-includes"_s;
+ return false;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseSystemInclude(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ const auto index = indexOfAttribute(*attributes, fileNameAttribute);
+ if (index == -1) {
+ m_error = msgMissingAttribute(fileNameAttribute);
+ return false;
+ }
+ TypeDatabase::instance()->addForceProcessSystemInclude(attributes->takeAt(index).value().toString());
+ return true;
+}
+
+TemplateInstance *
+ TypeSystemParser::parseInsertTemplate(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if ((topElement != StackElement::InjectCode) &&
+ (topElement != StackElement::Template) &&
+ (topElement != StackElement::NativeToTarget) &&
+ (topElement != StackElement::AddConversion) &&
+ (topElement != StackElement::ConversionRule)) {
+ m_error = u"Can only insert templates into code snippets, templates, "\
+ "conversion-rule, native-to-target or add-conversion tags."_s;
+ return nullptr;
+ }
+ const auto nameIndex = indexOfAttribute(*attributes, nameAttribute);
+ if (nameIndex == -1) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return nullptr;
+ }
+ return new TemplateInstance(attributes->takeAt(nameIndex).value().toString());
+}
+
+bool TypeSystemParser::parseReplace(const ConditionalStreamReader &,
+ StackElement topElement, QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::InsertTemplate) {
+ m_error = u"Can only insert replace rules into insert-template."_s;
+ return false;
+ }
+ QString from;
+ QString to;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"from")
+ from = attributes->takeAt(i).value().toString();
+ else if (name == toAttribute)
+ to = attributes->takeAt(i).value().toString();
+ }
+ m_templateInstance->addReplaceRule(from, to);
+ return true;
+}
+
+// Check for a duplicated type entry and return whether to add the new one.
+// We need to be able to have duplicate primitive type entries,
+// or it's not possible to cover all primitive target language
+// types (which we need to do in order to support fake meta objects)
+bool TypeSystemParser::checkDuplicatedTypeEntry(const ConditionalStreamReader &reader,
+ StackElement t,
+ const QString &name) const
+{
+ if (t == StackElement::PrimitiveTypeEntry || t == StackElement::FunctionTypeEntry)
+ return true;
+ const auto duplicated = m_context->db->findType(name);
+ if (!duplicated || duplicated->isNamespace())
+ return true;
+ if (duplicated->isBuiltIn()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgReaderMessage(reader, "Warning",
+ msgDuplicateBuiltInTypeEntry(name))));
+ return false;
+ }
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgReaderMessage(reader, "Warning",
+ msgDuplicateTypeEntry(name))));
+ return true;
+}
+
+static bool parseVersion(const QString &versionSpec, const QString &package,
+ QVersionNumber *result, QString *errorMessage)
+{
+ *result = QVersionNumber::fromString(versionSpec);
+ if (result->isNull()) {
+ *errorMessage = msgInvalidVersion(versionSpec, package);
+ return false;
+ }
+ return true;
+}
+
+bool TypeSystemParser::startElement(const ConditionalStreamReader &reader, StackElement element)
+{
+ if (m_ignoreDepth) {
+ ++m_ignoreDepth;
+ return true;
+ }
+
+ const auto tagName = reader.name();
+ QXmlStreamAttributes attributes = reader.attributes();
+
+ VersionRange versionRange;
+ for (auto i = attributes.size() - 1; i >= 0; --i) {
+ const auto name = attributes.at(i).qualifiedName();
+ if (name == sinceAttribute) {
+ if (!parseVersion(attributes.takeAt(i).value().toString(),
+ m_defaultPackage, &versionRange.since, &m_error)) {
+ return false;
+ }
+ } else if (name == untilAttribute) {
+ if (!parseVersion(attributes.takeAt(i).value().toString(),
+ m_defaultPackage, &versionRange.until, &m_error)) {
+ return false;
+ }
+ }
+ }
+
+ if (!m_defaultPackage.isEmpty() && !versionRange.isNull()) {
+ auto *td = TypeDatabase::instance();
+ if (!td->checkApiVersion(m_defaultPackage, versionRange)) {
+ ++m_ignoreDepth;
+ return true;
+ }
+ }
+
+ if (element == StackElement::ImportFile)
+ return importFileElement(attributes);
+
+ if (m_currentDroppedEntryDepth) {
+ ++m_currentDroppedEntryDepth;
+ return true;
+ }
+
+ if (element == StackElement::Root && m_generate == TypeEntry::GenerateCode)
+ customConversionsForReview.clear();
+
+ if (element == StackElement::Unimplemented) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedElementWarning(reader, tagName)));
+ return true;
+ }
+
+ if (isTypeEntry(element) || element == StackElement::Root)
+ m_contextStack.push(std::make_shared<StackElementContext>());
+
+ if (m_contextStack.isEmpty()) {
+ m_error = msgNoRootTypeSystemEntry();
+ return false;
+ }
+
+ const auto &top = m_contextStack.top();
+ const StackElement topElement = m_stack.value(m_stack.size() - 2, StackElement::None);
+
+ if (isTypeEntry(element)) {
+ QString name;
+ if (element != StackElement::FunctionTypeEntry) {
+ const auto nameIndex = indexOfAttribute(attributes, nameAttribute);
+ if (nameIndex != -1) {
+ name = attributes.takeAt(nameIndex).value().toString();
+ } else if (element != StackElement::EnumTypeEntry) { // anonymous enum?
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ }
+ // Allow for primitive and/or std:: types only, else require proper nesting.
+ if (element != StackElement::PrimitiveTypeEntry && name.contains(u':')
+ && !name.contains(u"std::")) {
+ m_error = msgIncorrectlyNestedName(name);
+ return false;
+ }
+
+ if (m_context->db->hasDroppedTypeEntries()) {
+ const QString identifier = element == StackElement::FunctionTypeEntry
+ ? attributes.value(signatureAttribute).toString().simplified() : name;
+ if (shouldDropTypeEntry(m_context->db, m_contextStack, identifier)) {
+ m_currentDroppedEntryDepth = 1;
+ if (ReportHandler::isDebug(ReportHandler::SparseDebug)) {
+ qCInfo(lcShiboken, "Type system entry '%s' was intentionally dropped from generation.",
+ qPrintable(identifier));
+ }
+ m_contextStack.pop();
+ return true;
+ }
+ }
+
+ // The top level tag 'function' has only the 'signature' tag
+ // and we should extract the 'name' value from it.
+ if (element == StackElement::FunctionTypeEntry
+ && !parseRenameFunction(reader, &name, &attributes)) {
+ return false;
+ }
+
+ // We need to be able to have duplicate primitive type entries,
+ // or it's not possible to cover all primitive target language
+ // types (which we need to do in order to support fake meta objects)
+ if (element != StackElement::PrimitiveTypeEntry
+ && element != StackElement::FunctionTypeEntry) {
+ TypeEntryPtr tmp = m_context->db->findType(name);
+ if (tmp && !tmp->isNamespace())
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Duplicate type entry: '" << name << '\'';
+ }
+
+ if (element == StackElement::EnumTypeEntry) {
+ const auto enumIdentifiedByIndex =
+ indexOfAttribute(attributes, enumIdentifiedByValueAttribute);
+ const QString identifiedByValue = enumIdentifiedByIndex != -1
+ ? attributes.takeAt(enumIdentifiedByIndex).value().toString() : QString();
+ if (name.isEmpty()) {
+ name = identifiedByValue;
+ } else if (!identifiedByValue.isEmpty()) {
+ m_error = u"can't specify both 'name' and 'identified-by-value' attributes"_s;
+ return false;
+ }
+ }
+
+ if (name.isEmpty()) {
+ m_error = u"no 'name' attribute specified"_s;
+ return false;
+ }
+
+ switch (element) {
+ case StackElement::CustomTypeEntry:
+ top->entry = parseCustomTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!top->entry))
+ return false;
+ break;
+ case StackElement::PrimitiveTypeEntry:
+ top->entry = parsePrimitiveTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!top->entry))
+ return false;
+ break;
+ case StackElement::ContainerTypeEntry:
+ top->entry = parseContainerTypeEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+
+ case StackElement::SmartPointerTypeEntry:
+ top->entry = parseSmartPointerEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ case StackElement::EnumTypeEntry:
+ m_currentEnum = parseEnumTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!m_currentEnum))
+ return false;
+ top->entry = m_currentEnum;
+ break;
+
+ case StackElement::ValueTypeEntry:
+ top->entry = parseValueTypeEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ case StackElement::NamespaceTypeEntry:
+ top->entry = parseNamespaceTypeEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ case StackElement::ObjectTypeEntry:
+ case StackElement::InterfaceTypeEntry: {
+ if (!checkRootElement())
+ return false;
+ auto ce = std::make_shared<ObjectTypeEntry>(name, versionRange.since, currentParentTypeEntry());
+ top->entry = ce;
+ applyCommonAttributes(reader, top->entry, &attributes);
+ applyComplexTypeAttributes(reader, ce, &attributes);
+ }
+ break;
+ case StackElement::FunctionTypeEntry:
+ top->entry = parseFunctionTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!top->entry))
+ return false;
+ break;
+ case StackElement::TypedefTypeEntry:
+ top->entry = parseTypedefEntry(reader, name, topElement,
+ versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if (top->entry) {
+ if (checkDuplicatedTypeEntry(reader, element, top->entry->name())
+ && !m_context->db->addType(top->entry, &m_error)) {
+ return false;
+ }
+ } else {
+ qCWarning(lcShiboken).noquote().nospace()
+ << u"Type: "_s + name + u" was rejected by typesystem"_s;
+ }
+
+ } else if (element == StackElement::InjectDocumentation) {
+ if (!parseInjectDocumentation(reader, topElement, &attributes))
+ return false;
+ } else if (element == StackElement::ModifyDocumentation) {
+ if (!parseModifyDocumentation(reader, topElement, &attributes))
+ return false;
+ } else if (element != StackElement::None) {
+ bool topLevel = element == StackElement::Root
+ || element == StackElement::SuppressedWarning
+ || element == StackElement::Rejection
+ || element == StackElement::LoadTypesystem
+ || element == StackElement::InjectCode
+ || element == StackElement::ExtraIncludes
+ || element == StackElement::SystemInclude
+ || element == StackElement::ConversionRule
+ || element == StackElement::AddFunction
+ || element == StackElement::DeclareFunction
+ || element == StackElement::Template
+ || element == StackElement::OpaqueContainer;
+
+ if (!topLevel && m_stack.at(m_stack.size() - 2) == StackElement::Root) {
+ m_error = u"Tag requires parent: '"_s + tagName.toString() + u'\'';
+ return false;
+ }
+
+ switch (element) {
+ case StackElement::Root:
+ top->entry = parseRootElement(reader, versionRange.since, &attributes);
+ break;
+ case StackElement::LoadTypesystem:
+ if (!loadTypesystem(reader, &attributes))
+ return false;
+ break;
+ case StackElement::RejectEnumValue:
+ if (!parseRejectEnumValue(reader, &attributes))
+ return false;
+ break;
+ case StackElement::ReplaceType:
+ if (!parseReplaceArgumentType(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ConversionRule:
+ if (!TypeSystemParser::parseCustomConversion(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::NativeToTarget:
+ if (!parseNativeToTarget(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::TargetToNative: {
+ if (topElement != StackElement::ConversionRule) {
+ m_error = u"Target to Native conversions can only be specified for custom conversion rules."_s;
+ return false;
+ }
+
+ const auto topParent = m_stack.value(m_stack.size() - 3, StackElement::None);
+ if (isTypeEntry(topParent)) {
+ const auto replaceIndex = indexOfAttribute(attributes, replaceAttribute);
+ const bool replace = replaceIndex == -1
+ || convertBoolean(attributes.takeAt(replaceIndex).value(),
+ replaceAttribute, true);
+ auto customConversion = CustomConversion::getCustomConversion(top->entry);
+ if (!customConversion) {
+ m_error = msgMissingCustomConversion(top->entry);
+ return false;
+ }
+ customConversion->setReplaceOriginalTargetToNativeConversions(replace);
+ }
+ }
+ break;
+ case StackElement::AddConversion:
+ if (!parseAddConversion(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ModifyArgument:
+ if (!parseModifyArgument(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::NoNullPointers:
+ if (!parseNoNullPointer(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::DefineOwnership:
+ if (!parseDefineOwnership(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::SuppressedWarning: {
+ const auto textIndex = indexOfAttribute(attributes, textAttribute);
+ if (textIndex == -1) {
+ qCWarning(lcShiboken) << "Suppressed warning with no text specified";
+ } else {
+ const QString suppressedWarning =
+ attributes.takeAt(textIndex).value().toString();
+ if (!m_context->db->addSuppressedWarning(suppressedWarning,
+ m_generate == TypeEntry::GenerateCode,
+ &m_error)) {
+ return false;
+ }
+ }
+ }
+ break;
+ case StackElement::Rename:
+ if (!parseRename(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::RemoveArgument:
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"Removing argument requires argument modification as parent"_s;
+ return false;
+ }
+
+ top->functionMods.last().argument_mods().last().setRemoved(true);
+ break;
+
+ case StackElement::ModifyField:
+ if (!parseModifyField(reader, &attributes))
+ return false;
+ break;
+ case StackElement::DeclareFunction:
+ case StackElement::AddFunction:
+ if (!parseAddFunction(reader, topElement, element, &attributes))
+ return false;
+ break;
+ case StackElement::AddPyMethodDef:
+ if (!parseAddPyMethodDef(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::Property:
+ if (!parseProperty(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ModifyFunction:
+ if (!parseModifyFunction(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ReplaceDefaultExpression:
+ if (!parseReplaceDefaultExpression(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::RemoveDefaultExpression:
+ top->functionMods.last().argument_mods().last().setRemovedDefaultExpression(true);
+ break;
+ case StackElement::ReferenceCount:
+ if (!parseReferenceCount(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ParentOwner:
+ if (!parseParentOwner(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::Array:
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"array must be child of modify-argument"_s;
+ return false;
+ }
+ top->functionMods.last().argument_mods().last().setArray(true);
+ break;
+ case StackElement::InjectCode:
+ if (!parseInjectCode(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::Include:
+ if (!parseInclude(reader, topElement, top->entry, &attributes))
+ return false;
+ break;
+ case StackElement::Rejection:
+ if (!addRejection(m_context->db, m_generate == TypeEntry::GenerateCode,
+ &attributes, &m_error)) {
+ return false;
+ }
+ break;
+ case StackElement::SystemInclude:
+ if (!parseSystemInclude(reader, &attributes))
+ return false;
+ break;
+ case StackElement::Template: {
+ const auto nameIndex = indexOfAttribute(attributes, nameAttribute);
+ if (nameIndex == -1) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_templateEntry.reset(new TemplateEntry(attributes.takeAt(nameIndex).value().toString()));
+ }
+ break;
+ case StackElement::InsertTemplate:
+ m_templateInstance.reset(parseInsertTemplate(reader, topElement, &attributes));
+ if (!m_templateInstance)
+ return false;
+ break;
+ case StackElement::Replace:
+ if (!parseReplace(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::OpaqueContainer:
+ if (!parseOpaqueContainerElement(&attributes))
+ case StackElement::Configuration:
+ if (!parseConfiguration(topElement, &attributes))
+ return false;
+ break;
+ default:
+ break; // nada
+ }
+ }
+
+ if (!attributes.isEmpty()) {
+ const QString message = msgUnusedAttributes(tagName, attributes);
+ qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message)));
+ }
+
+ return true;
+}
diff --git a/sources/shiboken6/ApiExtractor/typesystemparser_p.h b/sources/shiboken6/ApiExtractor/typesystemparser_p.h
new file mode 100644
index 000000000..4d9d4fd92
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystemparser_p.h
@@ -0,0 +1,297 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef TYPESYSTEMPARSER_H
+#define TYPESYSTEMPARSER_H
+
+#include "typesystem.h"
+#include "containertypeentry.h"
+#include "typedatabase.h"
+#include "typedatabase_p.h"
+#include "typesystem_typedefs.h"
+#include "codesnip.h"
+
+#include <QtCore/QStack>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+
+#include <memory>
+#include <optional>
+
+QT_FORWARD_DECLARE_CLASS(QVersionNumber)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamAttributes)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
+
+class ConditionalStreamReader;
+
+class TypeSystemEntityResolver;
+class TypeDatabase;
+
+class FlagsTypeEntry;
+class TypeSystemTypeEntry;
+class ValueTypeEntry;
+class EnumTypeEntry;
+
+enum class ParserState;
+
+enum class StackElement {
+ None,
+
+ // Type tags
+ ObjectTypeEntry,
+ FirstTypeEntry = ObjectTypeEntry,
+ ValueTypeEntry,
+ InterfaceTypeEntry,
+ NamespaceTypeEntry,
+ LastComplexTypeEntry = NamespaceTypeEntry,
+
+ // Non-complex type tags
+ PrimitiveTypeEntry,
+ EnumTypeEntry,
+ ContainerTypeEntry,
+ FunctionTypeEntry,
+ CustomTypeEntry,
+ SmartPointerTypeEntry,
+ TypedefTypeEntry,
+ LastTypeEntry = TypedefTypeEntry,
+
+ // Documentation tags
+ InjectDocumentation,
+ FirstDocumentation = InjectDocumentation,
+ ModifyDocumentation,
+ LastDocumentation = ModifyDocumentation,
+
+ // Simple tags
+ ExtraIncludes,
+ Include,
+ ModifyFunction,
+ ModifyField,
+ Root,
+ SuppressedWarning,
+ Rejection,
+ LoadTypesystem,
+ RejectEnumValue,
+ Template,
+ InsertTemplate,
+ Replace,
+ AddFunction,
+ AddPyMethodDef,
+ DeclareFunction,
+ NativeToTarget,
+ TargetToNative,
+ AddConversion,
+ SystemInclude,
+ Property,
+
+ // Code snip tags
+ InjectCode,
+
+ // Function modifier tags
+ Rename, // (modify-argument)
+ ModifyArgument,
+ Thread,
+
+ // Argument modifier tags
+ ConversionRule,
+ ReplaceType,
+ ReplaceDefaultExpression,
+ RemoveArgument,
+ DefineOwnership,
+ RemoveDefaultExpression,
+ NoNullPointers,
+ ReferenceCount,
+ ParentOwner,
+ Array,
+ ArgumentModifiers,
+
+ ImportFile,
+ OpaqueContainer,
+ Configuration,
+ Unimplemented
+};
+
+inline uint64_t operator&(StackElement s1, StackElement s2)
+{
+ return uint64_t(s1) & uint64_t(s2);
+}
+
+inline StackElement operator|(StackElement s1, StackElement s2)
+{
+ return StackElement(uint64_t(s1) | uint64_t(s2));
+}
+
+struct StackElementContext
+{
+ CodeSnipList conversionCodeSnips;
+ AddedFunctionList addedFunctions;
+ FunctionModificationList functionMods;
+ FieldModificationList fieldMods;
+ DocModificationList docModifications;
+ TypeEntryPtr entry;
+ int addedFunctionModificationIndex = -1;
+};
+
+class TypeSystemParser
+{
+public:
+ Q_DISABLE_COPY_MOVE(TypeSystemParser)
+
+ using StackElementContextPtr = std::shared_ptr<StackElementContext>;
+ using ContextStack = QStack<StackElementContextPtr>;
+
+ explicit TypeSystemParser(const std::shared_ptr<TypeDatabaseParserContext> &context,
+ bool generate);
+ ~TypeSystemParser();
+
+ bool parse(ConditionalStreamReader &reader);
+
+ QString errorString() const { return m_error; }
+
+private:
+ struct Snippet
+ {
+ QString content;
+ QString fileName;
+ QString snippetLabel;
+ };
+
+ bool parseXml(ConditionalStreamReader &reader);
+ bool setupSmartPointerInstantiations();
+ bool startElement(const ConditionalStreamReader &reader, StackElement element);
+ SmartPointerTypeEntryPtr parseSmartPointerEntry(const ConditionalStreamReader &,
+ const QString &name,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes);
+ bool endElement(StackElement element);
+ template <class String> // QString/QStringRef
+ bool characters(const String &ch);
+
+ bool importFileElement(const QXmlStreamAttributes &atts);
+
+ TypeEntryCPtr currentParentTypeEntry() const;
+ bool checkRootElement();
+ bool applyCommonAttributes(const ConditionalStreamReader &reader,
+ const TypeEntryPtr &type,
+ QXmlStreamAttributes *attributes);
+ PrimitiveTypeEntryPtr
+ parsePrimitiveTypeEntry(const ConditionalStreamReader &, const QString &name,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ CustomTypeEntryPtr
+ parseCustomTypeEntry(const ConditionalStreamReader &, const QString &name,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ bool parseOpaqueContainers(QStringView s, OpaqueContainers *result);
+ ContainerTypeEntryPtr
+ parseContainerTypeEntry(const ConditionalStreamReader &, const QString &name,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ bool parseOpaqueContainerElement(QXmlStreamAttributes *attributes);
+ EnumTypeEntryPtr
+ parseEnumTypeEntry(const ConditionalStreamReader &, const QString &name,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ FlagsTypeEntryPtr
+ parseFlagsEntry(const ConditionalStreamReader &, const EnumTypeEntryPtr &enumEntry,
+ QString flagName, const QVersionNumber &since,
+ QXmlStreamAttributes *);
+
+ NamespaceTypeEntryPtr
+ parseNamespaceTypeEntry(const ConditionalStreamReader &,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes);
+
+ ValueTypeEntryPtr
+ parseValueTypeEntry(const ConditionalStreamReader &, const QString &name,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ FunctionTypeEntryPtr
+ parseFunctionTypeEntry(const ConditionalStreamReader &, const QString &name,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ TypedefEntryPtr
+ parseTypedefEntry(const ConditionalStreamReader &, const QString &name,
+ StackElement topElement,
+ const QVersionNumber &since, QXmlStreamAttributes *);
+ void applyComplexTypeAttributes(const ConditionalStreamReader &, const ComplexTypeEntryPtr &ctype,
+ QXmlStreamAttributes *) const;
+ bool parseConfiguration(StackElement topElement,
+ QXmlStreamAttributes *attributes);
+ bool parseRenameFunction(const ConditionalStreamReader &, QString *name,
+ QXmlStreamAttributes *);
+ bool parseInjectDocumentation(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseModifyDocumentation(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ TypeSystemTypeEntryPtr
+ parseRootElement(const ConditionalStreamReader &, const QVersionNumber &since,
+ QXmlStreamAttributes *);
+ bool loadTypesystem(const ConditionalStreamReader &, QXmlStreamAttributes *);
+ bool parseRejectEnumValue(const ConditionalStreamReader &, QXmlStreamAttributes *);
+ bool parseReplaceArgumentType(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseCustomConversion(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseAddConversion(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseNativeToTarget(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes);
+ bool parseModifyArgument(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes);
+ bool parseNoNullPointer(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes);
+ bool parseDefineOwnership(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseRename(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseModifyField(const ConditionalStreamReader &, QXmlStreamAttributes *);
+ bool parseAddFunction(const ConditionalStreamReader &, StackElement topElement,
+ StackElement t, QXmlStreamAttributes *);
+ bool parseAddPyMethodDef(const ConditionalStreamReader &,
+ StackElement topElement, QXmlStreamAttributes *attributes);
+ bool parseProperty(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseBasicModifyFunctionAttributes(QXmlStreamAttributes *,
+ FunctionModification *mod);
+ bool parseModifyFunctionAttributes(QXmlStreamAttributes *,
+ FunctionModification *mod);
+ bool parseModifyFunction(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseReplaceDefaultExpression(const ConditionalStreamReader &,
+ StackElement topElement, QXmlStreamAttributes *);
+ bool parseReferenceCount(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseParentOwner(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ std::optional<Snippet> readFileSnippet(QXmlStreamAttributes *attributes);
+ bool readCodeSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip);
+ bool parseInjectCode(const ConditionalStreamReader &, StackElement topElement, QXmlStreamAttributes *);
+ bool parseInclude(const ConditionalStreamReader &, StackElement topElement,
+ const TypeEntryPtr &entry, QXmlStreamAttributes *);
+ bool parseSystemInclude(const ConditionalStreamReader &, QXmlStreamAttributes *);
+ TemplateInstance
+ *parseInsertTemplate(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool parseReplace(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *);
+ bool checkDuplicatedTypeEntry(const ConditionalStreamReader &reader,
+ StackElement t, const QString &name) const;
+ ParserState parserState(qsizetype offset = 0) const;
+ CodeSnipAbstract *injectCodeTarget(qsizetype offset = 0) const;
+
+ std::shared_ptr<TypeDatabaseParserContext> m_context;
+ QStack<StackElement> m_stack;
+ int m_currentDroppedEntryDepth = 0;
+ int m_ignoreDepth = 0;
+ QString m_defaultPackage;
+ QString m_defaultSuperclass;
+ TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified;
+ TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified;
+ QString m_error;
+ const TypeEntry::CodeGeneration m_generate;
+
+ EnumTypeEntryPtr m_currentEnum;
+ TemplateInstancePtr m_templateInstance;
+ TemplateEntryPtr m_templateEntry;
+ ContextStack m_contextStack;
+
+ QString m_currentSignature;
+ QString m_currentPath;
+ QString m_currentFile;
+ QScopedPointer<TypeSystemEntityResolver> m_entityResolver;
+};
+
+#endif // TYPESYSTEMPARSER_H
diff --git a/sources/shiboken6/ApiExtractor/typesystemtypeentry.h b/sources/shiboken6/ApiExtractor/typesystemtypeentry.h
new file mode 100644
index 000000000..9b9670696
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystemtypeentry.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef TYPESYSTEMTYPEENTRY_H
+#define TYPESYSTEMTYPEENTRY_H
+
+#include "typesystem.h"
+#include "modifications_typedefs.h"
+#include "typesystem_enums.h"
+#include "typesystem_typedefs.h"
+
+class TypeSystemTypeEntry : public TypeEntry
+{
+public:
+ explicit TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ TypeEntry *clone() const override;
+
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase sc);
+
+ const CodeSnipList &codeSnips() const;
+ CodeSnipList &codeSnips();
+ void addCodeSnip(const CodeSnip &codeSnip);
+
+ QString subModuleOf() const;
+ void setSubModule(const QString &);
+
+ const QString &namespaceBegin() const;
+ void setNamespaceBegin(const QString &n);
+
+ const QString &namespaceEnd() const;
+ void setNamespaceEnd(const QString &n);
+
+protected:
+ explicit TypeSystemTypeEntry(TypeEntryPrivate *d);
+};
+
+#endif // TYPESYSTEMTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/usingmember.h b/sources/shiboken6/ApiExtractor/usingmember.h
new file mode 100644
index 000000000..346eab13c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/usingmember.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef USINGMEMBER_H
+#define USINGMEMBER_H
+
+#include "abstractmetalang_typedefs.h"
+#include "parser/codemodel_enums.h"
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+struct UsingMember // Introducing a base class member via 'using' directive
+{
+ QString memberName;
+ AbstractMetaClassCPtr baseClass;
+ Access access;
+};
+
+QDebug operator<<(QDebug debug, const UsingMember &d);
+
+#endif // USINGMEMBER_H
diff --git a/sources/shiboken6/ApiExtractor/valuetypeentry.h b/sources/shiboken6/ApiExtractor/valuetypeentry.h
new file mode 100644
index 000000000..97bc26803
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/valuetypeentry.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef VALUETYPEENTRY_H
+#define VALUETYPEENTRY_H
+
+#include "complextypeentry.h"
+#include "customconversion_typedefs.h"
+
+class ValueTypeEntry : public ComplexTypeEntry
+{
+public:
+ explicit ValueTypeEntry(const QString &entryName, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+
+ bool hasCustomConversion() const;
+ void setCustomConversion(const CustomConversionPtr &customConversion);
+ CustomConversionPtr customConversion() const;
+
+ // FIXME PYSIDE7: Remove
+ /// Set the target type conversion rule
+ void setTargetConversionRule(const QString &conversionRule);
+
+ /// Returns the target type conversion rule
+ QString targetConversionRule() const;
+
+ /// TODO-CONVERTER: mark as deprecated
+ bool hasTargetConversionRule() const;
+
+ bool isValue() const override;
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit ValueTypeEntry(const QString &entryName, Type t, const QVersionNumber &vr,
+ const TypeEntryCPtr &parent);
+ explicit ValueTypeEntry(ComplexTypeEntryPrivate *d);
+};
+
+#endif // VALUETYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/varargstypeentry.h b/sources/shiboken6/ApiExtractor/varargstypeentry.h
new file mode 100644
index 000000000..b2a4f4d30
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/varargstypeentry.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef VARARGSTYPEENTRY_H
+#define VARARGSTYPEENTRY_H
+
+#include "typesystem.h"
+
+class VarargsTypeEntry : public TypeEntry
+{
+public:
+ VarargsTypeEntry();
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit VarargsTypeEntry(TypeEntryPrivate *d);
+};
+
+#endif // VARARGSTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/voidtypeentry.h b/sources/shiboken6/ApiExtractor/voidtypeentry.h
new file mode 100644
index 000000000..372c7c01f
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/voidtypeentry.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef VOIDTYPEENTRY_H
+#define VOIDTYPEENTRY_H
+
+#include "typesystem.h"
+
+class VoidTypeEntry : public TypeEntry
+{
+public:
+ VoidTypeEntry();
+
+ TypeEntry *clone() const override;
+
+protected:
+ explicit VoidTypeEntry(TypeEntryPrivate *d);
+};
+
+#endif // VOIDTYPEENTRY_H
diff --git a/sources/shiboken6/ApiExtractor/xmlutils.cpp b/sources/shiboken6/ApiExtractor/xmlutils.cpp
new file mode 100644
index 000000000..ccacd4ce7
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/xmlutils.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "xmlutils.h"
+
+#include "xmlutils_libxslt.h"
+
+#include "qtcompat.h"
+
+using namespace Qt::StringLiterals;
+
+XQuery::XQuery() = default;
+
+XQuery::~XQuery() = default;
+
+QString XQuery::evaluate(QString xPathExpression, QString *errorMessage)
+{
+ // XQuery can't have invalid XML characters
+ xPathExpression.replace(u'&', u"&amp;"_s);
+ xPathExpression.replace(u'<', u"&lt;"_s);
+ return doEvaluate(xPathExpression, errorMessage);
+}
+
+std::shared_ptr<XQuery> XQuery::create(const QString &focus, QString *errorMessage)
+{
+#if defined(HAVE_LIBXSLT)
+ return libXml_createXQuery(focus, errorMessage);
+#else
+ *errorMessage = QLatin1StringView(__FUNCTION__) + u" is not implemented."_s;
+ return std::shared_ptr<XQuery>();
+#endif
+}
+
+QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMessage)
+{
+#if defined(HAVE_LIBXSLT)
+ return libXslt_transform(xml, xsl, errorMessage);
+#else
+ *errorMessage = QLatin1StringView(__FUNCTION__) + u" is not implemented."_s;
+ return xml;
+#endif
+}
diff --git a/sources/shiboken6/ApiExtractor/xmlutils.h b/sources/shiboken6/ApiExtractor/xmlutils.h
new file mode 100644
index 000000000..ac23c9c9c
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/xmlutils.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef XMLUTILS_H
+#define XMLUTILS_H
+
+#include <QtCore/QString>
+
+#include <memory>
+
+class XQuery
+{
+public:
+ Q_DISABLE_COPY_MOVE(XQuery)
+
+ virtual ~XQuery();
+
+ QString evaluate(QString xPathExpression, QString *errorMessage);
+
+ static std::shared_ptr<XQuery> create(const QString &focus, QString *errorMessage);
+
+protected:
+ XQuery();
+
+ virtual QString doEvaluate(const QString &xPathExpression, QString *errorMessage) = 0;
+};
+
+QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMessage);
+
+#endif // XMLUTILS_H
diff --git a/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp b/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp
new file mode 100644
index 000000000..5a9a26913
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/xmlutils_libxslt.cpp
@@ -0,0 +1,208 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "xmlutils_libxslt.h"
+#include "xmlutils.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+
+#include <libxml/xmlsave.h>
+#include <libxml/xpath.h>
+
+#include <cstdlib>
+#include <memory>
+
+using namespace Qt::StringLiterals;
+
+static void cleanup()
+{
+ xsltCleanupGlobals();
+ xmlCleanupParser();
+}
+
+static void ensureInitialized()
+{
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ xmlInitParser();
+ xsltInit();
+ qAddPostRoutine(cleanup);
+ }
+}
+
+// RAI Helpers for cleaning up libxml2/libxslt data
+
+struct XmlDocDeleter // for std::unique_ptr<xmlDoc>
+{
+ void operator()(xmlDocPtr xmlDoc) { xmlFreeDoc(xmlDoc); }
+};
+
+struct XmlXPathObjectDeleter
+{
+ void operator()(xmlXPathObjectPtr xPathObject) { xmlXPathFreeObject(xPathObject); }
+};
+
+struct XmlStyleSheetDeleter // for std::unique_ptr<xsltStylesheet>
+{
+ void operator()(xsltStylesheetPtr xslt) { xsltFreeStylesheet(xslt); }
+};
+
+struct XmlXPathContextDeleter
+{
+ void operator()(xmlXPathContextPtr xPathContext) { xmlXPathFreeContext(xPathContext); }
+};
+
+using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
+using XmlPathObjectUniquePtr = std::unique_ptr<xmlXPathObject, XmlXPathObjectDeleter>;
+using XmlStyleSheetUniquePtr = std::unique_ptr<xsltStylesheet, XmlStyleSheetDeleter>;
+using XmlXPathContextUniquePtr = std::unique_ptr<xmlXPathContext, XmlXPathContextDeleter>;
+
+// Helpers for formatting nodes obtained from XPATH queries
+
+static int qbXmlOutputWriteCallback(void *context, const char *buffer, int len)
+{
+ static_cast<QByteArray *>(context)->append(buffer, len);
+ return len;
+}
+
+static int qbXmlOutputCloseCallback(void * /* context */) { return 0; }
+
+static QByteArray formatNode(xmlNodePtr node, QString *errorMessage)
+{
+ QByteArray result;
+ xmlSaveCtxtPtr saveContext =
+ xmlSaveToIO(qbXmlOutputWriteCallback, qbXmlOutputCloseCallback,
+ &result, "UTF-8", 0);
+ if (!saveContext) {
+ *errorMessage = u"xmlSaveToIO() failed."_s;
+ return result;
+ }
+ const long saveResult = xmlSaveTree(saveContext, node);
+ xmlSaveClose(saveContext);
+ if (saveResult < 0)
+ *errorMessage = u"xmlSaveTree() failed."_s;
+ return result;
+}
+
+// XPath expressions
+class LibXmlXQuery : public XQuery
+{
+public:
+ explicit LibXmlXQuery(XmlDocUniquePtr &doc, XmlXPathContextUniquePtr &xpathContext) :
+ m_doc(std::move(doc)), m_xpathContext(std::move(xpathContext))
+ {
+ ensureInitialized();
+ }
+
+protected:
+ QString doEvaluate(const QString &xPathExpression, QString *errorMessage) override;
+
+private:
+ XmlDocUniquePtr m_doc;
+ XmlXPathContextUniquePtr m_xpathContext;
+};
+
+QString LibXmlXQuery::doEvaluate(const QString &xPathExpression, QString *errorMessage)
+{
+ const QByteArray xPathExpressionB = xPathExpression.toUtf8();
+ auto xPathExpressionX = reinterpret_cast<const xmlChar *>(xPathExpressionB.constData());
+
+ XmlPathObjectUniquePtr xPathObject(xmlXPathEvalExpression(xPathExpressionX, m_xpathContext.get()));
+ if (!xPathObject) {
+ *errorMessage = u"xmlXPathEvalExpression() failed for \""_s + xPathExpression
+ + u'"';
+ return QString();
+ }
+ QString result;
+ if (xmlNodeSetPtr nodeSet = xPathObject->nodesetval) {
+ for (int n = 0, count = nodeSet->nodeNr; n < count; ++n) {
+ auto node = nodeSet->nodeTab[n];
+ if (node->type == XML_ELEMENT_NODE) {
+ result += QString::fromLocal8Bit(formatNode(node, errorMessage));
+ if (!errorMessage->isEmpty())
+ return QString();
+ }
+ }
+ }
+ return result;
+}
+
+std::shared_ptr<XQuery> libXml_createXQuery(const QString &focus, QString *errorMessage)
+{
+ XmlDocUniquePtr doc(xmlReadFile(QFile::encodeName(focus).constData(),
+ "utf-8", XML_PARSE_NOENT));
+ if (!doc) {
+ *errorMessage = u"libxml2: Cannot set focus to "_s + QDir::toNativeSeparators(focus);
+ return {};
+ }
+ XmlXPathContextUniquePtr xpathContext(xmlXPathNewContext(doc.get()));
+ if (!xpathContext) {
+ *errorMessage = u"libxml2: xmlXPathNewContext() failed"_s;
+ return {};
+ }
+ return std::shared_ptr<XQuery>(new LibXmlXQuery(doc, xpathContext));
+}
+
+// XSLT transformation
+
+static constexpr auto xsltPrefix = R"(<?xml version="1.0" encoding="UTF-8" ?>
+ <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+)"_L1;
+
+QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage)
+{
+ ensureInitialized();
+ // Read XML data
+ if (!xsl.startsWith(u"<?xml")) {
+ xsl.prepend(xsltPrefix);
+ xsl.append(u"</xsl:transform>"_s);
+ }
+ const QByteArray xmlData = xml.toUtf8();
+
+ XmlDocUniquePtr xmlDoc(xmlReadMemory(xmlData.constData(), int(xmlData.size()),
+ "", "utf-8", XML_PARSE_NOENT));
+ if (!xmlDoc) {
+ *errorMessage = u"xmlParseMemory() failed for XML."_s;
+ return xml;
+ }
+
+ // Read XSL data as a XML file
+ const QByteArray xslData = xsl.toUtf8();
+ // xsltFreeStylesheet will delete this pointer
+ xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size());
+ if (!xslDoc) {
+ *errorMessage = u"xmlParseMemory() failed for XSL \""_s + xsl + u"\"."_s;
+ return xml;
+ };
+
+ // Parse XSL data
+ XmlStyleSheetUniquePtr xslt(xsltParseStylesheetDoc(xslDoc));
+ if (!xslt) {
+ *errorMessage = u"xsltParseStylesheetDoc() failed."_s;
+ return xml;
+ }
+
+ // Apply XSL
+ XmlDocUniquePtr xslResult(xsltApplyStylesheet(xslt.get(), xmlDoc.get(), nullptr));
+ xmlChar *buffer = nullptr;
+ int bufferSize;
+ QString result;
+ if (xsltSaveResultToString(&buffer, &bufferSize, xslResult.get(), xslt.get()) == 0) {
+ result = QString::fromUtf8(reinterpret_cast<char*>(buffer), bufferSize);
+ std::free(buffer);
+ } else {
+ *errorMessage = u"xsltSaveResultToString() failed."_s;
+ result = xml;
+ }
+ return result.trimmed();
+}
diff --git a/sources/shiboken6/ApiExtractor/xmlutils_libxslt.h b/sources/shiboken6/ApiExtractor/xmlutils_libxslt.h
new file mode 100644
index 000000000..0dd8eafcb
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/xmlutils_libxslt.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef XMLUTILS_LIBXSLT_H
+#define XMLUTILS_LIBXSLT_H
+
+#include <QtCore/QString>
+
+#include <memory>
+
+class XQuery;
+
+std::shared_ptr<XQuery> libXml_createXQuery(const QString &focus, QString *errorMessage);
+
+QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage);
+
+#endif // XMLUTILS_LIBXSLT_H
diff --git a/sources/shiboken6/ApiExtractor/xmlutils_qt.h b/sources/shiboken6/ApiExtractor/xmlutils_qt.h
new file mode 100644
index 000000000..274827044
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/xmlutils_qt.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#ifndef XMLUTILS_QT_H
+#define XMLUTILS_QT_H
+
+#include <QtCore/QString>
+
+#include <memory>
+
+class XQuery;
+
+std::shared_ptr<XQuery> qt_createXQuery(const QString &focus, QString *errorMessage);
+
+QString qt_xsl_transform(const QString &xml, QString xsl, QString *errorMessage);
+
+#endif // XMLUTILS_QT_H