diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-10-14 20:34:46 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-10-14 20:34:46 +0200 |
commit | 68ec9c643abf30cf22b9932ec82098cdebc08b98 (patch) | |
tree | 18e6db70e971b3e437145183d07ed017933ab64d | |
parent | 30724622333ffc8bce61f7e19217977eebbf9564 (diff) | |
parent | b0da5a06e147b02af0bf2d69364e3bfcc04327d5 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Change-Id: I46f5d2dc758d0e1f23377c91ba7496793461771e
20 files changed, 815 insertions, 309 deletions
diff --git a/build_scripts/main.py b/build_scripts/main.py index 8429c1fdb..84628d8e0 100644 --- a/build_scripts/main.py +++ b/build_scripts/main.py @@ -48,7 +48,7 @@ from textwrap import dedent import time from .config import config from .utils import get_python_dict -from .options import OPTION +from .options import DistUtilsCommandMixin, OPTION from .wheel_utils import (get_package_version, get_qt_version, get_package_timestamp, macos_plat_name, macos_pyside_min_deployment_target) @@ -284,57 +284,6 @@ def check_allowed_python_version(): qt_src_dir = '' -if OPTION["QT_VERSION"] is None: - OPTION["QT_VERSION"] = "5" -if OPTION["QMAKE"] is None: - OPTION["QMAKE"] = find_executable("qmake-qt5") -if OPTION["QMAKE"] is None: - OPTION["QMAKE"] = find_executable("qmake") - -# make qtinfo.py independent of relative paths. -if OPTION["QMAKE"] is not None and os.path.exists(OPTION["QMAKE"]): - OPTION["QMAKE"] = os.path.abspath(OPTION["QMAKE"]) -if OPTION["CMAKE"] is not None and os.path.exists(OPTION["CMAKE"]): - OPTION["CMAKE"] = os.path.abspath(OPTION["CMAKE"]) - -if len(OPTION["QMAKE"]) == 0: - print("qmake could not be found.") - sys.exit(1) -if not os.path.exists(OPTION["QMAKE"]): - print("'{}' does not exist.".format(OPTION["QMAKE"])) - sys.exit(1) - -if OPTION["CMAKE"] is None: - OPTION["CMAKE"] = find_executable("cmake") - -if OPTION["CMAKE"] is None: - print("cmake could not be found.") - sys.exit(1) -if not os.path.exists(OPTION["CMAKE"]): - print("'{}' does not exist.".format(OPTION["CMAKE"])) - sys.exit(1) - -# First element is default -available_mkspecs = ["msvc", "mingw", "ninja"] if sys.platform == "win32" else ["make", "ninja"] - -if OPTION["MAKESPEC"] is None: - OPTION["MAKESPEC"] = available_mkspecs[0] - -if OPTION["MAKESPEC"] not in available_mkspecs: - print('Invalid option --make-spec "{}". Available values are {}'.format(OPTION["MAKESPEC"], - available_mkspecs)) - sys.exit(1) - -if OPTION["JOBS"]: - if sys.platform == 'win32' and OPTION["NO_JOM"]: - print("Option --jobs can only be used with jom on Windows.") - sys.exit(1) - else: - if not OPTION["JOBS"].startswith('-j'): - OPTION["JOBS"] = '-j' + OPTION["JOBS"] -else: - OPTION["JOBS"] = '' - def is_debug_python(): return getattr(sys, "gettotalrefcount", None) is not None @@ -357,10 +306,41 @@ def prefix(): return name -# Single global instance of QtInfo to be used later in multiple code -# paths. -qtinfo = QtInfo() -qtinfo.setup(OPTION["QMAKE"], OPTION["QT_VERSION"]) +# Initialize, pull and checkout submodules +def prepare_sub_modules(): + print("Initializing submodules for PySide2 version: {}".format( + get_package_version())) + submodules_dir = os.path.join(setup_script_dir, "sources") + + # Create list of [name, desired branch, absolute path, desired + # branch] and determine whether all submodules are present + need_init_sub_modules = False + + for m in submodules: + module_name = m[0] + module_dir = m[1] if len(m) > 1 else '' + module_dir = os.path.join(submodules_dir, module_dir, module_name) + # Check for non-empty directory (repository checked out) + if not os.listdir(module_dir): + need_init_sub_modules = True + break + + if need_init_sub_modules: + git_update_cmd = ["git", "submodule", "update", "--init"] + if run_process(git_update_cmd) != 0: + m = "Failed to initialize the git submodules: update --init failed" + raise DistutilsSetupError(m) + git_pull_cmd = ["git", "submodule", "foreach", "git", "fetch", "--all"] + if run_process(git_pull_cmd) != 0: + m = "Failed to initialize the git submodules: git fetch --all failed" + raise DistutilsSetupError(m) + else: + print("All submodules present.") + + git_update_cmd = ["git", "submodule", "update"] + if run_process(git_update_cmd) != 0: + m = "Failed to checkout the correct git submodules SHA1s." + raise DistutilsSetupError(m) def prepare_build(): @@ -377,7 +357,7 @@ def prepare_build(): # locate Qt sources for the documentation if OPTION["QT_SRC"] is None: - install_prefix = qtinfo.prefix_dir + install_prefix = QtInfo().prefix_dir if install_prefix: global qt_src_dir # In-source, developer build @@ -387,9 +367,13 @@ def prepare_build(): qt_src_dir = os.path.join(os.path.dirname(install_prefix), 'Src', 'qtbase') -class PysideInstall(_install): +class PysideInstall(_install, DistUtilsCommandMixin): + + user_options = _install.user_options + DistUtilsCommandMixin.mixin_user_options + def __init__(self, *args, **kwargs): _install.__init__(self, *args, **kwargs) + DistUtilsCommandMixin.__init__(self) def initialize_options(self): _install.initialize_options(self) @@ -408,6 +392,10 @@ class PysideInstall(_install): # similar cases. self.warn_dir = False + def finalize_options(self): + DistUtilsCommandMixin.mixin_finalize_options(self) + _install.finalize_options(self) + def run(self): _install.run(self) print('--- Install completed ({}s)'.format(elapsed())) @@ -471,13 +459,17 @@ class PysideInstallLib(_install_lib): return outfiles -class PysideBuild(_build): +class PysideBuild(_build, DistUtilsCommandMixin): + + user_options = _build.user_options + DistUtilsCommandMixin.mixin_user_options def __init__(self, *args, **kwargs): _build.__init__(self, *args, **kwargs) + DistUtilsCommandMixin.__init__(self) def finalize_options(self): os_name_backup = os.name + DistUtilsCommandMixin.mixin_finalize_options(self) if sys.platform == 'darwin': self.plat_name = macos_plat_name() # This is a hack to circumvent the dubious check in @@ -498,7 +490,6 @@ class PysideBuild(_build): _build.initialize_options(self) self.make_path = None self.make_generator = None - self.debug = False self.script_dir = None self.sources_dir = None self.build_dir = None @@ -543,7 +534,7 @@ class PysideBuild(_build): py_scripts_dir = os.path.join(py_prefix, "bin") self.py_scripts_dir = py_scripts_dir - self.qtinfo = qtinfo + self.qtinfo = QtInfo() qt_dir = os.path.dirname(OPTION["QMAKE"]) qt_version = get_qt_version() @@ -583,7 +574,6 @@ class PysideBuild(_build): self.make_path = make_path self.make_generator = make_generator - self.debug = OPTION["DEBUG"] self.script_dir = script_dir self.st_build_dir = os.path.join(self.script_dir, self.build_lib) self.sources_dir = sources_dir diff --git a/build_scripts/options.py b/build_scripts/options.py index db2a7e367..eb5d438a0 100644 --- a/build_scripts/options.py +++ b/build_scripts/options.py @@ -38,10 +38,27 @@ ############################################################################# from __future__ import print_function +import distutils.log as log +from distutils.spawn import find_executable import sys import os import warnings +from .qtinfo import QtInfo + + +_AVAILABLE_MKSPECS = ["msvc", "mingw", "ninja"] if sys.platform == "win32" else ["make", "ninja"] + + +# Global options not which are not part of the commands +ADDITIONAL_OPTIONS = """ +Additional options: + --limited-api Use Limited API [yes/no] + ---macos-use-libc++ Use libc++ on macOS + --snapshot-build Snapshot build + --package-timestamp Package Timestamp +""" + def _warn_multiple_option(option): warnings.warn('Option "{}" occurs multiple times on the command line.'.format(option)) @@ -132,60 +149,192 @@ def option_value(*args, **kwargs): return options.option_value(*args, **kwargs) -# Declare options +def _jobs_option_value(): + """Option value for parallel builds.""" + value = option_value('parallel', short_option_name='j') + if value: + return '-j' + value if not value.startswith('-j') else value + return '' + + +# Declare options which need to be known when instantiating the DistUtils +# commands. OPTION = { "BUILD_TYPE": option_value("build-type"), "INTERNAL_BUILD_TYPE": option_value("internal-build-type"), - "DEBUG": has_option("debug"), - "RELWITHDEBINFO": has_option('relwithdebinfo'), - "QMAKE": option_value("qmake"), - "QT_VERSION": option_value("qt"), - "CMAKE": option_value("cmake"), - "OPENSSL": option_value("openssl"), - "SHIBOKEN_CONFIG_DIR": option_value("shiboken-config-dir"), - "ONLYPACKAGE": has_option("only-package"), - "STANDALONE": has_option("standalone"), - "MAKESPEC": option_value("make-spec"), - "IGNOREGIT": has_option("ignore-git"), - # don't generate documentation - "SKIP_DOCS": has_option("skip-docs"), - # don't include pyside2-examples - "NOEXAMPLES": has_option("no-examples"), # number of parallel build jobs - "JOBS": option_value('parallel', short_option_name='j'), + "JOBS": _jobs_option_value(), # Legacy, not used any more. "JOM": has_option('jom'), - # Do not use jom instead of nmake with msvc - "NO_JOM": has_option('no-jom'), - "BUILDTESTS": has_option("build-tests"), - "MACOS_ARCH": option_value("macos-arch"), "MACOS_USE_LIBCPP": has_option("macos-use-libc++"), - "MACOS_SYSROOT": option_value("macos-sysroot"), - "MACOS_DEPLOYMENT_TARGET": option_value("macos-deployment-target"), - "XVFB": has_option("use-xvfb"), - "REUSE_BUILD": has_option("reuse-build"), - "SKIP_CMAKE": has_option("skip-cmake"), - "SKIP_MAKE_INSTALL": has_option("skip-make-install"), - "SKIP_PACKAGING": has_option("skip-packaging"), - "SKIP_MODULES": option_value("skip-modules"), - "MODULE_SUBSET": option_value("module-subset"), - "RPATH_VALUES": option_value("rpath"), - "QT_CONF_PREFIX": option_value("qt-conf-prefix"), - "QT_SRC": option_value("qt-src-dir"), "QUIET": has_option('quiet', remove=False), - "VERBOSE_BUILD": has_option("verbose-build"), - "SANITIZE_ADDRESS": has_option("sanitize-address"), "SNAPSHOT_BUILD": has_option("snapshot-build"), "LIMITED_API": option_value("limited-api"), "PACKAGE_TIMESTAMP": option_value("package-timestamp"), - "SHORTER_PATHS": has_option("shorter-paths"), # This is used automatically by distutils.command.install object, to # specify the final installation location. - "FINAL_INSTALL_PREFIX": option_value("prefix", remove=False), + "FINAL_INSTALL_PREFIX": option_value("prefix", remove=False) # This is used to identify the template for doc builds - "DOC_BUILD_ONLINE": has_option("doc-build-online"), } _deprecated_option_jobs = option_value('jobs') if _deprecated_option_jobs: _warn_deprecated_option('jobs', 'parallel') OPTION["JOBS"] = _deprecated_option_jobs + + +class DistUtilsCommandMixin(object): + """Mixin for the DistUtils build/install commands handling the options.""" + + _finalized = False + + mixin_user_options = [ + ('debug', None, 'Build with debug information'), + ('relwithdebinfo', None, 'Build in release mode with debug information'), + ('only-package', None, 'Package only'), + ('standalone', None, 'Standalone build'), + ('ignore-git', None, 'Do update subrepositories'), + ('skip-docs', None, 'Skip documentation build'), + ('no-examples', None, 'Do not build examples'), + ('no-jom', None, 'Do not use jom (MSVC)'), + ('build-tests', None, 'Build tests'), + ('use-xvfb', None, 'Use Xvfb for testing'), + ('reuse-build', None, 'Reuse existing build'), + ('skip-cmake', None, 'Skip CMake step'), + ('skip-make-install', None, 'Skip install step'), + ('skip-packaging', None, 'Skip packaging step'), + ('verbose-build', None, 'Verbose build'), + ('sanitize-address', None, 'Build with address sanitizer'), + ('shorter-paths', None, 'Use shorter paths'), + ('doc-build-online', None, 'Build online documentation'), + ('qmake=', None, 'Path to qmake'), + ('qt=', None, 'Qt version'), + ('cmake=', None, 'Path to CMake'), + ('openssl=', None, 'Path to OpenSSL libraries'), + ('shiboken-config-dir=', None, 'shiboken configuration directory'), + ('make-spec=', None, 'Qt make-spec'), + ('macos-arch=', None, 'macOS architecture'), + ('macos-sysroot=', None, 'macOS sysroot'), + ('macos-deployment-target=', None, 'macOS deployment target'), + ('skip-modules=', None, 'Qt modules to be skipped'), + ('module-subset=', None, 'Qt modules to be built'), + ('rpath=', None, 'RPATH'), + ('qt-conf-prefix=', None, 'Qt configuration prefix'), + ('qt-src-dir=', None, 'Qt source directory')] + + def __init__(self): + self.debug = False + self.relwithdebinfo = False + self.only_package = False + self.standalone = False + self.ignore_git = False + self.skip_docs = False + self.no_examples = False + self.no_jom = False + self.build_tests = False + self.use_xvfb = False + self.reuse_build = False + self.skip_cmake = False + self.skip_make_install = False + self.skip_packaging = False + self.verbose_build = False + self.sanitize_address = False + self.snapshot_build = False + self.shorter_paths = False + self.doc_build_online = False + self.qmake = None + self.qt = '5' + self.cmake = None + self.openssl = None + self.shiboken_config_dir = None + self.make_spec = None + self.macos_arch = None + self.macos_sysroot = None + self.macos_deployment_target = None + self.skip_modules = None + self.module_subset = None + self.rpath = None + self.qt_conf_prefix = None + self.qt_src_dir = None + + def mixin_finalize_options(self): + # Bail out on 2nd call to mixin_finalize_options() since that is the + # build command following the install command when invoking + # setup.py install + if not DistUtilsCommandMixin._finalized: + DistUtilsCommandMixin._finalized = True + self._do_finalize() + + def _do_finalize(self): + if not self._determine_defaults_and_check(): + sys.exit(-1) + OPTION['DEBUG'] = self.debug + OPTION['RELWITHDEBINFO'] = self.relwithdebinfo + OPTION['ONLYPACKAGE'] = self.only_package + OPTION['STANDALONE'] = self.standalone + OPTION['IGNOREGIT'] = self.ignore_git + OPTION['SKIP_DOCS'] = self.skip_docs + OPTION['NOEXAMPLES'] = self.no_examples + OPTION['BUILDTESTS'] = self.build_tests + OPTION['NO_JOM'] = self.no_jom + OPTION['XVFB'] = self.use_xvfb + OPTION['REUSE_BUILD'] = self.reuse_build + OPTION['SKIP_CMAKE'] = self.skip_cmake + OPTION['SKIP_MAKE_INSTALL'] = self.skip_make_install + OPTION['SKIP_PACKAGING'] = self.skip_packaging + OPTION['VERBOSE_BUILD'] = self.verbose_build + if self.verbose_build: + log.set_verbosity(1) + OPTION['SANITIZE_ADDRESS'] = self.sanitize_address + OPTION['SHORTER_PATHS'] = self.shorter_paths + OPTION['DOC_BUILD_ONLINE'] = self.doc_build_online + # make qtinfo.py independent of relative paths. + qmake_abs_path = os.path.abspath(self.qmake) + OPTION['QMAKE'] = qmake_abs_path + OPTION['QT_VERSION'] = self.qt + QtInfo().setup(qmake_abs_path, self.qt) + OPTION['CMAKE'] = os.path.abspath(self.cmake) + OPTION['OPENSSL'] = self.openssl + OPTION['SHIBOKEN_CONFIG_DIR'] = self.shiboken_config_dir + OPTION['MAKESPEC'] = self.make_spec + OPTION['MACOS_ARCH'] = self.macos_arch + OPTION['MACOS_SYSROOT'] = self.macos_sysroot + OPTION['MACOS_DEPLOYMENT_TARGET'] = self.macos_deployment_target + OPTION['SKIP_MODULES'] = self.skip_modules + OPTION['MODULE_SUBSET'] = self.module_subset + OPTION['RPATH_VALUES'] = self.rpath + OPTION['QT_CONF_PREFIX'] = self.qt_conf_prefix + OPTION['QT_SRC'] = self.qt_src_dir + + def _determine_defaults_and_check(self): + if not self.cmake: + self.cmake = find_executable("cmake") + if not self.cmake: + print("cmake could not be found.") + return False + if not os.path.exists(self.cmake): + print("'{}' does not exist.".format(self.cmake)) + return False + + if not self.qmake: + self.qmake = find_executable("qmake") + if not self.qmake: + self.qmake = find_executable("qmake-qt5") + if not self.qmake: + print("qmake could not be found.") + return False + if not os.path.exists(self.qmake): + print("'{}' does not exist.".format(self.qmake)) + return False + + if not self.make_spec: + self.make_spec = _AVAILABLE_MKSPECS[0] + if self.make_spec not in _AVAILABLE_MKSPECS: + print('Invalid option --make-spec "{}". Available values are {}'.format(OPTION["MAKESPEC"], + _AVAILABLE_MKSPECS)) + return False + + if OPTION["JOBS"] and sys.platform == 'win32' and self.no_jom: + print("Option --jobs can only be used with jom on Windows.") + return False + + return True diff --git a/build_scripts/setup_runner.py b/build_scripts/setup_runner.py index b54c62796..6b944c2c8 100644 --- a/build_scripts/setup_runner.py +++ b/build_scripts/setup_runner.py @@ -46,13 +46,11 @@ import distutils.log as log from build_scripts.config import config from build_scripts.main import get_package_version, get_setuptools_extension_modules from build_scripts.main import cmd_class_dict -from build_scripts.options import OPTION +from build_scripts.options import ADDITIONAL_OPTIONS, OPTION from build_scripts.utils import run_process from setuptools import setup -if OPTION["VERBOSE_BUILD"]: - log.set_verbosity(1) class SetupRunner(object): @@ -166,6 +164,10 @@ class SetupRunner(object): """).format(exit_code, cmd_as_string) raise RuntimeError(msg) + if help_requested: + print(ADDITIONAL_OPTIONS) + + @staticmethod def run_setuptools_setup(): """ diff --git a/build_scripts/wheel_override.py b/build_scripts/wheel_override.py index e4147a5bc..20e6f942c 100644 --- a/build_scripts/wheel_override.py +++ b/build_scripts/wheel_override.py @@ -40,22 +40,22 @@ wheel_module_exists = False +import os +import sys +from .options import DistUtilsCommandMixin, OPTION +from distutils import log as logger +from email.generator import Generator +from .wheel_utils import get_package_version, get_qt_version, macos_plat_name + try: - import os - import sys - from distutils import log as logger from wheel import pep425tags from wheel.bdist_wheel import bdist_wheel as _bdist_wheel from wheel.bdist_wheel import safer_name as _safer_name from wheel.pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag from wheel.pep425tags import get_platform as wheel_get_platform - from email.generator import Generator from wheel import __version__ as wheel_version - from .options import OPTION - from .wheel_utils import get_package_version, get_qt_version, macos_plat_name - wheel_module_exists = True except Exception as e: _bdist_wheel, wheel_version = type, '' # dummy to make class statement happy @@ -67,12 +67,18 @@ def get_bdist_wheel_override(): return PysideBuildWheel if wheel_module_exists else None -class PysideBuildWheel(_bdist_wheel): +class PysideBuildWheel(_bdist_wheel, DistUtilsCommandMixin): + + user_options = (_bdist_wheel.user_options + DistUtilsCommandMixin.mixin_user_options + if wheel_module_exists else None) + def __init__(self, *args, **kwargs): self._package_version = None _bdist_wheel.__init__(self, *args, **kwargs) + DistUtilsCommandMixin.__init__(self) def finalize_options(self): + DistUtilsCommandMixin.mixin_finalize_options(self) if sys.platform == 'darwin': # Override the platform name to contain the correct # minimum deployment target. diff --git a/sources/pyside2/libpyside/pysideproperty.cpp b/sources/pyside2/libpyside/pysideproperty.cpp index 79464541d..d79a46ade 100644 --- a/sources/pyside2/libpyside/pysideproperty.cpp +++ b/sources/pyside2/libpyside/pysideproperty.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -47,6 +47,8 @@ #include <shiboken.h> #include <signature.h> +using namespace Shiboken; + extern "C" { @@ -55,25 +57,41 @@ static int qpropertyTpInit(PyObject *, PyObject *, PyObject *); static void qpropertyDeAlloc(PyObject *self); //methods -static PyObject *qPropertyCall(PyObject *, PyObject *, PyObject *); -static PyObject *qPropertySetter(PyObject *, PyObject *); static PyObject *qPropertyGetter(PyObject *, PyObject *); +static PyObject *qPropertySetter(PyObject *, PyObject *); +static PyObject *qPropertyResetter(PyObject *, PyObject *); +static PyObject *qPropertyDeleter(PyObject *, PyObject *); +static PyObject *qPropertyCall(PyObject *, PyObject *, PyObject *); static int qpropertyTraverse(PyObject *self, visitproc visit, void *arg); static int qpropertyClear(PyObject *self); // Attributes static PyObject *qPropertyDocGet(PyObject *, void *); +static int qPropertyDocSet(PyObject *, PyObject *, void *); +static PyObject *qProperty_fget(PyObject *, void *); +static PyObject *qProperty_fset(PyObject *, void *); +static PyObject *qProperty_freset(PyObject *, void *); +static PyObject *qProperty_fdel(PyObject *, void *); static PyMethodDef PySidePropertyMethods[] = { - {"setter", (PyCFunction)qPropertySetter, METH_O, 0}, - {"write", (PyCFunction)qPropertySetter, METH_O, 0}, {"getter", (PyCFunction)qPropertyGetter, METH_O, 0}, + {"setter", (PyCFunction)qPropertySetter, METH_O, 0}, + {"resetter", (PyCFunction)qPropertyResetter, METH_O, 0}, + {"deleter", (PyCFunction)qPropertyDeleter, METH_O, 0}, + // Synonyms from Qt {"read", (PyCFunction)qPropertyGetter, METH_O, 0}, + {"write", (PyCFunction)qPropertySetter, METH_O, 0}, {0, 0, 0, 0} }; static PyGetSetDef PySidePropertyType_getset[] = { - {const_cast<char *>("__doc__"), qPropertyDocGet, nullptr, nullptr, nullptr}, + // Note: we could not use `PyMemberDef` like Python's properties, + // because of the indirection of PySidePropertyPrivate. + {const_cast<char *>("fget"), qProperty_fget, nullptr, nullptr, nullptr}, + {const_cast<char *>("fset"), qProperty_fset, nullptr, nullptr, nullptr}, + {const_cast<char *>("freset"), qProperty_freset, nullptr, nullptr, nullptr}, + {const_cast<char *>("fdel"), qProperty_fdel, nullptr, nullptr, nullptr}, + {const_cast<char *>("__doc__"), qPropertyDocGet, qPropertyDocSet, nullptr, nullptr}, {nullptr, nullptr, nullptr, nullptr, nullptr} }; @@ -166,21 +184,26 @@ static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds) pData->metaCallHandler = &qpropertyMetaCall; static const char *kwlist[] = {"type", "fget", "fset", "freset", "fdel", "doc", "notify", - "designable", "scriptable", "stored", "user", - "constant", "final", 0}; + "designable", "scriptable", "stored", + "user", "constant", "final", 0}; char *doc{}; if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|OOOOsObbbbbb:QtCore.QProperty", + "O|OOOOsObbbbbb:QtCore.Property", const_cast<char **>(kwlist), /*OO*/ &type, &(pData->fget), /*OOO*/ &(pData->fset), &(pData->freset), &(pData->fdel), /*s*/ &doc, /*O*/ &(pData->notify), - /*bbbbbb*/ &(pData->designable), &(pData->scriptable), &(pData->stored), &(pData->user), &(pData->constant), &(pData->final))) { + /*bbb*/ &(pData->designable), &(pData->scriptable), &(pData->stored), + /*bbb*/ &(pData->user), &(pData->constant), &(pData->final))) { return -1; } + // PYSIDE-1019: Fetching the default `__doc__` from fget would fail for inherited functions + // because we don't initialize the mro with signatures (and we will not!). + // But it is efficient and in-time to do that on demand in qPropertyDocGet. + pData->getter_doc = false; if (doc) pData->doc = doc; else @@ -220,53 +243,107 @@ static void qpropertyDeAlloc(PyObject *self) Py_TYPE(self)->tp_free(self); } -static PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw */) +static PyObject * +_property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *reset, PyObject *del) { - PyObject *callback = PyTuple_GetItem(args, 0); - if (PyFunction_Check(callback)) { - auto prop = reinterpret_cast<PySideProperty *>(self); - PySidePropertyPrivate *pData = prop->d; + PySideProperty *pold = reinterpret_cast<PySideProperty *>(old); + PySidePropertyPrivate *pData = pold->d; - Py_INCREF(callback); - pData->fget = callback; + AutoDecRef type(PyObject_Type(old)); + QByteArray doc{}; + if (type.isNull()) + return nullptr; - Py_INCREF(self); - return self; + if (get == nullptr || get == Py_None) { + Py_XDECREF(get); + get = pData->fget ? pData->fget : Py_None; + } + if (set == nullptr || set == Py_None) { + Py_XDECREF(set); + set = pData->fset ? pData->fset : Py_None; + } + if (reset == nullptr || reset == Py_None) { + Py_XDECREF(reset); + reset = pData->freset ? pData->freset : Py_None; + } + if (del == nullptr || del == Py_None) { + Py_XDECREF(del); + del = pData->fdel ? pData->fdel : Py_None; + } + if (pData->getter_doc && get != Py_None) { + /* make _init use __doc__ from getter */ + doc = ""; + } + else { + doc = !pData->doc.isEmpty() ? pData->doc : ""; } - PyErr_SetString(PyExc_TypeError, "Invalid property usage."); - return nullptr; + auto notify = pData->notify ? pData->notify : Py_None; + + PyObject *typeName = String::fromCString(pData->typeName); + PyObject *obNew = PyObject_CallFunction(type, const_cast<char *>("OOOOOsO" "bbb" "bbb"), + typeName, get, set, reset, del, doc.data(), notify, + pData->designable, pData->scriptable, pData->stored, + pData->user, pData->constant, pData->final); + + return obNew; } -static PyObject *qPropertySetter(PyObject *self, PyObject *callback) +static PyObject *qPropertyGetter(PyObject *self, PyObject *getter) { - if (PyFunction_Check(callback)) { - PySideProperty *prop = reinterpret_cast<PySideProperty *>(self); - PySidePropertyPrivate *pData = prop->d; + return _property_copy(self, getter, nullptr, nullptr, nullptr); +} - Py_INCREF(callback); - pData->fset = callback; +static PyObject *qPropertySetter(PyObject *self, PyObject *setter) +{ + return _property_copy(self, nullptr, setter, nullptr, nullptr); +} - Py_INCREF(callback); - return callback; - } - PyErr_SetString(PyExc_TypeError, "Invalid property setter agument."); - return nullptr; +static PyObject *qPropertyResetter(PyObject *self, PyObject *resetter) +{ + return _property_copy(self, nullptr, nullptr, resetter, nullptr); } -static PyObject *qPropertyGetter(PyObject *self, PyObject *callback) +static PyObject *qPropertyDeleter(PyObject *self, PyObject *deleter) { - if (PyFunction_Check(callback)) { - PySideProperty *prop = reinterpret_cast<PySideProperty *>(self); - PySidePropertyPrivate *pData = prop->d; + return _property_copy(self, nullptr, nullptr, nullptr, deleter); +} - Py_INCREF(callback); - pData->fget = callback; +static PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw */) +{ + PyObject *getter = PyTuple_GetItem(args, 0); + return _property_copy(self, getter, nullptr, nullptr, nullptr); +} - Py_INCREF(callback); - return callback; - } - PyErr_SetString(PyExc_TypeError, "Invalid property getter agument."); - return nullptr; +// PYSIDE-1019: Provide the same getters as Pythons `PyProperty`. +static PyObject *_property_func(PyObject *self, ssize_t offset) +{ + auto data = reinterpret_cast<PySideProperty *>(self); + PySidePropertyPrivate *pData = data->d; + auto funcptr = reinterpret_cast<char *>(pData) + offset; + auto func = *reinterpret_cast<PyObject **>(funcptr); + auto ret = func != nullptr ? func : Py_None; + Py_INCREF(ret); + return ret; +} + +static PyObject *qProperty_fget(PyObject *self, void *) +{ + return _property_func(self, offsetof(PySidePropertyPrivate, fget)); +} + +static PyObject *qProperty_fset(PyObject *self, void *) +{ + return _property_func(self, offsetof(PySidePropertyPrivate, fset)); +} + +static PyObject *qProperty_freset(PyObject *self, void *) +{ + return _property_func(self, offsetof(PySidePropertyPrivate, freset)); +} + +static PyObject *qProperty_fdel(PyObject *self, void *) +{ + return _property_func(self, offsetof(PySidePropertyPrivate, fdel)); } static PyObject *qPropertyDocGet(PyObject *self, void *) @@ -282,9 +359,40 @@ static PyObject *qPropertyDocGet(PyObject *self, void *) return PyString_FromString(doc); #endif } + if (pData->fget != nullptr) { + // PYSIDE-1019: Fetch the default `__doc__` from fget. We do it late. + AutoDecRef get_doc(PyObject_GetAttr(pData->fget, PyMagicName::doc())); + if (!get_doc.isNull()) { + pData->doc = String::toCString(get_doc); + pData->getter_doc = true; + if (Py_TYPE(self) == PySidePropertyTypeF()) + return qPropertyDocGet(self, nullptr); + /* + * If this is a property subclass, put __doc__ in dict of the + * subclass instance instead, otherwise it gets shadowed by + * __doc__ in the class's dict. + */ + auto get_doc_obj = get_doc.object(); + int err = PyObject_SetAttr(self, PyMagicName::doc(), get_doc); + return err < 0 ? nullptr : (Py_INCREF(get_doc_obj), get_doc_obj); + } + PyErr_Clear(); + } Py_RETURN_NONE; } +static int qPropertyDocSet(PyObject *self, PyObject *value, void *) +{ + auto data = reinterpret_cast<PySideProperty *>(self); + PySidePropertyPrivate *pData = data->d; + + if (String::check(value)) { + pData->doc = String::toCString(value); + return 0; + } + PyErr_SetString(PyExc_TypeError, "String argument expected."); + return -1; +} static int qpropertyTraverse(PyObject *self, visitproc visit, void *arg) { @@ -345,14 +453,20 @@ static PyObject *getFromType(PyTypeObject *type, PyObject *name) namespace PySide { namespace Property { static const char *Property_SignatureStrings[] = { - "PySide2.QtCore.Property(type:type,fget:typing.Callable=None,fset:typing.Callable=None," + "PySide2.QtCore.Property(self,type:type,fget:typing.Callable=None,fset:typing.Callable=None," "freset:typing.Callable=None,fdel:typing.Callable=None,doc:str=None," "notify:typing.Callable=None,designable:bool=True,scriptable:bool=True," - "stored:bool=True,user:bool=False,constant:bool=False,final:bool=False)", - "PySide2.QtCore.Property.getter(func:typing.Callable)", - "PySide2.QtCore.Property.read(func:typing.Callable)", - "PySide2.QtCore.Property.setter(func:typing.Callable)", - "PySide2.QtCore.Property.write(func:typing.Callable)", + "stored:bool=True,user:bool=False,constant:bool=False,final:bool=False)" + "->PySide2.QtCore.Property", + "PySide2.QtCore.Property.deleter(self,func:typing.Callable)", + "PySide2.QtCore.Property.fdel(self)->typing.Callable", + "PySide2.QtCore.Property.fget(self)->typing.Callable", + "PySide2.QtCore.Property.freset(self)->typing.Callable", + "PySide2.QtCore.Property.fset(self)->typing.Callable", + "PySide2.QtCore.Property.getter(self,func:typing.Callable)", + "PySide2.QtCore.Property.read(self,func:typing.Callable)", + "PySide2.QtCore.Property.setter(self,func:typing.Callable)", + "PySide2.QtCore.Property.write(self,func:typing.Callable)", nullptr}; // Sentinel void init(PyObject *module) @@ -375,7 +489,7 @@ bool checkType(PyObject *pyObj) int setValue(PySideProperty *self, PyObject *source, PyObject *value) { PyObject *fset = self->d->fset; - if (fset) { + if (fset && value) { Shiboken::AutoDecRef args(PyTuple_New(2)); PyTuple_SET_ITEM(args, 0, source); PyTuple_SET_ITEM(args, 1, value); @@ -383,9 +497,16 @@ int setValue(PySideProperty *self, PyObject *source, PyObject *value) Py_INCREF(value); Shiboken::AutoDecRef result(PyObject_CallObject(fset, args)); return (result.isNull() ? -1 : 0); - } else { - PyErr_SetString(PyExc_AttributeError, "Attibute read only"); } + PyObject *fdel = self->d->fdel; + if (fdel) { + Shiboken::AutoDecRef args(PyTuple_New(1)); + PyTuple_SET_ITEM(args, 0, source); + Py_INCREF(source); + Shiboken::AutoDecRef result(PyObject_CallObject(fdel, args)); + return (result.isNull() ? -1 : 0); + } + PyErr_SetString(PyExc_AttributeError, "Attibute read only"); return -1; } diff --git a/sources/pyside2/libpyside/pysideproperty_p.h b/sources/pyside2/libpyside/pysideproperty_p.h index 4db638021..e7b6e4d77 100644 --- a/sources/pyside2/libpyside/pysideproperty_p.h +++ b/sources/pyside2/libpyside/pysideproperty_p.h @@ -56,6 +56,7 @@ struct PySidePropertyPrivate PyObject *freset = nullptr; PyObject *fdel = nullptr; PyObject *notify = nullptr; + bool getter_doc = false; QByteArray notifySignature; QByteArray doc; bool designable = true; diff --git a/sources/pyside2/tests/QtCore/qproperty_decorator.py b/sources/pyside2/tests/QtCore/qproperty_decorator.py index aa31e59c4..c845ac6d3 100644 --- a/sources/pyside2/tests/QtCore/qproperty_decorator.py +++ b/sources/pyside2/tests/QtCore/qproperty_decorator.py @@ -47,7 +47,9 @@ class MyObject(QObject): return self._value @value.setter - def valueSet(self, value): + # Note: The name of property and setter must be the same, because the + # object changes its identity all the time. `valueSet` no longer works. + def value(self, value): self._value = value diff --git a/sources/pyside2/tests/pysidetest/CMakeLists.txt b/sources/pyside2/tests/pysidetest/CMakeLists.txt index bbdeb6a98..361f7d541 100644 --- a/sources/pyside2/tests/pysidetest/CMakeLists.txt +++ b/sources/pyside2/tests/pysidetest/CMakeLists.txt @@ -160,6 +160,7 @@ PYSIDE_TEST(modelview_test.py) PYSIDE_TEST(new_inherited_functions_test.py) PYSIDE_TEST(notify_id.py) PYSIDE_TEST(properties_test.py) +PYSIDE_TEST(property_python_test.py) PYSIDE_TEST(qapp_like_a_macro_test.py) PYSIDE_TEST(qvariant_test.py) PYSIDE_TEST(repr_test.py) diff --git a/sources/pyside2/tests/pysidetest/property_python_test.py b/sources/pyside2/tests/pysidetest/property_python_test.py new file mode 100644 index 000000000..7df104525 --- /dev/null +++ b/sources/pyside2/tests/pysidetest/property_python_test.py @@ -0,0 +1,232 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +Test for PySide's Property +========================== + +This test is copied from Python's `test_property.py` and adapted to +the PySide Property implementation. + +This test is to ensure maximum compatibility. +""" + +# Test case for property +# more tests are in test_descr + +import os +import sys +import unittest + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from init_paths import init_test_paths +init_test_paths(False) + +from PySide2.QtCore import Property, QObject +#from PyQt5.QtCore import pyqtProperty as Property, QObject + +# This are the original imports. +import sys +import unittest +has_test = False +try: + if sys.version_info[0] >= 3: # This test has no support in Python 2 + from test import support + has_test = True +except ImportError: + pass + +class PropertyBase(Exception): + pass + +class PropertyGet(PropertyBase): + pass + +class PropertySet(PropertyBase): + pass + +class PropertyDel(PropertyBase): + pass + +class BaseClass(QObject): + def __init__(self): + QObject.__init__(self) + + self._spam = 5 + + @Property(object) + def spam(self): + """BaseClass.getter""" + return self._spam + + @spam.setter + def spam(self, value): + self._spam = value + + @spam.deleter + def spam(self): + del self._spam + +class SubClass(BaseClass): + + @BaseClass.spam.getter + def spam(self): + """SubClass.getter""" + raise PropertyGet(self._spam) + + @spam.setter + def spam(self, value): + raise PropertySet(self._spam) + + @spam.deleter + def spam(self): + raise PropertyDel(self._spam) + +class PropertyDocBase(object): + _spam = 1 + def _get_spam(self): + return self._spam + spam = Property(object, _get_spam, doc="spam spam spam") + +class PropertyDocSub(PropertyDocBase): + @PropertyDocBase.spam.getter + def spam(self): + """The decorator does not use this doc string""" + return self._spam + +class PropertySubNewGetter(BaseClass): + @BaseClass.spam.getter + def spam(self): + """new docstring""" + return 5 + +class PropertyNewGetter(QObject): + def __init__(self): + QObject.__init__(self) + + @Property(object) + def spam(self): + """original docstring""" + return 1 + @spam.getter + def spam(self): + """new docstring""" + return 8 + +class PropertyTests(unittest.TestCase): + def test_property_decorator_baseclass(self): + # see #1620 + base = BaseClass() + self.assertEqual(base.spam, 5) + self.assertEqual(base._spam, 5) + base.spam = 10 + self.assertEqual(base.spam, 10) + self.assertEqual(base._spam, 10) + delattr(base, "spam") + self.assertTrue(not hasattr(base, "spam")) + self.assertTrue(not hasattr(base, "_spam")) + base.spam = 20 + self.assertEqual(base.spam, 20) + self.assertEqual(base._spam, 20) + + def test_property_decorator_subclass(self): + # see #1620 + sub = SubClass() + self.assertRaises(PropertyGet, getattr, sub, "spam") + self.assertRaises(PropertySet, setattr, sub, "spam", None) + self.assertRaises(PropertyDel, delattr, sub, "spam") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_decorator_subclass_doc(self): + sub = SubClass() + self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_decorator_baseclass_doc(self): + base = BaseClass() + self.assertEqual(base.__class__.spam.__doc__, "BaseClass.getter") + + def test_property_decorator_doc(self): + base = PropertyDocBase() + sub = PropertyDocSub() + self.assertEqual(base.__class__.spam.__doc__, "spam spam spam") + self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_getter_doc_override(self): + newgettersub = PropertySubNewGetter() + self.assertEqual(newgettersub.spam, 5) + self.assertEqual(newgettersub.__class__.spam.__doc__, "new docstring") + newgetter = PropertyNewGetter() + self.assertEqual(newgetter.spam, 8) + self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring") + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_builtin_doc_writable(self): + p = Property(object, doc='basic') + self.assertEqual(p.__doc__, 'basic') + p.__doc__ = 'extended' + self.assertEqual(p.__doc__, 'extended') + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_decorator_doc_writable(self): + class PropertyWritableDoc(object): + + @Property(object) + def spam(self): + """Eggs""" + return "eggs" + + sub = PropertyWritableDoc() + self.assertEqual(sub.__class__.spam.__doc__, 'Eggs') + sub.__class__.spam.__doc__ = 'Spam' + self.assertEqual(sub.__class__.spam.__doc__, 'Spam') + + if has_test: # This test has no support in Python 2 + @support.refcount_test + def test_refleaks_in___init__(self): + gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') + fake_prop = Property(object, 'fget', 'fset', "freset", 'fdel', 'doc') + refs_before = gettotalrefcount() + for i in range(100): + fake_prop.__init__(object, 'fget', 'fset', "freset", 'fdel', 'doc') + self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + + +# Note: We ignore the whole subclass tests concerning __doc__ strings. +# See the original Python test starting with: +# "Issue 5890: subclasses of property do not preserve method __doc__ strings" + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 70cccebcd..af10f1831 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1258,9 +1258,7 @@ void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaF if (!retType) return; - auto *metaType = new AbstractMetaType; - metaType->setTypeEntry(retType); - metaFunction->replaceType(metaType); + metaFunction->replaceType(new AbstractMetaType(retType)); } AbstractMetaFunctionList AbstractMetaBuilderPrivate::classFunctionList(const ScopeModelItem &scopeItem, @@ -1300,7 +1298,7 @@ void AbstractMetaBuilderPrivate::traverseFunctions(ScopeModelItem scopeItem, QPropertySpec *read = nullptr; if (!metaFunction->isSignal() && (read = metaClass->propertySpecForRead(metaFunction->name()))) { // Property reader must be in the form "<type> name()" - if (metaFunction->type() && (read->typeEntry() == metaFunction->type()->typeEntry()) + if (read->typeEntry() == metaFunction->type()->typeEntry() && metaFunction->arguments().isEmpty()) { *metaFunction += AbstractMetaAttributes::PropertyReader; metaFunction->setPropertySpec(read); @@ -1309,14 +1307,14 @@ void AbstractMetaBuilderPrivate::traverseFunctions(ScopeModelItem scopeItem, // 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->type()) && (metaFunction->arguments().size() == 1) + if (metaFunction->isVoid() && metaFunction->arguments().size() == 1 && (write->typeEntry() == metaFunction->arguments().at(0)->type()->typeEntry())) { *metaFunction += AbstractMetaAttributes::PropertyWriter; metaFunction->setPropertySpec(write); } } else if (QPropertySpec *reset = metaClass->propertySpecForReset(metaFunction->name())) { // Property resetter must be in the form "void name()" - if ((!metaFunction->type()) && metaFunction->arguments().isEmpty()) { + if (metaFunction->isVoid() && metaFunction->arguments().isEmpty()) { *metaFunction += AbstractMetaAttributes::PropertyResetter; metaFunction->setPropertySpec(reset); } @@ -1516,16 +1514,13 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu { QString errorMessage; - AbstractMetaType *returnType = nullptr; - if (addedFunc->returnType().name != QLatin1String("void")) { - returnType = translateType(addedFunc->returnType(), &errorMessage); - if (!returnType) { - qCWarning(lcShiboken, "%s", - qPrintable(msgAddedFunctionInvalidReturnType(addedFunc->name(), - addedFunc->returnType().name, - errorMessage))); - return nullptr; - } + AbstractMetaType *returnType = translateType(addedFunc->returnType(), &errorMessage); + if (!returnType) { + qCWarning(lcShiboken, "%s", + qPrintable(msgAddedFunctionInvalidReturnType(addedFunc->name(), + addedFunc->returnType().name, + errorMessage))); + return nullptr; } auto metaFunction = new AbstractMetaFunction(addedFunc); @@ -1806,10 +1801,12 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio QString errorMessage; switch (metaFunction->functionType()) { case AbstractMetaFunction::DestructorFunction: + metaFunction->setType(AbstractMetaType::createVoid()); break; case AbstractMetaFunction::ConstructorFunction: metaFunction->setExplicit(functionItem->isExplicit()); metaFunction->setName(currentClass->name()); + metaFunction->setType(AbstractMetaType::createVoid()); break; default: { TypeInfo returnType = functionItem->type(); @@ -1820,17 +1817,14 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio return nullptr; } - AbstractMetaType *type = nullptr; - if (!returnType.isVoid()) { - type = translateType(returnType, currentClass, {}, &errorMessage); - if (!type) { - const QString reason = msgUnmatchedReturnType(functionItem, errorMessage); - qCWarning(lcShiboken, "%s", - qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); - m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::UnmatchedReturnType); - delete metaFunction; - return nullptr; - } + AbstractMetaType *type = translateType(returnType, currentClass, {}, &errorMessage); + if (!type) { + const QString reason = msgUnmatchedReturnType(functionItem, errorMessage); + qCWarning(lcShiboken, "%s", + qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::UnmatchedReturnType); + delete metaFunction; + return nullptr; } metaFunction->setType(type); @@ -1979,7 +1973,7 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: QString typeName = typeInfo.name; if (typeName == QLatin1String("void")) - return nullptr; + return AbstractMetaType::createVoid(); type = typeDb->findType(typeName); if (!type) @@ -1989,13 +1983,21 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: bool isTemplate = false; QStringList templateArgs; if (!type && typeInfo.name.contains(QLatin1Char('<'))) { - const QStringList& parsedType = parseTemplateType(typeInfo.name); + QStringList parsedType = parseTemplateType(typeInfo.name); if (parsedType.isEmpty()) { *errorMessage = QStringLiteral("Template type parsing failed for '%1'").arg(typeInfo.name); return nullptr; } - templateArgs = parsedType.mid(1); - isTemplate = (type = typeDb->findContainerType(parsedType[0])); + const QString name = parsedType.takeFirst(); + templateArgs = parsedType; + type = typeDb->findContainerType(name); + if (!type) { // A template typedef? + if (auto candidate = typeDb->findType(name)) { + if (candidate->type() == TypeEntry::ObjectType || candidate->type() == TypeEntry::BasicValueType) + type = candidate; + } + } + isTemplate = type != nullptr; } if (!type) { @@ -2021,20 +2023,16 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: } // These are only implicit and should not appear in code... - auto *metaType = new AbstractMetaType; - metaType->setTypeEntry(type); + auto *metaType = new AbstractMetaType(type); metaType->setIndirections(typeInfo.indirections); if (typeInfo.isReference) metaType->setReferenceType(LValueReference); metaType->setConstant(typeInfo.isConstant); if (isTemplate) { for (const QString& templateArg : qAsConst(templateArgs)) { - AbstractMetaType *metaArgType = nullptr; - if (templateArg != QLatin1String("void")) { - metaArgType = translateType(AddedFunction::TypeInfo::fromSignature(templateArg), errorMessage); - if (!metaArgType) - return nullptr; - } + AbstractMetaType *metaArgType = translateType(AddedFunction::TypeInfo::fromSignature(templateArg), errorMessage); + if (!metaArgType) + return nullptr; metaType->addInstantiation(metaArgType); } metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); @@ -2120,6 +2118,9 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo 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); @@ -2622,7 +2623,7 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, const TypeInfo &info) { QVector<TypeInfo> targs = info.instantiations(); - QVector<AbstractMetaType *> templateTypes; + AbstractMetaTypeList templateTypes; QString errorMessage; if (subclass->isTypeDef()) { @@ -2665,8 +2666,7 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, } if (t) { - auto *temporaryType = new AbstractMetaType; - temporaryType->setTypeEntry(t); + auto *temporaryType = new AbstractMetaType(t); temporaryType->setConstant(i.isConstant()); temporaryType->setReferenceType(i.referenceType()); temporaryType->setIndirectionsV(i.indirectionsV()); @@ -2692,7 +2692,7 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, QScopedPointer<AbstractMetaFunction> f(function->copy()); f->setArguments(AbstractMetaArgumentList()); - if (function->type()) { // Non-void + if (!function->isVoid()) { AbstractMetaType *returnType = inheritTemplateType(templateTypes, function->type()); if (!returnType) continue; diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index f705994f2..2f68dd743 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -186,7 +186,8 @@ void AbstractMetaAttributes::assignMetaAttributes(const AbstractMetaAttributes & * AbstractMetaType */ -AbstractMetaType::AbstractMetaType() : +AbstractMetaType::AbstractMetaType(const TypeEntry *t) : + m_typeEntry(t), m_constant(false), m_volatile(false), m_cppInstantiation(true), @@ -220,7 +221,7 @@ QString AbstractMetaType::fullName() const AbstractMetaType *AbstractMetaType::copy() const { - auto *cpy = new AbstractMetaType; + auto *cpy = new AbstractMetaType(typeEntry()); cpy->setTypeUsagePattern(typeUsagePattern()); cpy->setConstant(isConstant()); @@ -234,8 +235,6 @@ AbstractMetaType *AbstractMetaType::copy() const cpy->setArrayElementType(arrayElementType() ? arrayElementType()->copy() : nullptr); - cpy->setTypeEntry(typeEntry()); - return cpy; } @@ -322,8 +321,11 @@ AbstractMetaType::TypeUsagePattern AbstractMetaType::determineUsagePattern() con if (m_typeEntry->isPrimitive() && (actualIndirections() == 0 || passByConstRef())) return PrimitivePattern; - if (m_typeEntry->isVoid()) - return NativePointerPattern; + if (m_typeEntry->isVoid()) { + return m_arrayElementCount < 0 && m_referenceType == NoReference + && m_indirections.isEmpty() && m_constant == 0 && m_volatile == 0 + ? VoidPattern : NativePointerPattern; + } if (m_typeEntry->isVarargs()) return VarargsPattern; @@ -413,6 +415,15 @@ bool AbstractMetaType::compare(const AbstractMetaType &rhs, ComparisonFlags flag return true; } +AbstractMetaType *AbstractMetaType::createVoid() +{ + static const TypeEntry *voidTypeEntry = TypeDatabase::instance()->findType(QLatin1String("void")); + Q_ASSERT(voidTypeEntry); + auto *metaType = new AbstractMetaType(voidTypeEntry); + metaType->decideUsagePattern(); + return metaType; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaType *at) { @@ -647,8 +658,7 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const cpy->setImplementingClass(implementingClass()); cpy->setFunctionType(functionType()); cpy->setDeclaringClass(declaringClass()); - if (type()) - cpy->setType(type()->copy()); + cpy->setType(type()->copy()); cpy->setConstant(isConstant()); cpy->setExceptionSpecification(m_exceptionSpecification); cpy->setAllowThreadModification(m_allowThreadModification); @@ -658,8 +668,7 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const for (AbstractMetaArgument *arg : m_arguments) cpy->addArgument(arg->copy()); - Q_ASSERT((!type() && !cpy->type()) - || (type()->instantiations() == cpy->type()->instantiations())); + Q_ASSERT(type()->instantiations() == cpy->type()->instantiations()); return cpy; } @@ -668,7 +677,7 @@ bool AbstractMetaFunction::usesRValueReferences() const { if (m_functionType == MoveConstructorFunction || m_functionType == MoveAssignmentOperatorFunction) return true; - if (m_type && m_type->referenceType() == RValueReference) + if (m_type->referenceType() == RValueReference) return true; for (const AbstractMetaArgument *a : m_arguments) { if (a->type()->referenceType() == RValueReference) @@ -833,8 +842,7 @@ bool AbstractMetaFunction::isDeprecated() const bool AbstractMetaFunction::autoDetectAllowThread() const { // Disallow for simple getter functions. - const bool maybeGetter = m_constant != 0 && m_type != nullptr - && m_arguments.isEmpty(); + const bool maybeGetter = m_constant != 0 && !isVoid() && m_arguments.isEmpty(); return !maybeGetter; } @@ -1409,8 +1417,7 @@ AbstractMetaClass::~AbstractMetaClass() qDeleteAll(m_fields); qDeleteAll(m_enums); qDeleteAll(m_propertySpecs); - if (hasTemplateBaseClassInstantiations()) - qDeleteAll(templateBaseClassInstantiations()); + qDeleteAll(m_baseTemplateInstantiations); } /******************************************************************************* @@ -1736,28 +1743,20 @@ QPropertySpec *AbstractMetaClass::propertySpecForReset(const QString &name) cons return nullptr; } -using AbstractMetaClassBaseTemplateInstantiationsMap = QHash<const AbstractMetaClass *, AbstractMetaTypeList>; -Q_GLOBAL_STATIC(AbstractMetaClassBaseTemplateInstantiationsMap, metaClassBaseTemplateInstantiations); - bool AbstractMetaClass::hasTemplateBaseClassInstantiations() const { - if (!templateBaseClass()) - return false; - return metaClassBaseTemplateInstantiations()->contains(this); + return m_templateBaseClass != nullptr && !m_baseTemplateInstantiations.isEmpty(); } -AbstractMetaTypeList AbstractMetaClass::templateBaseClassInstantiations() const +const AbstractMetaTypeList &AbstractMetaClass::templateBaseClassInstantiations() const { - if (!templateBaseClass()) - return AbstractMetaTypeList(); - return metaClassBaseTemplateInstantiations()->value(this); + return m_baseTemplateInstantiations; } -void AbstractMetaClass::setTemplateBaseClassInstantiations(AbstractMetaTypeList &instantiations) +void AbstractMetaClass::setTemplateBaseClassInstantiations(const AbstractMetaTypeList &instantiations) { - if (!templateBaseClass()) - return; - metaClassBaseTemplateInstantiations()->insert(this, instantiations); + Q_ASSERT(m_templateBaseClass != nullptr); + m_baseTemplateInstantiations = instantiations; } // Does any of the base classes require deletion in the main thread? @@ -1930,6 +1929,7 @@ bool AbstractMetaClass::hasPrivateCopyConstructor() const void AbstractMetaClass::addDefaultConstructor() { auto *f = new AbstractMetaFunction; + f->setType(AbstractMetaType::createVoid()); f->setOriginalName(name()); f->setName(name()); f->setOwnerClass(this); @@ -1948,14 +1948,14 @@ void AbstractMetaClass::addDefaultConstructor() void AbstractMetaClass::addDefaultCopyConstructor(bool isPrivate) { auto f = new AbstractMetaFunction; + f->setType(AbstractMetaType::createVoid()); f->setOriginalName(name()); f->setName(name()); f->setOwnerClass(this); f->setFunctionType(AbstractMetaFunction::CopyConstructorFunction); f->setDeclaringClass(this); - auto argType = new AbstractMetaType; - argType->setTypeEntry(typeEntry()); + auto argType = new AbstractMetaType(typeEntry()); argType->setReferenceType(LValueReference); argType->setConstant(true); argType->setTypeUsagePattern(AbstractMetaType::ValuePattern); @@ -2184,8 +2184,7 @@ static void addExtraIncludeForType(AbstractMetaClass *metaClass, const AbstractM } if (type->hasInstantiations()) { - const AbstractMetaTypeList &instantiations = type->instantiations(); - for (const AbstractMetaType *instantiation : instantiations) + for (const AbstractMetaType *instantiation : type->instantiations()) addExtraIncludeForType(metaClass, instantiation); } } @@ -2630,7 +2629,7 @@ void AbstractMetaClass::format(QDebug &d) const d << " \"" << b->name() << '"'; } if (auto templateBase = templateBaseClass()) { - const auto instantiatedTypes = templateBaseClassInstantiations(); + const auto &instantiatedTypes = templateBaseClassInstantiations(); d << ", instantiates \"" << templateBase->name(); for (int i = 0, count = instantiatedTypes.size(); i < count; ++i) d << (i ? ',' : '<') << instantiatedTypes.at(i)->name(); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index df9790a3d..154314d69 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -293,7 +293,8 @@ public: ContainerPattern, SmartPointerPattern, VarargsPattern, - ArrayPattern + ArrayPattern, + VoidPattern // Plain "void", no "void *" or similar. }; Q_ENUM(TypeUsagePattern) @@ -302,7 +303,7 @@ public: }; Q_DECLARE_FLAGS(ComparisonFlags, ComparisonFlag); - AbstractMetaType(); + explicit AbstractMetaType(const TypeEntry *t = nullptr); AbstractMetaType(const AbstractMetaType &); ~AbstractMetaType(); @@ -341,7 +342,7 @@ public: } } - AbstractMetaTypeList instantiations() const + const AbstractMetaTypeList &instantiations() const { return m_instantiations; } @@ -417,6 +418,8 @@ public: return m_pattern == FlagsPattern; } + bool isVoid() const { return m_pattern == VoidPattern; } + bool isConstant() const { return m_constant; @@ -510,7 +513,7 @@ public: AbstractMetaType *getSmartPointerInnerType() const { Q_ASSERT(isSmartPointer()); - AbstractMetaTypeList instantiations = this->instantiations(); + const AbstractMetaTypeList &instantiations = this->instantiations(); Q_ASSERT(!instantiations.isEmpty()); AbstractMetaType *innerType = instantiations.at(0); return innerType; @@ -537,12 +540,14 @@ public: const AbstractMetaType *viewOn() const { return m_viewOn; } void setViewOn(const AbstractMetaType *v) { m_viewOn = v; } + static AbstractMetaType *createVoid(); + private: TypeUsagePattern determineUsagePattern() const; QString formatSignature(bool minimal) const; QString formatPythonSignature() const; - const TypeEntry *m_typeEntry = nullptr; + const TypeEntry *m_typeEntry; AbstractMetaTypeList m_instantiations; QString m_package; mutable QString m_cachedCppSignature; @@ -887,6 +892,7 @@ public: bool isModifiedRemoved(int types = TypeSystem::All) const; + bool isVoid() const { return m_type->isVoid(); } AbstractMetaType *type() const { return m_type; @@ -1640,8 +1646,8 @@ public: } bool hasTemplateBaseClassInstantiations() const; - AbstractMetaTypeList templateBaseClassInstantiations() const; - void setTemplateBaseClassInstantiations(AbstractMetaTypeList& instantiations); + const AbstractMetaTypeList &templateBaseClassInstantiations() const; + void setTemplateBaseClassInstantiations(const AbstractMetaTypeList& instantiations); void setTypeDef(bool typeDef) { m_isTypeDef = typeDef; } bool isTypeDef() const { return m_isTypeDef; } @@ -1712,6 +1718,7 @@ private: const AbstractMetaClass *m_enclosingClass = nullptr; AbstractMetaClassList m_baseClasses; // Real base classes after setting up inheritance + AbstractMetaTypeList m_baseTemplateInstantiations; AbstractMetaClass *m_extendedNamespace = nullptr; const AbstractMetaClass *m_templateBaseClass = nullptr; diff --git a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp index ca4af9a10..a131e7fe2 100644 --- a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp @@ -145,7 +145,7 @@ void TestAddFunction::testAddFunctionConstructor() QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::ConstructorFunction); QCOMPARE(addedFunc->arguments().size(), 1); QVERIFY(addedFunc->isUserAdded()); - QVERIFY(!addedFunc->type()); + QVERIFY(addedFunc->isVoid()); } void TestAddFunction::testAddFunctionTagDefaultValues() @@ -167,7 +167,7 @@ void TestAddFunction::testAddFunctionTagDefaultValues() QCOMPARE(addedFunc->visibility(), AbstractMetaFunction::Public); QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction); QVERIFY(addedFunc->isUserAdded()); - QVERIFY(!addedFunc->type()); + QVERIFY(addedFunc->isVoid()); } void TestAddFunction::testAddFunctionCodeSnippets() diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 0d5a8cba7..6b56d362e 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -887,9 +887,8 @@ void TypeEntry::formatDebug(QDebug &d) const d << '"' << m_name << '"'; if (m_name != cppName) d << "\", cppName=\"" << cppName << '"'; - d << ", type=" << m_type << ", codeGeneration=0x" - << Qt::hex << m_codeGeneration << Qt::dec - << ", target=\"" << targetLangName() << '"'; + d << ", type=" << m_type << ", codeGeneration=" + << m_codeGeneration << ", target=\"" << targetLangName() << '"'; FORMAT_NONEMPTY_STRING("package", m_targetLangPackage) FORMAT_BOOL("stream", m_stream) FORMAT_LIST_SIZE("codeSnips", m_codeSnips) @@ -1002,6 +1001,16 @@ void TypeDatabase::formatDebug(QDebug &d) const << "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(); diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index 445743a74..2418f51fe 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -229,7 +229,7 @@ QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType *type) // Strip a "const QSharedPtr<const Foo> &" or similar to "QSharedPtr<Foo>" (PYSIDE-1016/454) const AbstractMetaType *canonicalSmartPtrInstantiation(const AbstractMetaType *type) { - AbstractMetaTypeList instantiations = type->instantiations(); + const AbstractMetaTypeList &instantiations = type->instantiations(); Q_ASSERT(instantiations.size() == 1); const bool needsFix = type->isConstant() || type->referenceType() != NoReference; const bool pointeeNeedsFix = instantiations.constFirst()->isConstant(); @@ -256,8 +256,7 @@ void Generator::addInstantiatedContainersAndSmartPointers(const AbstractMetaType { if (!type) return; - const AbstractMetaTypeList &instantiations = type->instantiations(); - for (const AbstractMetaType *t : instantiations) + for (const auto *t : type->instantiations()) addInstantiatedContainersAndSmartPointers(t, context); const auto typeEntry = type->typeEntry(); const bool isContainer = typeEntry->isContainer(); diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index 282da6eca..81b404239 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -2081,7 +2081,7 @@ void QtDocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractM writeParameterType(s, cppClass, arg); } - if (!func->isConstructor() && func->type()) { + if (!func->isConstructor() && !func->isVoid()) { QString retType; // check if the return type was modified diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index a6e9a00b6..434b03dc7 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -85,10 +85,9 @@ static const char *typeNameOf(const T &t) inline AbstractMetaType *getTypeWithoutContainer(AbstractMetaType *arg) { if (arg && arg->typeEntry()->isContainer()) { - AbstractMetaTypeList lst = arg->instantiations(); // only support containers with 1 type - if (lst.size() == 1) - return lst[0]; + if (arg->instantiations().size() == 1) + return arg->instantiations().constFirst(); } return arg; } @@ -247,7 +246,7 @@ const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass *meta return nullptr; // TODO: This could be configurable someday const AbstractMetaFunction *func = metaClass->findFunction(QLatin1String("isNull")); - if (!func || !func->type() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) + if (!func || func->isVoid() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) return nullptr; auto pte = static_cast<const PrimitiveTypeEntry *>(func->type()->typeEntry()); while (pte->referencedTypeEntry()) @@ -892,9 +891,9 @@ QString CppGenerator::virtualMethodReturn(QTextStream &s, const AbstractMetaFunction *func, const FunctionModificationList &functionModifications) { - const AbstractMetaType *returnType = func->type(); - if (!returnType) + if (func->isVoid()) return QLatin1String("return;"); + const AbstractMetaType *returnType = func->type(); for (const FunctionModification &mod : functionModifications) { for (const ArgumentModification &argMod : mod.argument_mods) { if (argMod.index == 0 && !argMod.replacedDefaultExpression.isEmpty()) { @@ -947,7 +946,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, ((func->name() == QLatin1String("metaObject")) || (func->name() == QLatin1String("qt_metacall")))) return; - const TypeEntry *retType = func->type() ? func->type()->typeEntry() : nullptr; + const TypeEntry *retType = func->type()->typeEntry(); const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->name(); QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::"); @@ -990,7 +989,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, << R"(] << '\n';)" << '\n'; } // PYSIDE-803: Build a boolean cache for unused overrides. - const bool multi_line = retType == nullptr || !snips.isEmpty() || func->isAbstract(); + const bool multi_line = func->isVoid() || !snips.isEmpty() || func->isAbstract(); s << INDENT << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n"); { Indentation indentation(INDENT); @@ -1122,7 +1121,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, } s << INDENT << "}\n"; - if (retType) { + if (!func->isVoid()) { if (invalidateReturn) s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;\n"; @@ -1207,7 +1206,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode, func, lastArg); } - if (retType) { + if (!func->isVoid()) { s << INDENT << "return "; if (avoidProtectedHack() && retType->isEnum()) { const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(retType); @@ -2325,6 +2324,7 @@ void CppGenerator::writeTypeCheck(QTextStream &s, const AbstractMetaType *argTyp static void checkTypeViability(const AbstractMetaFunction *func, const AbstractMetaType *type, int argIdx) { if (!type + || type->isVoid() || !type->typeEntry()->isPrimitive() || type->indirections() == 0 || (type->indirections() == 1 && type->typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) @@ -2597,7 +2597,7 @@ void CppGenerator::writeConversionRule(QTextStream &s, const AbstractMetaFunctio void CppGenerator::writeNoneReturn(QTextStream &s, const AbstractMetaFunction *func, bool thereIsReturnValue) { - if (thereIsReturnValue && (!func->type() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { + if (thereIsReturnValue && (func->isVoid() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { s << INDENT << PYTHON_RETURN_VAR << " = Py_None;\n"; s << INDENT << "Py_INCREF(Py_None);\n"; } @@ -3238,7 +3238,7 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction *func, in } else if (argIndex == 0) { AbstractMetaType *funcType = func->type(); AbstractMetaType *returnType = getTypeWithoutContainer(funcType); - if (returnType) { + if (!returnType->isVoid()) { pyArgName = QLatin1String(PYTHON_RETURN_VAR); *wrappedClass = AbstractMetaClass::findClass(classes(), returnType->typeEntry()); } else { @@ -3561,7 +3561,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f if (isCtor) { s << (useVAddr.isEmpty() ? QString::fromLatin1("cptr = %1;").arg(methodCall) : useVAddr) << Qt::endl; - } else if (func->type() && !func->isInplaceOperator()) { + } else if (!func->isVoid() && !func->isInplaceOperator()) { bool writeReturnType = true; if (avoidProtectedHack()) { const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(func->type()); @@ -3599,7 +3599,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f // Convert result if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); - } else if (!isCtor && !func->isInplaceOperator() && func->type() + } else if (!isCtor && !func->isInplaceOperator() && !func->isVoid() && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) { s << INDENT << PYTHON_RETURN_VAR << " = "; if (isObjectTypeUsedAsValueType(func->type())) { @@ -4684,7 +4684,7 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, const GeneratorConte } if (generateOperatorCode) { s << INDENT; - if (func->type()) + if (!func->isVoid()) s << func->type()->cppSignature() << " " << CPP_RETURN_VAR << " = "; // expression if (func->isPointerOperator()) @@ -4694,7 +4694,7 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, const GeneratorConte s << '*'; s << CPP_ARG0 << ");\n"; s << INDENT << PYTHON_RETURN_VAR << " = "; - if (func->type()) + if (!func->isVoid()) writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); else s << "Py_None;\n" << INDENT << "Py_INCREF(Py_None)"; @@ -4821,7 +4821,7 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction if (multiple) s << idx-- << ':'; s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; - if (f->type()) + if (!f->isVoid()) s << "->" << f->type()->pythonSignature(); s << Qt::endl; } @@ -6182,7 +6182,7 @@ void CppGenerator::writeReturnValueHeuristics(QTextStream &s, const AbstractMeta AbstractMetaType *type = func->type(); if (!useReturnValueHeuristic() || !func->ownerClass() - || !type + || type->isVoid() || func->isStatic() || func->isConstructor() || !func->typeReplaced(0).isEmpty()) { @@ -6232,7 +6232,7 @@ void CppGenerator::writeDefaultSequenceMethods(QTextStream &s, const GeneratorCo << CPP_SELF_VAR << "->begin();\n" << INDENT << "std::advance(_item, _i);\n"; - const AbstractMetaTypeList instantiations = metaClass->templateBaseClassInstantiations(); + const AbstractMetaTypeList &instantiations = metaClass->templateBaseClassInstantiations(); if (instantiations.isEmpty()) { qFatal("shiboken: %s: Internal error, no instantiations of \"%s\" were found.", __FUNCTION__, qPrintable(metaClass->qualifiedCppName())); diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index 1ba846d87..c780df01a 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -239,7 +239,8 @@ void HeaderGenerator::writeFunction(QTextStream &s, const AbstractMetaFunction * s << INDENT << "inline " << (func->isStatic() ? "static " : ""); s << functionSignature(func, QString(), QLatin1String("_protected"), Generator::EnumAsInts|Generator::OriginalTypeDescription) << " { "; - s << (func->type() ? "return " : ""); + if (!func->isVoid()) + s << "return "; if (!func->isAbstract()) s << func->ownerClass()->qualifiedCppName() << "::"; s << func->originalName() << '('; @@ -637,8 +638,10 @@ void HeaderGenerator::writeInheritedOverloads(QTextStream &s) { for (const AbstractMetaFunction *func : qAsConst(m_inheritedOverloads)) { s << INDENT << "inline "; - s << functionSignature(func, QString(), QString(), Generator::EnumAsInts|Generator::OriginalTypeDescription) << " { "; - s << (func->type() ? "return " : ""); + s << functionSignature(func, QString(), QString(), Generator::EnumAsInts|Generator::OriginalTypeDescription) + << " { "; + if (!func->isVoid()) + s << "return "; s << func->ownerClass()->qualifiedCppName() << "::" << func->originalName() << '('; QStringList args; const AbstractMetaArgumentList &arguments = func->arguments(); diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.cpp b/sources/shiboken2/generator/shiboken2/overloaddata.cpp index 36725d3fc..193384853 100644 --- a/sources/shiboken2/generator/shiboken2/overloaddata.cpp +++ b/sources/shiboken2/generator/shiboken2/overloaddata.cpp @@ -57,8 +57,7 @@ static QString getTypeName(const AbstractMetaType *type) QString typeName = typeEntry->name(); if (typeEntry->isContainer()) { QStringList types; - const AbstractMetaTypeList &instantiations = type->instantiations(); - for (const AbstractMetaType *cType : instantiations) { + for (const auto *cType : type->instantiations()) { const TypeEntry *typeEntry = getReferencedTypeEntry(cType->typeEntry()); types << typeEntry->name(); } @@ -147,8 +146,7 @@ static QString getImplicitConversionTypeName(const AbstractMetaType *containerTy impConv = getTypeName(function->arguments().constFirst()->type()); QStringList types; - const AbstractMetaTypeList &instantiations = containerType->instantiations(); - for (const AbstractMetaType *otherType : instantiations) + for (const auto *otherType : containerType->instantiations()) types << (otherType == instantiation ? impConv : getTypeName(otherType)); return containerType->typeEntry()->qualifiedCppName() + QLatin1Char('<') @@ -258,8 +256,7 @@ void OverloadData::sortNextOverloads() qstringIndex = sortData.lastProcessedItemId(); } - const AbstractMetaTypeList &instantiations = ov->argType()->instantiations(); - for (const AbstractMetaType *instantiation : instantiations) { + for (const auto *instantiation : ov->argType()->instantiations()) { // Add dependencies for type instantiation of container. QString typeName = getTypeName(instantiation); sortData.mapType(typeName); @@ -346,8 +343,7 @@ void OverloadData::sortNextOverloads() } // Process template instantiations - const AbstractMetaTypeList &instantiations = targetType->instantiations(); - for (const AbstractMetaType *instantiation : instantiations) { + for (const auto *instantiation : targetType->instantiations()) { if (sortData.map.contains(getTypeName(instantiation))) { int convertible = sortData.map[getTypeName(instantiation)]; @@ -579,10 +575,8 @@ QStringList OverloadData::returnTypes() const for (const AbstractMetaFunction *func : m_overloads) { if (!func->typeReplaced(0).isEmpty()) retTypes << func->typeReplaced(0); - else if (func->type() && !func->argumentRemoved(0)) + else if (!func->argumentRemoved(0)) retTypes << func->type()->cppSignature(); - else - retTypes << QLatin1String("void"); } return retTypes.values(); } @@ -878,12 +872,9 @@ QString OverloadData::dumpGraph() const // Shows all function signatures s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; for (const AbstractMetaFunction *func : m_overloads) { - s << "f" << functionNumber(func) << " : "; - if (func->type()) - s << toHtml(func->type()->cppSignature()); - else - s << "void"; - s << ' ' << toHtml(func->minimalSignature()) << "\\l"; + s << "f" << functionNumber(func) << " : " + << toHtml(func->type()->cppSignature()) + << ' ' << toHtml(func->minimalSignature()) << "\\l"; } s << "\"];\n"; @@ -903,12 +894,9 @@ QString OverloadData::dumpGraph() const s << "</td></tr>"; // Function return type - s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">"; - if (rfunc->type()) - s << toHtml(rfunc->type()->cppSignature()); - else - s << "void"; - s << "</td></tr>"; + s << "<tr><td bgcolor=\"gray\" align=\"right\">original type</td><td bgcolor=\"gray\" align=\"left\">" + << toHtml(rfunc->type()->cppSignature()) + << "</td></tr>"; // Shows type changes for all function signatures for (const AbstractMetaFunction *func : m_overloads) { diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 7b8f2c7e4..97aecf529 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -1862,7 +1862,7 @@ void ShibokenGenerator::writeCodeSnips(QTextStream &s, if (func->isConstructor()) { code.replace(QLatin1String("%0."), QLatin1String("cptr->")); code.replace(QLatin1String("%0"), QLatin1String("cptr")); - } else if (func->type()) { + } else if (!func->isVoid()) { QString returnValueOp = isPointerToWrapperType(func->type()) ? QLatin1String("%1->") : QLatin1String("%1."); if (ShibokenGenerator::isWrapperType(func->type())) @@ -2406,8 +2406,7 @@ AbstractMetaType *ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const Ty typeName.remove(0, 2); if (m_metaTypeFromStringCache.contains(typeName)) return m_metaTypeFromStringCache.value(typeName); - auto *metaType = new AbstractMetaType; - metaType->setTypeEntry(typeEntry); + auto *metaType = new AbstractMetaType(typeEntry); metaType->clearIndirections(); metaType->setReferenceType(NoReference); metaType->setConstant(false); @@ -2737,8 +2736,7 @@ QString ShibokenGenerator::convertersVariableName(const QString &moduleName) con static QString processInstantiationsVariableName(const AbstractMetaType *type) { QString res = QLatin1Char('_') + _fixedCppTypeName(type->typeEntry()->qualifiedCppName()).toUpper(); - const AbstractMetaTypeList &instantiations = type->instantiations(); - for (const AbstractMetaType *instantiation : instantiations) { + for (const auto *instantiation : type->instantiations()) { res += instantiation->isContainer() ? processInstantiationsVariableName(instantiation) : QLatin1Char('_') + _fixedCppTypeName(instantiation->cppSignature()).toUpper(); @@ -2762,8 +2760,7 @@ QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass *met return QString(); QString result = QLatin1String("SBK_") + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); - const AbstractMetaTypeList &templateBaseClassInstantiations = metaClass->templateBaseClassInstantiations(); - for (const AbstractMetaType *instantiation : templateBaseClassInstantiations) + for (const auto *instantiation : metaClass->templateBaseClassInstantiations()) result += processInstantiationsVariableName(instantiation); appendIndexSuffix(&result); return result; |