From e0e44f0fd5b05ee299bd4e377b0d4a302c442aae Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 13 Jul 2018 15:05:27 +0200 Subject: shiboken: Refactor attribute parsing in typesystem parser Split up the 1400 lines Handler::startElement() function into smaller helper functions. Previously, the function populated a hash with the default values of all attributes. The values were then set by fetchAttributes() from the XML attributes and applied later on. In this setup, it is not possible to add deprecation warnings since it not possible to tell which attributes were actually present in the file. Change this to operate on the QXmlStreamAttributes list from which the consumed options are removed. Add a warning about unused attributes. It is now possible to add deprecation warnings and the default values are now more obvious. Task-number: PYSIDE-743 Change-Id: I1ee04e9490b3664bba4c976fe654183819610b58 Reviewed-by: Qt CI Bot Reviewed-by: Christian Tismer --- sources/shiboken2/ApiExtractor/typesystem.cpp | 2916 ++++++++++++++----------- 1 file changed, 1672 insertions(+), 1244 deletions(-) (limited to 'sources/shiboken2/ApiExtractor/typesystem.cpp') diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 0b695c0c2..d486dc41b 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -48,7 +48,9 @@ static QString strings_char = QLatin1String("char"); static QString strings_jchar = QLatin1String("jchar"); static QString strings_jobject = QLatin1String("jobject"); +static inline QString allowThreadAttribute() { return QStringLiteral("allow-thread"); } static inline QString colonColon() { return QStringLiteral("::"); } +static inline QString copyableAttribute() { return QStringLiteral("copyable"); } static inline QString accessAttribute() { return QStringLiteral("access"); } static inline QString actionAttribute() { return QStringLiteral("action"); } static inline QString quoteAfterLineAttribute() { return QStringLiteral("quote-after-line"); } @@ -56,20 +58,39 @@ static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote- static inline QString textAttribute() { return QStringLiteral("text"); } static inline QString nameAttribute() { return QStringLiteral("name"); } static inline QString sinceAttribute() { return QStringLiteral("since"); } +static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); } +static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); } +static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); } +static inline QString extensibleAttribute() { return QStringLiteral("extensible"); } static inline QString flagsAttribute() { return QStringLiteral("flags"); } +static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); } +static inline QString forceIntegerAttribute() { return QStringLiteral("force-integer"); } static inline QString formatAttribute() { return QStringLiteral("format"); } static inline QString classAttribute() { return QStringLiteral("class"); } -static inline QString functionNameAttribute() { return QStringLiteral("function-name"); } -static inline QString fieldNameAttribute() { return QStringLiteral("field-name"); } +static inline QString generateAttribute() { return QStringLiteral("generate"); } +static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); } static inline QString indexAttribute() { return QStringLiteral("index"); } -static inline QString enumNameAttribute() { return QStringLiteral("enum-name"); } -static inline QString argumentTypeAttribute() { return QStringLiteral("argument-type"); } +static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); } static inline QString locationAttribute() { return QStringLiteral("location"); } +static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); } static inline QString modifierAttribute() { return QStringLiteral("modifier"); } static inline QString ownershipAttribute() { return QStringLiteral("owner"); } +static inline QString packageAttribute() { return QStringLiteral("package"); } static inline QString positionAttribute() { return QStringLiteral("position"); } -static inline QString returnTypeAttribute() { return QStringLiteral("return-type"); } +static inline QString preferredConversionAttribute() { return QStringLiteral("preferred-conversion"); } +static inline QString preferredTargetLangTypeAttribute() { return QStringLiteral("preferred-target-lang-type"); } +static inline QString removeAttribute() { return QStringLiteral("remove"); } +static inline QString renameAttribute() { return QStringLiteral("rename"); } +static inline QString readAttribute() { return QStringLiteral("read"); } +static inline QString writeAttribute() { return QStringLiteral("write"); } +static inline QString replaceAttribute() { return QStringLiteral("replace"); } +static inline QString toAttribute() { return QStringLiteral("to"); } +static inline QString signatureAttribute() { return QStringLiteral("signature"); } +static inline QString staticAttribute() { return QStringLiteral("static"); } +static inline QString threadAttribute() { return QStringLiteral("thread"); } +static inline QString streamAttribute() { return QStringLiteral("stream"); } static inline QString xPathAttribute() { return QStringLiteral("xpath"); } +static inline QString virtualSlotAttribute() { return QStringLiteral("virtual-slot"); } static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); } static inline QString noAttributeValue() { return QStringLiteral("no"); } @@ -102,49 +123,6 @@ static bool setRejectionRegularExpression(const QString &patternIn, return true; } -static bool addRejection(TypeDatabase *database, const QHash &attributes, - QString *errorMessage) -{ - typedef QPair AttributeMatchTypePair; - - TypeRejection rejection; - - const QString className = attributes.value(classAttribute()); - if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) - return false; - - static const AttributeMatchTypePair attributeMatchTypeMapping[] = - {{functionNameAttribute(), TypeRejection::Function}, - {fieldNameAttribute(), TypeRejection::Field}, - {enumNameAttribute(), TypeRejection::Enum}, - {argumentTypeAttribute(), TypeRejection::ArgumentType}, - {returnTypeAttribute(), TypeRejection::ReturnType} - }; - - // Search for non-empty attribute (function, field, enum) - const auto aend = attributes.cend(); - for (const AttributeMatchTypePair &mapping : attributeMatchTypeMapping) { - const auto it = attributes.constFind(mapping.first); - if (it != aend && !it.value().isEmpty()) { - if (!setRejectionRegularExpression(it.value(), &rejection.pattern, errorMessage)) - return false; - rejection.matchType = mapping.second; - database->addRejection(rejection); - return true; - } - } - - // Special case: When all fields except class are empty, completely exclude class - if (className == QLatin1String("*")) { - *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" - " nor 'field' specified"); - return false; - } - rejection.matchType = TypeRejection::ExcludeClass; - database->addRejection(rejection); - return true; -} - template struct EnumLookup { @@ -305,6 +283,18 @@ ENUM_LOOKUP_BEGIN(ContainerTypeEntry::Type, Qt::CaseSensitive, }; ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, + typeRejectionFromAttribute, TypeRejection::Invalid) + { + {QStringViewLiteral("class"), TypeRejection::ExcludeClass}, + {QStringViewLiteral("function-name"), TypeRejection::Function}, + {QStringViewLiteral("field-name"), TypeRejection::Field}, + {QStringViewLiteral("enum-name"), TypeRejection::Enum }, + {QStringViewLiteral("argument-type"), TypeRejection::ArgumentType}, + {QStringViewLiteral("return-type"), TypeRejection::ReturnType} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, elementFromTag, StackElement::None) { @@ -357,6 +347,35 @@ ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, }; ENUM_LOOKUP_BINARY_SEARCH() +static int indexOfAttribute(const QXmlStreamAttributes &atts, + QStringView name) +{ + for (int 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 QLatin1String("Required attribute '") + a + + QLatin1String("' missing."); +} + +static QString msgUnusedAttributes(const QStringRef &tag, const QXmlStreamAttributes &attributes) +{ + QString result; + QTextStream str(&result); + str << attributes.size() << " attributes(s) unused on <" << tag << ">: "; + for (int i = 0, size = attributes.size(); i < size; ++i) { + if (i) + str << ", "; + str << attributes.at(i).qualifiedName() << "=\"" << attributes.at(i).value() << '"'; + } + return result; +} + Handler::Handler(TypeDatabase* database, bool generate) : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) { @@ -373,11 +392,13 @@ static QString readerFileName(const QXmlStreamReader &reader) return file != nullptr ? file->fileName() : QString(); } -static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) +static QString msgReaderMessage(const QXmlStreamReader &reader, + const char *type, + const QString &what) { QString message; QTextStream str(&message); - str << "Error: "; + str << type << ": "; const QString fileName = readerFileName(reader); if (!fileName.isEmpty()) str << "file=" << QDir::toNativeSeparators(fileName) << ", "; @@ -386,6 +407,16 @@ static QString msgReaderError(const QXmlStreamReader &reader, const QString &wha return message; } +static QString msgReaderWarning(const QXmlStreamReader &reader, const QString &what) +{ + return msgReaderMessage(reader, "Warning", what); +} + +static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) +{ + return msgReaderMessage(reader, "Error", what); +} + static QString msgInvalidVersion(const QStringRef &version, const QString &package = QString()) { QString result; @@ -397,6 +428,53 @@ static QString msgInvalidVersion(const QStringRef &version, const QString &packa return result; } +static bool addRejection(TypeDatabase *database, QXmlStreamAttributes *attributes, + QString *errorMessage) +{ + const int classIndex = indexOfAttribute(*attributes, classAttribute()); + if (classIndex == -1) { + *errorMessage = msgMissingAttribute(classAttribute()); + return false; + } + + TypeRejection rejection; + const QString className = attributes->takeAt(classIndex).value().toString(); + if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) + return false; + + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + const TypeRejection::MatchType type = typeRejectionFromAttribute(name); + switch (type) { + 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 = type; + database->addRejection(rejection); + return true; + } + break; + default: + break; + } + } + + // Special case: When all fields except class are empty, completely exclude class + if (className == QLatin1String("*")) { + *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" + " nor 'field' specified"); + return false; + } + rejection.matchType = TypeRejection::ExcludeClass; + database->addRejection(rejection); + return true; +} + bool Handler::parse(QXmlStreamReader &reader) { m_error.clear(); @@ -412,7 +490,7 @@ bool Handler::parse(QXmlStreamReader &reader) m_error = msgReaderError(reader, reader.errorString()); return false; case QXmlStreamReader::StartElement: - if (!startElement(reader.name(), reader.attributes())) { + if (!startElement(reader)) { m_error = msgReaderError(reader, m_error); return false; } @@ -442,22 +520,6 @@ bool Handler::parse(QXmlStreamReader &reader) return true; } -void Handler::fetchAttributeValues(const QStringRef &name, const QXmlStreamAttributes &atts, - QHash *acceptedAttributes) -{ - Q_ASSERT(acceptedAttributes); - - for (int i = 0; i < atts.length(); ++i) { - const QString key = atts.at(i).name().toString().toLower(); - if (!acceptedAttributes->contains(key)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Unknown attribute for '%1': '%2'").arg(name.toString(), key); - } else { - acceptedAttributes->insert(key, atts.at(i).value().toString()); - } - } -} - bool Handler::endElement(const QStringRef &localName) { if (m_ignoreDepth) { @@ -722,8 +784,9 @@ bool Handler::importFileElement(const QXmlStreamAttributes &atts) return true; } -static bool convertBoolean(const QString &value, const QString &attributeName, bool defaultValue) +static bool convertBoolean(QStringView value, const QString &attributeName, bool defaultValue) { +#ifdef QTBUG_69389_FIXED if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0 || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) { return true; @@ -732,8 +795,19 @@ static bool convertBoolean(const QString &value, const QString &attributeName, b || value.compare(noAttributeValue(), Qt::CaseInsensitive) == 0) { return false; } +#else + if (QtPrivate::compareStrings(value, trueAttributeValue(), Qt::CaseInsensitive) == 0 + || QtPrivate::compareStrings(value, yesAttributeValue(), Qt::CaseInsensitive) == 0) { + return true; + } + if (QtPrivate::compareStrings(value, falseAttributeValue(), Qt::CaseInsensitive) == 0 + || QtPrivate::compareStrings(value, noAttributeValue(), Qt::CaseInsensitive) == 0) { + return false; + } +#endif const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.") - .arg(value, attributeName, + .arg(value) + .arg(attributeName, defaultValue ? yesAttributeValue() : noAttributeValue()); qCWarning(lcShiboken).noquote().nospace() << warn; @@ -794,553 +868,1549 @@ static QString checkSignatureError(const QString& signature, const QString& tag) return QString(); } -void Handler::addFlags(const QString &name, QString flagName, - const QHash &attributes, - const QVersionNumber &since) +void Handler::applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const +{ + type->setCodeGeneration(m_generate); + const int revisionIndex = + indexOfAttribute(*attributes, QStringViewLiteral("revision")); + if (revisionIndex != -1) + type->setRevision(attributes->takeAt(revisionIndex).value().toInt()); +} + +FlagsTypeEntry * + Handler::parseFlagsEntry(const QXmlStreamReader &, + EnumTypeEntry *enumEntry, + const QString &name, QString flagName, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) + { FlagsTypeEntry *ftype = new FlagsTypeEntry(QLatin1String("QFlags<") + name + QLatin1Char('>'), since); - ftype->setOriginator(m_currentEnum); - ftype->setTargetLangPackage(m_currentEnum->targetLangPackage()); + ftype->setOriginator(enumEntry); + ftype->setTargetLangPackage(enumEntry->targetLangPackage()); // Try to get the guess the qualified flag name const int lastSepPos = name.lastIndexOf(colonColon()); if (lastSepPos >= 0 && !flagName.contains(colonColon())) flagName.prepend(name.left(lastSepPos + 2)); ftype->setOriginalName(flagName); - ftype->setCodeGeneration(m_generate); + applyCommonAttributes(ftype, attributes); QString n = ftype->originalName(); QStringList lst = n.split(colonColon()); - if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != m_currentEnum->targetLangQualifier()) { + const QString &targetLangQualifier = enumEntry->targetLangQualifier(); + if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != targetLangQualifier) { qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("enum %1 and flags %2 differ in qualifiers") - .arg(m_currentEnum->targetLangQualifier(), lst.constFirst()); + .arg(targetLangQualifier, lst.constFirst()); } ftype->setFlagsName(lst.constLast()); - m_currentEnum->setFlags(ftype); + enumEntry->setFlags(ftype); m_database->addFlagsType(ftype); m_database->addType(ftype); - QString revision = attributes.value(QLatin1String("flags-revision")); - if (revision.isEmpty()) - revision = attributes.value(QLatin1String("revision")); - ftype->setRevision(revision.toInt()); -} + const int revisionIndex = + indexOfAttribute(*attributes, QStringViewLiteral("flags-revision")); + ftype->setRevision(revisionIndex != -1 + ? attributes->takeAt(revisionIndex).value().toInt() + : enumEntry->revision()); + return ftype; +} + +SmartPointerTypeEntry * + Handler::parseSmartPointerEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QString smartPointerType; + QString getter; + QString refCountMethodName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("type")) { + smartPointerType = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("getter")) { + getter = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("ref-count-method")) { + refCountMethodName = attributes->takeAt(i).value().toString(); + } + } -bool Handler::handleSmartPointerEntry(StackElement *element, - QHash &attributes, - const QString &name, - const QVersionNumber &since) -{ - QString smartPointerType = attributes[QLatin1String("type")]; if (smartPointerType.isEmpty()) { m_error = QLatin1String("No type specified for the smart pointer. Currently supported types: 'shared',"); - return false; + return nullptr; } if (smartPointerType != QLatin1String("shared")) { m_error = QLatin1String("Currently only the 'shared' type is supported."); - return false; + return nullptr; } - QString getter = attributes[QLatin1String("getter")]; if (getter.isEmpty()) { m_error = QLatin1String("No function getter name specified for getting the raw pointer held by the smart pointer."); - return false; + return nullptr; } - QString refCountMethodName = attributes[QLatin1String("ref-count-method")]; QString signature = getter + QLatin1String("()"); - signature = TypeDatabase::normalizedSignature(signature); if (signature.isEmpty()) { m_error = QLatin1String("No signature for the smart pointer getter found."); - return false; + return nullptr; } QString errorString = checkSignatureError(signature, QLatin1String("smart-pointer-type")); if (!errorString.isEmpty()) { m_error = errorString; - return false; + return nullptr; + } + + SmartPointerTypeEntry *type = + new SmartPointerTypeEntry(name, getter, smartPointerType, refCountMethodName, since); + applyCommonAttributes(type, attributes); + return type; +} + +PrimitiveTypeEntry * + Handler::parsePrimitiveTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since); + applyCommonAttributes(type, attributes); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("target-lang-name")) { + type->setTargetLangName(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("target-lang-api-name")) { + type->setTargetLangApiName(attributes->takeAt(i).value().toString()); + } else if (name == preferredConversionAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), + preferredConversionAttribute(), true); + type->setPreferredConversion(v); + } else if (name == preferredTargetLangTypeAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), + preferredTargetLangTypeAttribute(), true); + type->setPreferredTargetLangType(v); + } else if (name == QLatin1String("default-constructor")) { + type->setDefaultConstructor(attributes->takeAt(i).value().toString()); + } } - SmartPointerTypeEntry *type = new SmartPointerTypeEntry(name, - getter, - smartPointerType, - refCountMethodName, - since); + if (type->targetLangName().isEmpty()) + type->setTargetLangName(type->name()); + if (type->targetLangApiName().isEmpty()) + type->setTargetLangApiName(type->name()); type->setTargetLangPackage(m_defaultPackage); - type->setCodeGeneration(m_generate); - element->entry = type; - return true; + return type; } -bool Handler::startElement(const QStringRef &tagName, const QXmlStreamAttributes &atts) +ContainerTypeEntry * + Handler::parseContainerTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) { - if (m_ignoreDepth) { - ++m_ignoreDepth; - return true; + const int typeIndex = indexOfAttribute(*attributes, QStringViewLiteral("type")); + if (typeIndex == -1) { + m_error = QLatin1String("no 'type' attribute specified"); + return nullptr; } - - QVersionNumber since(0, 0); - const QStringRef sinceSpec = atts.value(sinceAttribute()); - if (!sinceSpec.isNull()) { - since = QVersionNumber::fromString(sinceSpec.toString()); - if (since.isNull()) { - m_error = msgInvalidVersion(sinceSpec, m_defaultPackage); - return false; + const QStringRef typeName = attributes->takeAt(typeIndex).value(); + ContainerTypeEntry::Type containerType = containerTypeFromAttribute(typeName); + if (containerType == ContainerTypeEntry::NoContainer) { + m_error = QLatin1String("there is no container of type ") + typeName.toString(); + return nullptr; + } + ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType, since); + applyCommonAttributes(type, attributes); + return type; +} + +EnumTypeEntry * + Handler::parseEnumTypeEntry(const QXmlStreamReader &reader, + const QString &fullName, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QString scope; + QString name = fullName; + const int sep = fullName.lastIndexOf(colonColon()); + if (sep != -1) { + scope = fullName.left(sep); + name = fullName.right(fullName.size() - sep - 2); + } + EnumTypeEntry *entry = new EnumTypeEntry(scope, name, since); + applyCommonAttributes(entry, attributes); + entry->setTargetLangPackage(m_defaultPackage); + + QString flagNames; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("upper-bound")) { + entry->setUpperBound(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("lower-bound")) { + entry->setLowerBound(attributes->takeAt(i).value().toString()); + } else if (name == forceIntegerAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), + forceIntegerAttribute(), false); + entry->setForceInteger(v); + } else if (name == extensibleAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), + extensibleAttribute(), false); + entry->setExtensible(v); + } else if (name == flagsAttribute()) { + flagNames = attributes->takeAt(i).value().toString(); } } - if (!m_defaultPackage.isEmpty() && since > QVersionNumber(0, 0)) { - TypeDatabase* td = TypeDatabase::instance(); - if (!td->checkApiVersion(m_defaultPackage, since)) { - ++m_ignoreDepth; - return true; + // put in the flags parallel... + if (!flagNames.isEmpty()) { + const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); + for (const QString &flagName : flagNameList) + parseFlagsEntry(reader, entry, fullName, flagName.trimmed(), since, attributes); + } + return entry; +} + +ObjectTypeEntry * + Handler::parseInterfaceTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + ObjectTypeEntry *otype = new ObjectTypeEntry(name, since); + applyCommonAttributes(otype, attributes); + QString targetLangName = name; + bool generate = true; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("target-lang-name")) { + targetLangName = attributes->takeAt(i).value().toString(); + } else if (name == generateAttribute()) { + generate = convertBoolean(attributes->takeAt(i).value(), + generateAttribute(), true); } } - if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) - return importFileElement(atts); + InterfaceTypeEntry *itype = + new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); - const StackElement::ElementType elementType = elementFromTag(tagName); - if (elementType == StackElement::None) { - m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); - return false; - } + if (generate) + itype->setCodeGeneration(m_generate); + else + itype->setCodeGeneration(TypeEntry::GenerateForSubclass); - if (m_currentDroppedEntry) { - ++m_currentDroppedEntryDepth; - return true; + otype->setDesignatedInterface(itype); + itype->setOrigin(otype); + return otype; +} + +ValueTypeEntry * + Handler::parseValueTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + ValueTypeEntry *typeEntry = new ValueTypeEntry(name, since); + applyCommonAttributes(typeEntry, attributes); + const int defaultCtIndex = + indexOfAttribute(*attributes, QStringViewLiteral("default-constructor")); + if (defaultCtIndex != -1) + typeEntry->setDefaultConstructor(attributes->takeAt(defaultCtIndex).value().toString()); + return typeEntry; +} + +FunctionTypeEntry * + Handler::parseFunctionTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + const int signatureIndex = indexOfAttribute(*attributes, signatureAttribute()); + if (signatureIndex == -1) { + m_error = msgMissingAttribute(signatureAttribute()); + return nullptr; } + const QString signature = + TypeDatabase::normalizedSignature(attributes->takeAt(signatureIndex).value().toString()); - StackElement* element = new StackElement(m_current); - element->type = elementType; + TypeEntry *existingType = m_database->findType(name); - if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) - customConversionsForReview.clear(); + if (!existingType) { + FunctionTypeEntry *result = new FunctionTypeEntry(name, signature, since); + applyCommonAttributes(result, attributes); + return result; + } - if (element->type == StackElement::Root - || element->type == StackElement::NamespaceTypeEntry - || element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::PrimitiveTypeEntry) { - m_contextStack.push(new StackElementContext()); + if (existingType->type() != TypeEntry::FunctionType) { + m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") + .arg(name); + return nullptr; } - if (element->type & StackElement::TypeEntryMask) { - QHash attributes; - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("revision"), QLatin1String("0")); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes + FunctionTypeEntry *result = reinterpret_cast(existingType); + result->addSignature(signature); + return result; +} - switch (element->type) { - case StackElement::PrimitiveTypeEntry: - attributes.insert(QLatin1String("target-lang-name"), QString()); - attributes.insert(QLatin1String("target-lang-api-name"), QString()); - attributes.insert(QLatin1String("preferred-conversion"), yesAttributeValue()); - attributes.insert(QLatin1String("preferred-target-lang-type"), yesAttributeValue()); - attributes.insert(QLatin1String("default-constructor"), QString()); - break; - case StackElement::ContainerTypeEntry: - attributes.insert(QLatin1String("type"), QString()); - break; - case StackElement::SmartPointerTypeEntry: - attributes.insert(QLatin1String("type"), QString()); - attributes.insert(QLatin1String("getter"), QString()); - attributes.insert(QLatin1String("ref-count-method"), QString()); - break; - case StackElement::EnumTypeEntry: - attributes.insert(flagsAttribute(), QString()); - attributes.insert(QLatin1String("flags-revision"), QString()); - attributes.insert(QLatin1String("upper-bound"), QString()); - attributes.insert(QLatin1String("lower-bound"), QString()); - attributes.insert(QLatin1String("force-integer"), noAttributeValue()); - attributes.insert(QLatin1String("extensible"), noAttributeValue()); - attributes.insert(enumIdentifiedByValueAttribute(), QString()); - attributes.insert(classAttribute(), falseAttributeValue()); - break; - case StackElement::ValueTypeEntry: - attributes.insert(QLatin1String("default-constructor"), QString()); - Q_FALLTHROUGH(); - case StackElement::ObjectTypeEntry: - attributes.insert(QLatin1String("force-abstract"), noAttributeValue()); - attributes.insert(QLatin1String("deprecated"), noAttributeValue()); - attributes.insert(QLatin1String("hash-function"), QString()); - attributes.insert(QLatin1String("stream"), noAttributeValue()); - Q_FALLTHROUGH(); - case StackElement::InterfaceTypeEntry: - attributes[QLatin1String("default-superclass")] = m_defaultSuperclass; - attributes.insert(QLatin1String("polymorphic-id-expression"), QString()); - attributes.insert(QLatin1String("delete-in-main-thread"), noAttributeValue()); - attributes.insert(QLatin1String("held-type"), QString()); - attributes.insert(QLatin1String("copyable"), QString()); - Q_FALLTHROUGH(); - case StackElement::NamespaceTypeEntry: - attributes.insert(QLatin1String("target-lang-name"), QString()); - attributes[QLatin1String("package")] = m_defaultPackage; - attributes.insert(QLatin1String("expense-cost"), QLatin1String("1")); - attributes.insert(QLatin1String("expense-limit"), QLatin1String("none")); - attributes.insert(QLatin1String("polymorphic-base"), noAttributeValue()); - attributes.insert(QLatin1String("generate"), yesAttributeValue()); - attributes.insert(QLatin1String("target-type"), QString()); - attributes.insert(QLatin1String("generic-class"), noAttributeValue()); - break; - case StackElement::FunctionTypeEntry: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("rename"), QString()); - break; - default: - { } // nada - }; +void Handler::applyComplexTypeAttributes(const QXmlStreamReader &, + ComplexTypeEntry *ctype, + QXmlStreamAttributes *attributes) const +{ + bool generate = true; + ctype->setCopyable(ComplexTypeEntry::Unknown); + + QString package = m_defaultPackage; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == streamAttribute()) { + ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute(), 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()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), genericClassAttribute(), false); + ctype->setGenericClass(v); + } else if (name == QLatin1String("target-lang-name")) { + ctype->setTargetLangName(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("polymorphic-base")) { + ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("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 == QLatin1String("held-type")) { + ctype->setHeldType(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("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 == deleteInMainThreadAttribute()) { + if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute(), false)) + ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread); + } else if (name == QLatin1String("target-type")) { + ctype->setTargetType(attributes->takeAt(i).value().toString()); + } + } - fetchAttributeValues(tagName, atts, &attributes); - QString name = attributes[nameAttribute()]; + // The generator code relies on container's package being empty. + if (ctype->type() != TypeEntry::ContainerType) + ctype->setTargetLangPackage(package); - if (m_database->hasDroppedTypeEntries()) { - QString identifier = getNamePrefix(element) + QLatin1Char('.'); - identifier += (element->type == StackElement::FunctionTypeEntry ? attributes[QLatin1String("signature")] : name); - if (m_database->shouldDropTypeEntry(identifier)) { - m_currentDroppedEntry = element; - m_currentDroppedEntryDepth = 1; - if (ReportHandler::isDebug(ReportHandler::SparseDebug)) { - qCDebug(lcShiboken) - << QStringLiteral("Type system entry '%1' was intentionally dropped from generation.").arg(identifier); - } - return true; - } - } + if (InterfaceTypeEntry *di = ctype->designatedInterface()) + di->setTargetLangPackage(package); - // The top level tag 'function' has only the 'signature' tag - // and we should extract the 'name' value from it. - if (element->type == StackElement::FunctionTypeEntry) { - QString signature = attributes[QLatin1String("signature")]; - name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - QString errorString = checkSignatureError(signature, QLatin1String("function")); - if (!errorString.isEmpty()) { - m_error = errorString; - return false; - } - QString rename = attributes[QLatin1String("rename")]; - if (!rename.isEmpty()) { - static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); - Q_ASSERT(functionNameRegExp.isValid()); - if (!functionNameRegExp.match(rename).hasMatch()) { - m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") - + rename + QLatin1String("' is not a valid function name"); - return false; - } - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) - return false; - mod.renamedToName = attributes[QLatin1String("rename")]; - mod.modifiers |= Modification::Rename; - m_contextStack.top()->functionMods << mod; - } - } + if (generate) + ctype->setCodeGeneration(m_generate); + else + ctype->setCodeGeneration(TypeEntry::GenerateForSubclass); +} - // 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->type != StackElement::PrimitiveTypeEntry - && element->type != StackElement::FunctionTypeEntry) { - TypeEntry *tmp = m_database->findType(name); - if (tmp) - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Duplicate type entry: '%1'").arg(name); +bool Handler::parseRenameFunction(const QXmlStreamReader &, + QString *name, QXmlStreamAttributes *attributes) +{ + QString signature; + QString rename; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef 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(); + } else if (name == renameAttribute()) { + rename = attributes->takeAt(i).value().toString(); } + } - if (element->type == StackElement::EnumTypeEntry) { - const QString identifiedByValue = attributes.value(enumIdentifiedByValueAttribute()); - if (name.isEmpty()) { - name = identifiedByValue; - } else if (!identifiedByValue.isEmpty()) { - m_error = QLatin1String("can't specify both 'name' and 'identified-by-value' attributes"); - return false; - } - } + if (signature.isEmpty()) { + m_error = msgMissingAttribute(signatureAttribute()); + return false; + } - // Fix type entry name using nesting information. - if (element->type & StackElement::TypeEntryMask - && element->parent && element->parent->type != StackElement::Root) { - name = element->parent->entry->name() + colonColon() + name; - } + *name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); + QString errorString = checkSignatureError(signature, QLatin1String("function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } - if (name.isEmpty()) { - m_error = QLatin1String("no 'name' attribute specified"); + if (!rename.isEmpty()) { + static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); + Q_ASSERT(functionNameRegExp.isValid()); + if (!functionNameRegExp.match(rename).hasMatch()) { + m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") + + rename + QLatin1String("' is not a valid function name"); return false; } + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + m_contextStack.top()->functionMods << mod; + } + return true; +} - switch (element->type) { - case StackElement::CustomTypeEntry: - element->entry = new TypeEntry(name, TypeEntry::CustomType, since); - break; - case StackElement::PrimitiveTypeEntry: { - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - QString targetLangApiName = attributes[QLatin1String("target-lang-api-name")]; - QString preferredConversion = attributes[QLatin1String("preferred-conversion")].toLower(); - QString preferredTargetLangType = attributes[QLatin1String("preferred-target-lang-type")].toLower(); - QString defaultConstructor = attributes[QLatin1String("default-constructor")]; - - if (targetLangName.isEmpty()) - targetLangName = name; - if (targetLangApiName.isEmpty()) - targetLangApiName = name; - - PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since); - type->setCodeGeneration(m_generate); - type->setTargetLangName(targetLangName); - type->setTargetLangApiName(targetLangApiName); - type->setTargetLangPackage(m_defaultPackage); - type->setDefaultConstructor(defaultConstructor); - - bool preferred; - preferred = convertBoolean(preferredConversion, QLatin1String("preferred-conversion"), true); - type->setPreferredConversion(preferred); - preferred = convertBoolean(preferredTargetLangType, - QLatin1String("preferred-target-lang-type"), true); - type->setPreferredTargetLangType(preferred); - - element->entry = type; - } - break; +bool Handler::parseInjectDocumentation(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (!m_current->parent || (m_current->parent->type & validParent) == 0) { + m_error = QLatin1String("inject-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"); + return false; + } - case StackElement::ContainerTypeEntry: { - QString typeName = attributes[QLatin1String("type")]; - ContainerTypeEntry::Type containerType = - containerTypeFromAttribute(typeName); - if (typeName.isEmpty()) { - m_error = QLatin1String("no 'type' attribute specified"); + TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace; + TypeSystem::Language lang = TypeSystem::NativeCode; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("mode")) { + const QStringRef modeName = attributes->takeAt(i).value(); + mode = docModificationFromAttribute(modeName); + if (mode == TypeSystem::DocModificationInvalid) { + m_error = QLatin1String("Unknown documentation injection mode: ") + modeName; + return false; + } + } else if (name == formatAttribute()) { + const QStringRef format = attributes->takeAt(i).value(); + lang = languageFromAttribute(format); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(format); + return false; + } + } + } + + QString signature = m_current->type & StackElement::TypeEntryMask + ? QString() : m_currentSignature; + DocModification mod(mode, signature); + mod.setFormat(lang); + m_contextStack.top()->docModifications << mod; + return true; +} + +bool Handler::parseModifyDocumentation(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (!m_current->parent || (m_current->parent->type & validParent) == 0) { + m_error = QLatin1String("modify-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"); + return false; + } + + const int xpathIndex = indexOfAttribute(*attributes, xPathAttribute()); + if (xpathIndex == -1) { + m_error = msgMissingAttribute(xPathAttribute()); + return false; + } + + const QString xpath = attributes->takeAt(xpathIndex).value().toString(); + QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; + m_contextStack.top()->docModifications + << DocModification(xpath, signature); + return true; +} + +TypeSystemTypeEntry *Handler::parseRootElement(const QXmlStreamReader &, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef 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(); + } + + TypeSystemTypeEntry* moduleEntry = + reinterpret_cast(m_database->findType(m_defaultPackage)); + if (!moduleEntry) + moduleEntry = new TypeSystemTypeEntry(m_defaultPackage, since); + moduleEntry->setCodeGeneration(m_generate); + + if ((m_generate == TypeEntry::GenerateForSubclass || + m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) + TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); + + if (!moduleEntry->qualifiedCppName().isEmpty()) + m_database->addType(moduleEntry); + return moduleEntry; +} + +bool Handler::loadTypesystem(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + QString typeSystemName; + bool generateChild = true; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef 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 = QLatin1String("No typesystem name specified"); + return false; + } + const bool result = + m_database->parseFile(typeSystemName, m_currentPath, generateChild + && m_generate == TypeEntry::GenerateAll); + if (!result) + m_error = QStringLiteral("Failed to parse: '%1'").arg(typeSystemName); + return result; +} + +bool Handler::parseRejectEnumValue(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + if (!m_currentEnum) { + m_error = QLatin1String(" node must be used inside a node"); + return false; + } + const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString()); + return true; +} + +bool Handler::parseReplaceArgumentType(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Type replacement can only be specified for argument modifications"); + return false; + } + const int modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute()); + if (modifiedTypeIndex == -1) { + m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = + attributes->takeAt(modifiedTypeIndex).value().toString(); + return true; +} + +bool Handler::parseCustomConversion(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument + && topElement.type != StackElement::ValueTypeEntry + && topElement.type != StackElement::PrimitiveTypeEntry + && topElement.type != StackElement::ContainerTypeEntry) { + m_error = QLatin1String("Conversion rules can only be specified for argument modification, " + "value-type, primitive-type or container-type conversion."); + return false; + } + + QString sourceFile; + TypeSystem::Language lang = TypeSystem::NativeCode; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef languageAttribute = attributes->takeAt(i).value(); + lang = languageFromAttribute(languageAttribute); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); + return false; + } + } else if (name == QLatin1String("file")) { + sourceFile = attributes->takeAt(i).value().toString(); + } + } + + if (topElement.type == StackElement::ModifyArgument) { + CodeSnip snip; + snip.language = lang; + m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); + return true; + } + + if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { + m_error = QLatin1String("Types can have only one conversion rule"); + return false; + } + + // The old conversion rule tag that uses a file containing the conversion + // will be kept temporarily for compatibility reasons. + if (!sourceFile.isEmpty()) { + if (m_generate != TypeEntry::GenerateForSubclass + && m_generate != TypeEntry::GenerateNothing) { + + const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; + if (lang == TypeSystem::TargetLangCode) + conversionFlag = TARGET_CONVERSION_RULE_FLAG; + + QFile conversionSource(sourceFile); + if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { + topElement.entry->setConversionRule(QLatin1String(conversionFlag) + QString::fromUtf8(conversionSource.readAll())); + } else { + qCWarning(lcShiboken).noquote().nospace() + << "File containing conversion code for " + << topElement.entry->name() << " type does not exist or is not readable: " + << sourceFile; + } + } + } + + CustomConversion* customConversion = new CustomConversion(m_current->entry); + customConversionsForReview.append(customConversion); + return true; +} + +bool Handler::parseAddConversion(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::TargetToNative) { + m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); + return false; + } + QString sourceTypeName; + QString typeCheck; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("type")) + sourceTypeName = attributes->takeAt(i).value().toString(); + else if (name == QLatin1String("check")) + typeCheck = attributes->takeAt(i).value().toString(); + } + if (sourceTypeName.isEmpty()) { + m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); + return false; + } + m_current->entry->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); + m_contextStack.top()->codeSnips << CodeSnip(); + return true; +} + +static bool parseIndex(const QStringRef &index, int *result, QString *errorMessage) +{ + bool ok = false; + *result = index.toInt(&ok); + if (!ok) + *errorMessage = QStringLiteral("Cannot convert '%1' to integer").arg(index); + return ok; +} + +static bool parseArgumentIndex(const QStringRef &index, int *result, QString *errorMessage) +{ + if (index == QLatin1String("return")) { + *result = 0; + return true; + } + if (index == QLatin1String("this")) { + *result = -1; + return true; + } + return parseIndex(index, result, errorMessage); +} + +bool Handler::parseModifyArgument(const QXmlStreamReader &, + const StackElement &topElement, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyFunction + && topElement.type != StackElement::AddFunction) { + m_error = QString::fromLatin1("argument modification requires function" + " modification as parent, was %1") + .arg(topElement.type, 0, 16); + return false; + } + + QStringRef index; + QString replaceValue; + bool resetAfterUse = false; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + index = attributes->takeAt(i).value(); + } else if (name == QLatin1String("replace-value")) { + replaceValue = attributes->takeAt(i).value().toString(); + } else if (name == invalidateAfterUseAttribute()) { + resetAfterUse = convertBoolean(attributes->takeAt(i).value(), + invalidateAfterUseAttribute(), false); + } + } + + if (index.isEmpty()) { + m_error = msgMissingAttribute(indexAttribute()); + return false; + } + + int idx; + if (!parseArgumentIndex(index, &idx, &m_error)) + return false; + + if (!replaceValue.isEmpty() && idx) { + m_error = QLatin1String("replace-value is only supported for return values (index=0)."); + return false; + } + + ArgumentModification argumentModification = ArgumentModification(idx); + argumentModification.replace_value = replaceValue; + argumentModification.resetAfterUse = resetAfterUse; + m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); + return true; +} + +bool Handler::parseNoNullPointer(const QXmlStreamReader &, + const StackElement &topElement, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + return false; + } + + ArgumentModification &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods.last(); + lastArgMod.noNullPointers = true; + + const int defaultValueIndex = + indexOfAttribute(*attributes, QStringViewLiteral("default-value")); + if (defaultValueIndex != -1) { + if (lastArgMod.index == 0) { + lastArgMod.nullPointerDefaultValue = + attributes->takeAt(defaultValueIndex).value().toString(); + } else { + qCWarning(lcShiboken) + << "default values for null pointer guards are only effective for return values"; + } + } + return true; +} + +bool Handler::parseDefineOwnership(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("define-ownership requires argument modification as parent"); + return false; + } + + TypeSystem::Language lang = TypeSystem::TargetLangCode; + QStringRef ownership; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef className = attributes->takeAt(i).value(); + lang = languageFromAttribute(className); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(className); + return false; + } + } else if (name == ownershipAttribute()) { + ownership = attributes->takeAt(i).value(); + } + } + const TypeSystem::Ownership owner = ownershipFromFromAttribute(ownership); + if (owner == TypeSystem::InvalidOwnership) { + m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownership); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; + return true; +} + +bool Handler::parseArgumentMap(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::CodeSnipMask)) { + m_error = QLatin1String("Argument maps requires code injection as parent"); + return false; + } + + int pos = 1; + QString metaName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + if (!parseIndex(attributes->takeAt(i).value(), &pos, &m_error)) + return false; + if (pos <= 0) { + m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); + return false; + } + } else if (name == QLatin1String("meta-name")) { + metaName = attributes->takeAt(i).value().toString(); + } + } + + if (metaName.isEmpty()) + qCWarning(lcShiboken) << "Empty meta name in argument map"; + + if (topElement.type == StackElement::InjectCodeInFunction) { + m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = metaName; + } else { + qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " + "into functions."; + } + return true; +} + +bool Handler::parseRemoval(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyFunction) { + m_error = QLatin1String("Function modification parent required"); + return false; + } + + TypeSystem::Language lang = TypeSystem::All; + const int classIndex = indexOfAttribute(*attributes, classAttribute()); + if (classIndex != -1) { + const QStringRef value = attributes->takeAt(classIndex).value(); + lang = languageFromAttribute(value); + if (lang == TypeSystem::TargetLangCode) // "target" means TargetLangAndNativeCode here + lang = TypeSystem::TargetLangAndNativeCode; + if (lang != TypeSystem::TargetLangAndNativeCode && lang != TypeSystem::All) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(value); + return false; + } + } + m_contextStack.top()->functionMods.last().removal = lang; + return true; +} + +bool Handler::parseRename(const QXmlStreamReader &, + StackElement::ElementType type, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyField + && topElement.type != StackElement::ModifyFunction + && topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Function, field or argument modification parent required"); + return false; + } + + Modification *mod = nullptr; + if (topElement.type == StackElement::ModifyFunction) + mod = &m_contextStack.top()->functionMods.last(); + else if (topElement.type == StackElement::ModifyField) + mod = &m_contextStack.top()->fieldMods.last(); + + Modification::Modifiers modifierFlag = Modification::Rename; + if (type == StackElement::Rename) { + const int toIndex = indexOfAttribute(*attributes, toAttribute()); + if (toIndex == -1) { + m_error = msgMissingAttribute(toAttribute()); + return false; + } + const QString renamed_to = attributes->takeAt(toIndex).value().toString(); + if (topElement.type == StackElement::ModifyFunction) + mod->setRenamedTo(renamed_to); + else if (topElement.type == StackElement::ModifyField) + mod->setRenamedTo(renamed_to); + else + m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to; + } else { + const int modifierIndex = indexOfAttribute(*attributes, modifierAttribute()); + if (modifierIndex == -1) { + m_error = msgMissingAttribute(modifierAttribute()); + return false; + } + const QStringRef modifier = attributes->takeAt(modifierIndex).value(); + modifierFlag = modifierFromAttribute(modifier); + if (modifierFlag == Modification::InvalidModifier) { + m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); + return false; + } + } + + if (mod) + mod->modifiers |= modifierFlag; + return true; +} + +bool Handler::parseModifyField(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + FieldModification fm; + fm.modifiers = FieldModification::Readable | FieldModification::Writable; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) { + fm.name = attributes->takeAt(i).value().toString(); + } else if (name == removeAttribute()) { + if (!convertRemovalAttribute(attributes->takeAt(i).value(), fm, m_error)) + return false; + } else if (name == readAttribute()) { + if (!convertBoolean(attributes->takeAt(i).value(), readAttribute(), true)) + fm.modifiers &= ~FieldModification::Readable; + } else if (name == writeAttribute()) { + if (!convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true)) + fm.modifiers &= ~FieldModification::Writable; + } + } + if (fm.name.isEmpty()) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + m_contextStack.top()->fieldMods << fm; + return true; +} + +bool Handler::parseAddFunction(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) { + m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + QString originalSignature; + QString returnType = QLatin1String("void"); + bool staticFunction = false; + QStringRef access; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("signature")) { + originalSignature = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("return-type")) { + returnType = attributes->takeAt(i).value().toString(); + } else if (name == staticAttribute()) { + staticFunction = convertBoolean(attributes->takeAt(i).value(), + staticAttribute(), false); + } else if (name == accessAttribute()) { + access = attributes->takeAt(i).value(); + } + } + + QString signature = TypeDatabase::normalizedSignature(originalSignature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for the added function"); + return false; + } + + QString errorString = checkSignatureError(signature, QLatin1String("add-function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + AddedFunction func(signature, returnType); + func.setStatic(staticFunction); + if (!signature.contains(QLatin1Char('('))) + signature += QLatin1String("()"); + m_currentSignature = signature; + + if (!access.isEmpty()) { + const AddedFunction::Access a = addedFunctionAccessFromAttribute(access); + if (a == AddedFunction::InvalidAccess) { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + func.setAccess(a); + } + + m_contextStack.top()->addedFunctions << func; + + FunctionModification mod; + if (!mod.setSignature(m_currentSignature, &m_error)) + return false; + mod.setOriginalSignature(originalSignature); + m_contextStack.top()->functionMods << mod; + return true; +} + +bool Handler::parseModifyFunction(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { + m_error = QString::fromLatin1("Modify function requires complex type as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + + QString originalSignature; + QStringRef access; + QStringRef removal; + QString rename; + QString association; + bool deprecated = false; + bool isThread = false; + bool allowThread = false; + bool virtualSlot = false; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("signature")) { + originalSignature = attributes->takeAt(i).value().toString(); + } else if (name == accessAttribute()) { + access = attributes->takeAt(i).value(); + } else if (name == renameAttribute()) { + rename = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("associated-to")) { + association = attributes->takeAt(i).value().toString(); + } else if (name == removeAttribute()) { + removal = attributes->takeAt(i).value(); + } else if (name == deprecatedAttribute()) { + deprecated = convertBoolean(attributes->takeAt(i).value(), + deprecatedAttribute(), false); + } else if (name == threadAttribute()) { + isThread = convertBoolean(attributes->takeAt(i).value(), + threadAttribute(), false); + } else if (name == allowThreadAttribute()) { + allowThread = convertBoolean(attributes->takeAt(i).value(), + allowThreadAttribute(), false); + } else if (name == virtualSlotAttribute()) { + virtualSlot = convertBoolean(attributes->takeAt(i).value(), + virtualSlotAttribute(), false); + } + } + + const QString signature = TypeDatabase::normalizedSignature(originalSignature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for modified function"); + return false; + } + + QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + mod.setOriginalSignature(originalSignature); + m_currentSignature = signature; + + if (!access.isEmpty()) { + const Modification::Modifiers m = modifierFromAttribute(access); + if ((m & (Modification::AccessModifierMask | Modification::FinalMask)) == 0) { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + mod.modifiers |= m; + } + + if (deprecated) + mod.modifiers |= Modification::Deprecated; + + if (!removal.isEmpty() && !convertRemovalAttribute(removal, mod, m_error)) + return false; + + if (!rename.isEmpty()) { + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + } + + if (!association.isEmpty()) + mod.association = association; + + mod.setIsThread(isThread); + mod.setAllowThread(allowThread); + if (virtualSlot) + mod.modifiers |= Modification::VirtualSlot; + + m_contextStack.top()->functionMods << mod; + return true; +} + +bool Handler::parseReplaceDefaultExpression(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ModifyArgument)) { + m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); + return false; + } + const int withIndex = indexOfAttribute(*attributes, QStringViewLiteral("with")); + if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) { + m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + return false; + } + + m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = + attributes->takeAt(withIndex).value().toString(); + return true; +} + +CustomFunction * + Handler::parseCustomMetaConstructor(const QXmlStreamReader &, + StackElement::ElementType type, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + QString functionName = topElement.entry->name().toLower() + + (type == StackElement::CustomMetaConstructor + ? QLatin1String("_create") : QLatin1String("_delete")); + QString paramName = QLatin1String("copy"); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) + functionName = attributes->takeAt(i).value().toString(); + else if (name == QLatin1String("param-name")) + paramName = attributes->takeAt(i).value().toString(); + } + CustomFunction *func = new CustomFunction(functionName); + func->paramName = paramName; + return func; +} + +bool Handler::parseReferenceCount(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("reference-count must be child of modify-argument"); + return false; + } + + ReferenceCount rc; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == actionAttribute()) { + const QStringRef action = attributes->takeAt(i).value(); + rc.action = referenceCountFromAttribute(action); + if (rc.action == ReferenceCount::Invalid) { + m_error = QLatin1String("unrecognized value '") + action + + QLatin1String("' for action attribute."); return false; - } else if (containerType == ContainerTypeEntry::NoContainer) { - m_error = QLatin1String("there is no container of type ") + typeName; + } + } else if (name == QLatin1String("variable-name")) { + rc.varName = attributes->takeAt(i).value().toString(); + } + } + + m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); + return true; +} + +bool Handler::parseParentOwner(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("parent-policy must be child of modify-argument"); + return false; + } + ArgumentOwner ao; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + const QStringRef index = attributes->takeAt(i).value(); + if (!parseArgumentIndex(index, &ao.index, &m_error)) + return false; + } else if (name == actionAttribute()) { + const QStringRef action = attributes->takeAt(i).value(); + ao.action = argumentOwnerActionFromAttribute(action); + if (ao.action == ArgumentOwner::Invalid) { + m_error = QLatin1String("Invalid parent actionr '") + action + QLatin1String("'."); return false; } + } + } + m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; + return true; +} + +bool Handler::parseInjectCode(const QXmlStreamReader &, + const StackElement &topElement, + StackElement* element, QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ComplexTypeEntryMask) + && (topElement.type != StackElement::AddFunction) + && (topElement.type != StackElement::ModifyFunction) + && (topElement.type != StackElement::Root)) { + m_error = QLatin1String("wrong parent type for code injection"); + return false; + } + + TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning; + TypeSystem::Language lang = TypeSystem::TargetLangCode; + QString fileName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef className = attributes->takeAt(i).value(); + lang = languageFromAttribute(className); + if (lang == TypeSystem::NoLanguage) { + m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); + return false; + } + } else if (name == positionAttribute()) { + const QStringRef value = attributes->takeAt(i).value(); + position = codeSnipPositionFromAttribute(value); + if (position == TypeSystem::CodeSnipPositionInvalid) { + m_error = QStringLiteral("Invalid position: '%1'").arg(value); + return false; + } + } else if (name == QLatin1String("file")) { + fileName = attributes->takeAt(i).value().toString(); + } + } + + CodeSnip snip; + snip.position = position; + snip.language = lang; + bool in_file = false; + + // Handler constructor.... + if (m_generate != TypeEntry::GenerateForSubclass && + m_generate != TypeEntry::GenerateNothing && + !fileName.isEmpty()) { + const QString resolved = m_database->modifiedTypesystemFilepath(fileName, m_currentPath); + if (QFile::exists(resolved)) { + QFile codeFile(resolved); + if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { + QString content = QLatin1String("// ========================================================================\n" + "// START of custom code block [file: "); + content += fileName; + content += QLatin1String("]\n"); + content += QString::fromUtf8(codeFile.readAll()); + content += QLatin1String("\n// END of custom code block [file: "); + content += fileName; + content += QLatin1String("]\n// ========================================================================\n"); + snip.addCode(content); + in_file = true; + } + } else { + qCWarning(lcShiboken).noquote().nospace() + << "File for inject code not exist: " << QDir::toNativeSeparators(fileName); + } + + } + + if (snip.language == TypeSystem::Interface + && topElement.type != StackElement::InterfaceTypeEntry) { + m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); + return false; + } + + if (topElement.type == StackElement::ModifyFunction + || topElement.type == StackElement::AddFunction) { + if (snip.language == TypeSystem::ShellDeclaration) { + m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); + return false; + } + + FunctionModification &mod = m_contextStack.top()->functionMods.last(); + mod.snips << snip; + if (in_file) + mod.modifiers |= FunctionModification::CodeInjection; + element->type = StackElement::InjectCodeInFunction; + } else if (topElement.type == StackElement::Root) { + element->entry->addCodeSnip(snip); + } else if (topElement.type != StackElement::Root) { + m_contextStack.top()->codeSnips << snip; + } + return true; +} + +bool Handler::parseInclude(const QXmlStreamReader &, + const StackElement &topElement, + TypeEntry *entry, QXmlStreamAttributes *attributes) +{ + QString fileName; + QStringRef location; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("file-name")) + fileName = attributes->takeAt(i).value().toString(); + else if (name == locationAttribute()) + location = attributes->takeAt(i).value(); + } + const Include::IncludeType loc = locationFromAttribute(location); + if (loc == Include::InvalidInclude) { + m_error = QStringLiteral("Location not recognized: '%1'").arg(location); + return false; + } + + Include inc(loc, fileName); + if (topElement.type + & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { + entry->setInclude(inc); + } else if (topElement.type == StackElement::ExtraIncludes) { + entry->addExtraInclude(inc); + } else { + m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + return false; + } + if (InterfaceTypeEntry *di = entry->designatedInterface()) { + di->setInclude(entry->include()); + di->setExtraIncludes(entry->extraIncludes()); + } + return true; +} + +TemplateInstance * + Handler::parseTemplateInstanceEnum(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::CodeSnipMask) && + (topElement.type != StackElement::Template) && + (topElement.type != StackElement::CustomMetaConstructor) && + (topElement.type != StackElement::CustomMetaDestructor) && + (topElement.type != StackElement::NativeToTarget) && + (topElement.type != StackElement::AddConversion) && + (topElement.type != StackElement::ConversionRule)) { + m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ + "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); + return nullptr; + } + const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return nullptr; + } + return new TemplateInstance(attributes->takeAt(nameIndex).value().toString()); +} + +bool Handler::parseReplace(const QXmlStreamReader &, + const StackElement &topElement, + StackElement *element, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::TemplateInstanceEnum) { + m_error = QLatin1String("Can only insert replace rules into insert-template."); + return false; + } + QString from; + QString to; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("from")) + from = attributes->takeAt(i).value().toString(); + else if (name == toAttribute()) + to = attributes->takeAt(i).value().toString(); + } + element->parent->value.templateInstance->addReplaceRule(from, to); + return true; +} + +bool Handler::startElement(const QXmlStreamReader &reader) +{ + if (m_ignoreDepth) { + ++m_ignoreDepth; + return true; + } - ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType, since); - type->setCodeGeneration(m_generate); - element->entry = type; + const QStringRef tagName = reader.name(); + QXmlStreamAttributes attributes = reader.attributes(); + + QVersionNumber since(0, 0); + int index = indexOfAttribute(attributes, sinceAttribute()); + if (index != -1) { + const QStringRef sinceSpec = attributes.takeAt(index).value(); + since = QVersionNumber::fromString(sinceSpec.toString()); + if (since.isNull()) { + m_error = msgInvalidVersion(sinceSpec, m_defaultPackage); + return false; } - break; + } - case StackElement::SmartPointerTypeEntry: { - bool result = handleSmartPointerEntry(element, attributes, name, since); - if (!result) - return result; + if (!m_defaultPackage.isEmpty() && since > QVersionNumber(0, 0)) { + TypeDatabase* td = TypeDatabase::instance(); + if (!td->checkApiVersion(m_defaultPackage, since)) { + ++m_ignoreDepth; + return true; } - break; + } - case StackElement::EnumTypeEntry: { - QStringList names = name.split(colonColon()); - if (names.size() == 1) - m_currentEnum = new EnumTypeEntry(QString(), name, since); - else - m_currentEnum = - new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join(colonColon()), - names.constLast(), since); - element->entry = m_currentEnum; - m_currentEnum->setCodeGeneration(m_generate); - m_currentEnum->setTargetLangPackage(m_defaultPackage); - m_currentEnum->setUpperBound(attributes[QLatin1String("upper-bound")]); - m_currentEnum->setLowerBound(attributes[QLatin1String("lower-bound")]); - m_currentEnum->setForceInteger(convertBoolean(attributes[QLatin1String("force-integer")], QLatin1String("force-integer"), false)); - m_currentEnum->setExtensible(convertBoolean(attributes[QLatin1String("extensible")], QLatin1String("extensible"), false)); - - // put in the flags parallel... - const QString flagNames = attributes.value(flagsAttribute()); - if (!flagNames.isEmpty()) { - const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); - for (const QString &flagName : flagNameList) - addFlags(name, flagName.trimmed(), attributes, since); + if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) + return importFileElement(attributes); + + const StackElement::ElementType elementType = elementFromTag(tagName); + if (elementType == StackElement::None) { + m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); + return false; + } + + if (m_currentDroppedEntry) { + ++m_currentDroppedEntryDepth; + return true; + } + + StackElement* element = new StackElement(m_current); + element->type = elementType; + + if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) + customConversionsForReview.clear(); + + if (element->type == StackElement::Root + || element->type == StackElement::NamespaceTypeEntry + || element->type == StackElement::InterfaceTypeEntry + || element->type == StackElement::ObjectTypeEntry + || element->type == StackElement::ValueTypeEntry + || element->type == StackElement::PrimitiveTypeEntry) { + m_contextStack.push(new StackElementContext()); + } + + if (element->type & StackElement::TypeEntryMask) { + QString name; + if (element->type != StackElement::FunctionTypeEntry) { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex != -1) { + name = attributes.takeAt(nameIndex).value().toString(); + } else if (element->type != StackElement::EnumTypeEntry) { // anonymous enum? + m_error = msgMissingAttribute(nameAttribute()); + return false; } } - break; - case StackElement::InterfaceTypeEntry: { - ObjectTypeEntry *otype = new ObjectTypeEntry(name, since); - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - if (targetLangName.isEmpty()) - targetLangName = name; - InterfaceTypeEntry *itype = - new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); - - if (!convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true)) - itype->setCodeGeneration(TypeEntry::GenerateForSubclass); - else - itype->setCodeGeneration(m_generate); - otype->setDesignatedInterface(itype); - itype->setOrigin(otype); - element->entry = otype; - } - Q_FALLTHROUGH(); - case StackElement::ValueTypeEntry: { - if (!element->entry) { - ValueTypeEntry* typeEntry = new ValueTypeEntry(name, since); - QString defaultConstructor = attributes[QLatin1String("default-constructor")]; - if (!defaultConstructor.isEmpty()) - typeEntry->setDefaultConstructor(defaultConstructor); - element->entry = typeEntry; + if (m_database->hasDroppedTypeEntries()) { + QString identifier = getNamePrefix(element) + QLatin1Char('.'); + identifier += element->type == StackElement::FunctionTypeEntry + ? attributes.value(signatureAttribute()).toString() + : name; + if (m_database->shouldDropTypeEntry(identifier)) { + m_currentDroppedEntry = element; + m_currentDroppedEntryDepth = 1; + if (ReportHandler::isDebug(ReportHandler::SparseDebug)) { + qCDebug(lcShiboken) + << QStringLiteral("Type system entry '%1' was intentionally dropped from generation.").arg(identifier); + } + return true; } + } - Q_FALLTHROUGH(); - case StackElement::NamespaceTypeEntry: - if (!element->entry) - element->entry = new NamespaceTypeEntry(name, since); + // The top level tag 'function' has only the 'signature' tag + // and we should extract the 'name' value from it. + if (element->type == StackElement::FunctionTypeEntry + && !parseRenameFunction(reader, &name, &attributes)) { + return false; + } - Q_FALLTHROUGH(); - case StackElement::ObjectTypeEntry: - if (!element->entry) - element->entry = new ObjectTypeEntry(name, since); - - element->entry->setStream(attributes[QLatin1String("stream")] == yesAttributeValue()); - - ComplexTypeEntry *ctype = static_cast(element->entry); - ctype->setTargetLangPackage(attributes[QLatin1String("package")]); - ctype->setDefaultSuperclass(attributes[QLatin1String("default-superclass")]); - ctype->setGenericClass(convertBoolean(attributes[QLatin1String("generic-class")], QLatin1String("generic-class"), false)); - - if (!convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true)) - element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass); - else - element->entry->setCodeGeneration(m_generate); - - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - if (!targetLangName.isEmpty()) - ctype->setTargetLangName(targetLangName); - - ctype->setIsPolymorphicBase(convertBoolean(attributes[QLatin1String("polymorphic-base")], QLatin1String("polymorphic-base"), false)); - ctype->setPolymorphicIdValue(attributes[QLatin1String("polymorphic-id-expression")]); - //Copyable - if (attributes[QLatin1String("copyable")].isEmpty()) - ctype->setCopyable(ComplexTypeEntry::Unknown); - else { - if (convertBoolean(attributes[QLatin1String("copyable")], QLatin1String("copyable"), false)) - ctype->setCopyable(ComplexTypeEntry::CopyableSet); - else - ctype->setCopyable(ComplexTypeEntry::NonCopyableSet); + // 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->type != StackElement::PrimitiveTypeEntry + && element->type != StackElement::FunctionTypeEntry) { + TypeEntry *tmp = m_database->findType(name); + if (tmp) + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Duplicate type entry: '%1'").arg(name); + } + if (element->type == StackElement::EnumTypeEntry) { + const int 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 = QLatin1String("can't specify both 'name' and 'identified-by-value' attributes"); + return false; } + } - if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) - ctype->setHashFunction(attributes[QLatin1String("hash-function")]); + // Fix type entry name using nesting information. + if (element->type & StackElement::TypeEntryMask + && element->parent && element->parent->type != StackElement::Root) { + name = element->parent->entry->name() + colonColon() + name; + } - ctype->setHeldType(attributes[QLatin1String("held-type")]); + if (name.isEmpty()) { + m_error = QLatin1String("no 'name' attribute specified"); + return false; + } - if (element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry) { - if (convertBoolean(attributes[QLatin1String("force-abstract")], QLatin1String("force-abstract"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract); - if (convertBoolean(attributes[QLatin1String("deprecated")], QLatin1String("deprecated"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); + switch (element->type) { + case StackElement::CustomTypeEntry: + element->entry = new TypeEntry(name, TypeEntry::CustomType, since); + break; + case StackElement::PrimitiveTypeEntry: + element->entry = parsePrimitiveTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!element->entry)) + return false; + break; + case StackElement::ContainerTypeEntry: + if (ContainerTypeEntry *ce = parseContainerTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ce, &attributes); + element->entry = ce; + } else { + return false; } + break; - if (element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::ObjectTypeEntry) { - if (convertBoolean(attributes[QLatin1String("delete-in-main-thread")], QLatin1String("delete-in-main-thread"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread); + case StackElement::SmartPointerTypeEntry: + if (SmartPointerTypeEntry *se = parseSmartPointerEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, se, &attributes); + element->entry = se; + } else { + return false; } + break; + case StackElement::EnumTypeEntry: + m_currentEnum = parseEnumTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!m_currentEnum)) + return false; + element->entry = m_currentEnum; + break; - QString targetType = attributes[QLatin1String("target-type")]; - if (!targetType.isEmpty() && element->entry->isComplex()) - static_cast(element->entry)->setTargetType(targetType); - - // ctype->setInclude(Include(Include::IncludePath, ctype->name())); - ctype = ctype->designatedInterface(); - if (ctype) - ctype->setTargetLangPackage(attributes[QLatin1String("package")]); - - } - break; - case StackElement::FunctionTypeEntry: { - QString signature = attributes[QLatin1String("signature")]; - signature = TypeDatabase::normalizedSignature(signature); - element->entry = m_database->findType(name); - if (element->entry) { - if (element->entry->type() == TypeEntry::FunctionType) { - reinterpret_cast(element->entry)->addSignature(signature); - } else { - m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") - .arg(name); - return false; - } + case StackElement::InterfaceTypeEntry: + if (ObjectTypeEntry *oe = parseInterfaceTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, oe, &attributes); + element->entry = oe; } else { - element->entry = new FunctionTypeEntry(name, signature, since); - element->entry->setCodeGeneration(m_generate); + return false; } - } - break; + break; + case StackElement::ValueTypeEntry: + if (ValueTypeEntry *ve = parseValueTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ve, &attributes); + element->entry = ve; + } else { + return false; + } + break; + case StackElement::NamespaceTypeEntry: + element->entry = new NamespaceTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast(element->entry), &attributes); + break; + case StackElement::ObjectTypeEntry: + element->entry = new ObjectTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast(element->entry), &attributes); + break; + case StackElement::FunctionTypeEntry: + element->entry = parseFunctionTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!element->entry)) + return false; + break; default: Q_ASSERT(false); }; if (element->entry) { m_database->addType(element->entry); - element->entry->setRevision(attributes.value(QLatin1String("revision")).toInt()); } else { qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("Type: %1 was rejected by typesystem").arg(name); } } else if (element->type == StackElement::InjectDocumentation) { - // check the XML tag attributes - QHash attributes; - attributes.insert(QLatin1String("mode"), QLatin1String("replace")); - attributes.insert(formatAttribute(), QLatin1String("native")); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - - fetchAttributeValues(tagName, atts, &attributes); - - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (m_current->parent && m_current->parent->type & validParent) { - const QString modeName = attributes.value(QLatin1String("mode")); - const TypeSystem::DocModificationMode mode = docModificationFromAttribute(modeName); - if (mode == TypeSystem::DocModificationInvalid) { - m_error = QLatin1String("Unknow documentation injection mode: ") + modeName; - return false; - } - QString format = attributes.value(formatAttribute()); - TypeSystem::Language lang = languageFromAttribute(format); - if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(format); - return false; - } - - QString signature = m_current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature; - DocModification mod(mode, signature); - mod.setFormat(lang); - m_contextStack.top()->docModifications << mod; - } else { - m_error = QLatin1String("inject-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + if (!parseInjectDocumentation(reader, &attributes)) return false; - } } else if (element->type == StackElement::ModifyDocumentation) { - // check the XML tag attributes - QHash attributes; - attributes.insert(xPathAttribute(), QString()); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - fetchAttributeValues(tagName, atts, &attributes); - - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (m_current->parent && m_current->parent->type & validParent) { - QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; - m_contextStack.top()->docModifications - << DocModification(attributes.value(xPathAttribute()), signature); - } else { - m_error = QLatin1String("modify-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + if (!parseModifyDocumentation(reader, &attributes)) return false; - } } else if (element->type != StackElement::None) { bool topLevel = element->type == StackElement::Root || element->type == StackElement::SuppressedWarning @@ -1352,470 +2422,95 @@ bool Handler::startElement(const QStringRef &tagName, const QXmlStreamAttributes || element->type == StackElement::AddFunction || element->type == StackElement::Template; - if (!topLevel && m_current->type == StackElement::Root) { - m_error = QStringLiteral("Tag requires parent: '%1'").arg(tagName); - return false; - } - - StackElement topElement = !m_current ? StackElement(0) : *m_current; - element->entry = topElement.entry; - - QHash attributes; - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - switch (element->type) { - case StackElement::Root: - attributes.insert(QLatin1String("package"), QString()); - attributes.insert(QLatin1String("default-superclass"), QString()); - break; - case StackElement::LoadTypesystem: - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("generate"), yesAttributeValue()); - break; - case StackElement::NoNullPointers: - attributes.insert(QLatin1String("default-value"), QString()); - break; - case StackElement::SuppressedWarning: - attributes.insert(textAttribute(), QString()); - break; - case StackElement::ReplaceDefaultExpression: - attributes.insert(QLatin1String("with"), QString()); - break; - case StackElement::DefineOwnership: - attributes.insert(QLatin1String("class"), QLatin1String("target")); - attributes.insert(ownershipAttribute(), QString()); - break; - case StackElement::AddFunction: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("return-type"), QLatin1String("void")); - attributes.insert(accessAttribute(), QLatin1String("public")); - attributes.insert(QLatin1String("static"), noAttributeValue()); - break; - case StackElement::ModifyFunction: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(accessAttribute(), QString()); - attributes.insert(QLatin1String("remove"), QString()); - attributes.insert(QLatin1String("rename"), QString()); - attributes.insert(QLatin1String("deprecated"), noAttributeValue()); - attributes.insert(QLatin1String("associated-to"), QString()); - attributes.insert(QLatin1String("virtual-slot"), noAttributeValue()); - attributes.insert(QLatin1String("thread"), noAttributeValue()); - attributes.insert(QLatin1String("allow-thread"), noAttributeValue()); - break; - case StackElement::ModifyArgument: - attributes.insert(indexAttribute(), QString()); - attributes.insert(QLatin1String("replace-value"), QString()); - attributes.insert(QLatin1String("invalidate-after-use"), noAttributeValue()); - break; - case StackElement::ModifyField: - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("write"), trueAttributeValue()); - attributes.insert(QLatin1String("read"), trueAttributeValue()); - attributes.insert(QLatin1String("remove"), QString()); - break; - case StackElement::Access: - attributes.insert(modifierAttribute(), QString()); - break; - case StackElement::Include: - attributes.insert(QLatin1String("file-name"), QString()); - attributes.insert(locationAttribute(), QString()); - break; - case StackElement::CustomMetaConstructor: - attributes[nameAttribute()] = topElement.entry->name().toLower() + QLatin1String("_create"); - attributes.insert(QLatin1String("param-name"), QLatin1String("copy")); - break; - case StackElement::CustomMetaDestructor: - attributes[nameAttribute()] = topElement.entry->name().toLower() + QLatin1String("_delete"); - attributes.insert(QLatin1String("param-name"), QLatin1String("copy")); - break; - case StackElement::ReplaceType: - attributes.insert(QLatin1String("modified-type"), QString()); - break; - case StackElement::InjectCode: - attributes.insert(QLatin1String("class"), QLatin1String("target")); - attributes.insert(positionAttribute(), QLatin1String("beginning")); - attributes.insert(QLatin1String("file"), QString()); - break; - case StackElement::ConversionRule: - attributes.insert(QLatin1String("class"), QString()); - attributes.insert(QLatin1String("file"), QString()); - break; - case StackElement::TargetToNative: - attributes.insert(QLatin1String("replace"), yesAttributeValue()); - break; - case StackElement::AddConversion: - attributes.insert(QLatin1String("type"), QString()); - attributes.insert(QLatin1String("check"), QString()); - break; - case StackElement::RejectEnumValue: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::ArgumentMap: - attributes.insert(indexAttribute(), QLatin1String("1")); - attributes.insert(QLatin1String("meta-name"), QString()); - break; - case StackElement::Rename: - attributes.insert(QLatin1String("to"), QString()); - break; - case StackElement::Rejection: - attributes.insert(classAttribute(), QString()); - attributes.insert(functionNameAttribute(), QString()); - attributes.insert(fieldNameAttribute(), QString()); - attributes.insert(enumNameAttribute(), QString()); - attributes.insert(argumentTypeAttribute(), QString()); - attributes.insert(returnTypeAttribute(), QString()); - break; - case StackElement::Removal: - attributes.insert(QLatin1String("class"), QLatin1String("all")); - break; - case StackElement::Template: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::TemplateInstanceEnum: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::Replace: - attributes.insert(QLatin1String("from"), QString()); - attributes.insert(QLatin1String("to"), QString()); - break; - case StackElement::ReferenceCount: - attributes.insert(actionAttribute(), QString()); - attributes.insert(QLatin1String("variable-name"), QString()); - break; - case StackElement::ParentOwner: - attributes.insert(indexAttribute(), QString()); - attributes.insert(QLatin1String("action"), QString()); - break; - case StackElement::Array: - break; - default: - { }; - }; - - if (!attributes.isEmpty()) - fetchAttributeValues(tagName, atts, &attributes); - - switch (element->type) { - case StackElement::Root: - m_defaultPackage = attributes[QLatin1String("package")]; - m_defaultSuperclass = attributes[QLatin1String("default-superclass")]; - element->type = StackElement::Root; - { - TypeSystemTypeEntry* moduleEntry = reinterpret_cast( - m_database->findType(m_defaultPackage)); - element->entry = moduleEntry ? moduleEntry : new TypeSystemTypeEntry(m_defaultPackage, since); - element->entry->setCodeGeneration(m_generate); - } - - if ((m_generate == TypeEntry::GenerateForSubclass || - m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) - TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); - - if (!element->entry->qualifiedCppName().isEmpty()) - m_database->addType(element->entry); - break; - case StackElement::LoadTypesystem: { - QString name = attributes[nameAttribute()]; - if (name.isEmpty()) { - m_error = QLatin1String("No typesystem name specified"); - return false; - } - bool generateChild = (convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true) && (m_generate == TypeEntry::GenerateAll)); - if (!m_database->parseFile(name, m_currentPath, generateChild)) { - m_error = QStringLiteral("Failed to parse: '%1'").arg(name); - return false; - } - } - break; - case StackElement::RejectEnumValue: - if (!m_currentEnum) { - m_error = QLatin1String(" node must be used inside a node"); - return false; - } - break; - case StackElement::ReplaceType: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Type replacement can only be specified for argument modifications"); - return false; - } - - if (attributes[QLatin1String("modified-type")].isEmpty()) { - m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); - return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = attributes[QLatin1String("modified-type")]; - } - break; - case StackElement::ConversionRule: { - if (topElement.type != StackElement::ModifyArgument - && topElement.type != StackElement::ValueTypeEntry - && topElement.type != StackElement::PrimitiveTypeEntry - && topElement.type != StackElement::ContainerTypeEntry) { - m_error = QLatin1String("Conversion rules can only be specified for argument modification, " - "value-type, primitive-type or container-type conversion."); - return false; - } - - QString languageAttribute = attributes.value(classAttribute()); - TypeSystem::Language lang = languageFromAttribute(languageAttribute); - - if (topElement.type == StackElement::ModifyArgument) { - if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); - return false; - } - - CodeSnip snip; - snip.language = lang; - m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); - } else { - if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { - m_error = QLatin1String("Types can have only one conversion rule"); - return false; - } - - // The old conversion rule tag that uses a file containing the conversion - // will be kept temporarily for compatibility reasons. - QString sourceFile = attributes[QLatin1String("file")]; - if (!sourceFile.isEmpty()) { - if (m_generate != TypeEntry::GenerateForSubclass - && m_generate != TypeEntry::GenerateNothing) { - - const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; - if (lang == TypeSystem::TargetLangCode) - conversionFlag = TARGET_CONVERSION_RULE_FLAG; - - QFile conversionSource(sourceFile); - if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { - topElement.entry->setConversionRule(QLatin1String(conversionFlag) + QString::fromUtf8(conversionSource.readAll())); - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File containing conversion code for " - << topElement.entry->name() << " type does not exist or is not readable: " - << sourceFile; - } - } - } - - CustomConversion* customConversion = new CustomConversion(static_cast(m_current->entry)); - customConversionsForReview.append(customConversion); - } + if (!topLevel && m_current->type == StackElement::Root) { + m_error = QStringLiteral("Tag requires parent: '%1'").arg(tagName); + return false; } - break; - case StackElement::NativeToTarget: { + + StackElement topElement = !m_current ? StackElement(0) : *m_current; + element->entry = topElement.entry; + + switch (element->type) { + case StackElement::Root: + element->entry = parseRootElement(reader, since, &attributes); + element->type = StackElement::Root; + 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 (!Handler::parseCustomConversion(reader, topElement, &attributes)) + return false; + break; + case StackElement::NativeToTarget: if (topElement.type != StackElement::ConversionRule) { m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); return false; } m_contextStack.top()->codeSnips << CodeSnip(); - } - break; + break; case StackElement::TargetToNative: { if (topElement.type != StackElement::ConversionRule) { m_error = QLatin1String("Target to Native conversions can only be specified for custom conversion rules."); return false; } - bool replace = attributes[QLatin1String("replace")] == yesAttributeValue(); - static_cast(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); - } - break; - case StackElement::AddConversion: { - if (topElement.type != StackElement::TargetToNative) { - m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); - return false; - } - QString sourceTypeName = attributes[QLatin1String("type")]; - if (sourceTypeName.isEmpty()) { - m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); - return false; - } - QString typeCheck = attributes[QLatin1String("check")]; - static_cast(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); - m_contextStack.top()->codeSnips << CodeSnip(); - } - break; - case StackElement::ModifyArgument: { - if (topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::AddFunction) { - m_error = QString::fromLatin1("argument modification requires function" - " modification as parent, was %1") - .arg(topElement.type, 0, 16); - return false; - } - - QString index = attributes.value(indexAttribute()); - if (index == QLatin1String("return")) - index = QLatin1String("0"); - else if (index == QLatin1String("this")) - index = QLatin1String("-1"); - - bool ok = false; - int idx = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Cannot convert '%1' to integer").arg(index); - return false; - } - - QString replace_value = attributes[QLatin1String("replace-value")]; - - if (!replace_value.isEmpty() && idx) { - m_error = QLatin1String("replace-value is only supported for return values (index=0)."); - return false; - } - - ArgumentModification argumentModification = ArgumentModification(idx); - argumentModification.replace_value = replace_value; - argumentModification.resetAfterUse = convertBoolean(attributes[QLatin1String("invalidate-after-use")], QLatin1String("invalidate-after-use"), false); - m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); + const int replaceIndex = indexOfAttribute(attributes, replaceAttribute()); + const bool replace = replaceIndex == -1 + || convertBoolean(attributes.takeAt(replaceIndex).value(), + replaceAttribute(), true); + m_current->entry->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); } break; - case StackElement::NoNullPointers: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + case StackElement::AddConversion: + if (!parseAddConversion(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().noNullPointers = true; - if (!m_contextStack.top()->functionMods.last().argument_mods.last().index) - m_contextStack.top()->functionMods.last().argument_mods.last().nullPointerDefaultValue = attributes[QLatin1String("default-value")]; - else if (!attributes[QLatin1String("default-value")].isEmpty()) - qCWarning(lcShiboken) << "default values for null pointer guards are only effective for return values"; - - } - break; - case StackElement::DefineOwnership: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("define-ownership requires argument modification as parent"); + break; + case StackElement::ModifyArgument: + if (!parseModifyArgument(reader, topElement, &attributes)) return false; - } - - QString classAttribute = attributes.value(::classAttribute()); - TypeSystem::Language lang = languageFromAttribute(classAttribute); - if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(classAttribute); + break; + case StackElement::NoNullPointers: + if (!parseNoNullPointer(reader, topElement, &attributes)) return false; - } - - QString ownershipAttribute = attributes.value(::ownershipAttribute()); - TypeSystem::Ownership owner = ownershipFromFromAttribute(ownershipAttribute); - if (owner == TypeSystem::InvalidOwnership) { - m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownershipAttribute); + break; + case StackElement::DefineOwnership: + if (!parseDefineOwnership(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; - } - break; + break; case StackElement::SuppressedWarning: { - const QString suppressedWarning = attributes.value(textAttribute()); - if (suppressedWarning.isEmpty()) { + const int 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_database->addSuppressedWarning(suppressedWarning, &m_error)) return false; } } break; - case StackElement::ArgumentMap: { - if (!(topElement.type & StackElement::CodeSnipMask)) { - m_error = QLatin1String("Argument maps requires code injection as parent"); - return false; - } - - bool ok; - const QString index = attributes.value(indexAttribute()); - int pos = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Can't convert position '%1' to integer") - .arg(index); - return false; - } - - if (pos <= 0) { - m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); - return false; - } - - QString meta_name = attributes[QLatin1String("meta-name")]; - if (meta_name.isEmpty()) - qCWarning(lcShiboken) << "Empty meta name in argument map"; - - - if (topElement.type == StackElement::InjectCodeInFunction) - m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = meta_name; - else { - qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " - "into functions."; - } - } - break; - case StackElement::Removal: { - if (topElement.type != StackElement::ModifyFunction) { - m_error = QLatin1String("Function modification parent required"); + case StackElement::ArgumentMap: + if (!parseArgumentMap(reader, topElement, &attributes)) return false; - } - - QString languageAttribute = attributes.value(classAttribute()); - TypeSystem::Language lang = languageFromAttribute(languageAttribute); - if (lang == TypeSystem::TargetLangCode) // "target" means TargetLangAndNativeCode here - lang = TypeSystem::TargetLangAndNativeCode; - if (lang != TypeSystem::TargetLangAndNativeCode && lang != TypeSystem::All) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); + break; + case StackElement::Removal: + if (!parseRemoval(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().removal = lang; - } - break; + break; case StackElement::Rename: - case StackElement::Access: { - if (topElement.type != StackElement::ModifyField - && topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Function, field or argument modification parent required"); - return false; - } - - Modification *mod = 0; - if (topElement.type == StackElement::ModifyFunction) - mod = &m_contextStack.top()->functionMods.last(); - else if (topElement.type == StackElement::ModifyField) - mod = &m_contextStack.top()->fieldMods.last(); - - QString modifier; - if (element->type == StackElement::Rename) { - modifier = QLatin1String("rename"); - QString renamed_to = attributes[QLatin1String("to")]; - if (renamed_to.isEmpty()) { - m_error = QLatin1String("Rename modifier requires 'to' attribute"); - return false; - } - - if (topElement.type == StackElement::ModifyFunction) - mod->setRenamedTo(renamed_to); - else if (topElement.type == StackElement::ModifyField) - mod->setRenamedTo(renamed_to); - else - m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to; - } else { - modifier = attributes.value(modifierAttribute()); - } - - if (modifier.isEmpty()) { - m_error = QLatin1String("No access modification specified"); - return false; - } - - const Modification::Modifiers modifierFlag = modifierFromAttribute(modifier); - if (modifierFlag == Modification::InvalidModifier) { - m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); + case StackElement::Access: + if (!parseRename(reader, element->type, topElement, &attributes)) return false; - } - - if (mod) - mod->modifiers |= modifierFlag; - } - break; + break; case StackElement::RemoveArgument: if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("Removing argument requires argument modification as parent"); @@ -1825,206 +2520,38 @@ bool Handler::startElement(const QStringRef &tagName, const QXmlStreamAttributes m_contextStack.top()->functionMods.last().argument_mods.last().removed = true; break; - case StackElement::ModifyField: { - QString name = attributes[nameAttribute()]; - if (name.isEmpty()) - break; - FieldModification fm; - fm.name = name; - fm.modifiers = 0; - - if (!convertRemovalAttribute(attributes[QLatin1String("remove")], fm, m_error)) - return false; - - QString read = attributes[QLatin1String("read")]; - QString write = attributes[QLatin1String("write")]; - - if (read == trueAttributeValue()) fm.modifiers |= FieldModification::Readable; - if (write == trueAttributeValue()) fm.modifiers |= FieldModification::Writable; - - m_contextStack.top()->fieldMods << fm; - } - break; - case StackElement::AddFunction: { - if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) { - m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; - } - const QString originalSignature = attributes[QLatin1String("signature")]; - - QString signature = TypeDatabase::normalizedSignature(originalSignature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for the added function"); - return false; - } - - QString errorString = checkSignatureError(signature, QLatin1String("add-function")); - if (!errorString.isEmpty()) { - m_error = errorString; - return false; - } - - AddedFunction func(signature, attributes[QLatin1String("return-type")]); - func.setStatic(attributes[QLatin1String("static")] == yesAttributeValue()); - if (!signature.contains(QLatin1Char('('))) - signature += QLatin1String("()"); - m_currentSignature = signature; - - QString access = attributes.value(accessAttribute()); - if (!access.isEmpty()) { - const AddedFunction::Access a = addedFunctionAccessFromAttribute(access); - if (a == AddedFunction::InvalidAccess) { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - func.setAccess(a); - } - - m_contextStack.top()->addedFunctions << func; - - FunctionModification mod; - if (!mod.setSignature(m_currentSignature, &m_error)) - return false; - mod.setOriginalSignature(originalSignature); - m_contextStack.top()->functionMods << mod; - } - break; - case StackElement::ModifyFunction: { - if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { - m_error = QString::fromLatin1("Modify function requires complex type as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; - } - const QString originalSignature = attributes[QLatin1String("signature")]; - - const QString signature = TypeDatabase::normalizedSignature(originalSignature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for modified function"); - return false; - } - - QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); - if (!errorString.isEmpty()) { - m_error = errorString; + case StackElement::ModifyField: + if (!parseModifyField(reader, &attributes)) return false; - } - - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) + break; + case StackElement::AddFunction: + if (!parseAddFunction(reader, topElement, &attributes)) return false; - mod.setOriginalSignature(originalSignature); - m_currentSignature = signature; - - QString access = attributes.value(accessAttribute()); - if (!access.isEmpty()) { - const Modification::Modifiers m = modifierFromAttribute(access); - if ((m & (Modification::AccessModifierMask | Modification::FinalMask)) == 0) { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - mod.modifiers |= m; - } - - if (convertBoolean(attributes[QLatin1String("deprecated")], QLatin1String("deprecated"), false)) - mod.modifiers |= Modification::Deprecated; - - if (!convertRemovalAttribute(attributes[QLatin1String("remove")], mod, m_error)) + break; + case StackElement::ModifyFunction: + if (!parseModifyFunction(reader, topElement, &attributes)) return false; - - QString rename = attributes[QLatin1String("rename")]; - if (!rename.isEmpty()) { - mod.renamedToName = rename; - mod.modifiers |= Modification::Rename; - } - - QString association = attributes[QLatin1String("associated-to")]; - if (!association.isEmpty()) - mod.association = association; - - mod.setIsThread(convertBoolean(attributes[QLatin1String("thread")], QLatin1String("thread"), false)); - mod.setAllowThread(convertBoolean(attributes[QLatin1String("allow-thread")], QLatin1String("allow-thread"), false)); - - mod.modifiers |= (convertBoolean(attributes[QLatin1String("virtual-slot")], QLatin1String("virtual-slot"), false) ? Modification::VirtualSlot : 0); - - m_contextStack.top()->functionMods << mod; - } - break; + break; case StackElement::ReplaceDefaultExpression: - if (!(topElement.type & StackElement::ModifyArgument)) { - m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); - return false; - } - - if (attributes[QLatin1String("with")].isEmpty()) { - m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + if (!parseReplaceDefaultExpression(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = attributes[QLatin1String("with")]; break; case StackElement::RemoveDefaultExpression: m_contextStack.top()->functionMods.last().argument_mods.last().removedDefaultExpression = true; break; case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: { - CustomFunction *func = new CustomFunction(attributes[nameAttribute()]); - func->paramName = attributes[QLatin1String("param-name")]; - element->value.customFunction = func; - } - break; - case StackElement::ReferenceCount: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("reference-count must be child of modify-argument"); - return false; - } - - ReferenceCount rc; - const QString action = attributes.value(actionAttribute()); - rc.action = referenceCountFromAttribute(action); - rc.varName = attributes[QLatin1String("variable-name")]; - - if (rc.action == ReferenceCount::Invalid) { - m_error = QLatin1String("unrecognized value '") + action - + QLatin1String("' for action attribute."); - return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); - } - break; - - case StackElement::ParentOwner: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("parent-policy must be child of modify-argument"); - return false; - } - - ArgumentOwner ao; - - QString index = attributes.value(indexAttribute()); - if (index == QLatin1String("return")) - index = QLatin1String("0"); - else if (index == QLatin1String("this")) - index = QLatin1String("-1"); - - bool ok = false; - int idx = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Cannot convert '%1' to integer").arg(index); + case StackElement::CustomMetaDestructor: + element->value.customFunction = + parseCustomMetaConstructor(reader, element->type, topElement, &attributes); + break; + case StackElement::ReferenceCount: + if (!parseReferenceCount(reader, topElement, &attributes)) return false; - } - - const QString action = attributes.value(actionAttribute()); - ao.action = argumentOwnerActionFromAttribute(action); - if (ao.action == ArgumentOwner::Invalid) { - m_error = QLatin1String("Invalid parent actionr '") + action + QLatin1String("'."); + break; + case StackElement::ParentOwner: + if (!parseParentOwner(reader, topElement, &attributes)) return false; - } - ao.index = idx; - m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; - } - break; + break; case StackElement::Array: if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("array must be child of modify-argument"); @@ -2032,147 +2559,48 @@ bool Handler::startElement(const QStringRef &tagName, const QXmlStreamAttributes } m_contextStack.top()->functionMods.last().argument_mods.last().array = true; break; - case StackElement::InjectCode: { - if (!(topElement.type & StackElement::ComplexTypeEntryMask) - && (topElement.type != StackElement::AddFunction) - && (topElement.type != StackElement::ModifyFunction) - && (topElement.type != StackElement::Root)) { - m_error = QLatin1String("wrong parent type for code injection"); - return false; - } - - const QString className = attributes.value(classAttribute()); - const TypeSystem::Language lang = languageFromAttribute(className); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); - return false; - } - - const QString position = attributes.value(positionAttribute()); - CodeSnip snip; - snip.position = codeSnipPositionFromAttribute(position); - if (snip.position == TypeSystem::CodeSnipPositionInvalid) { - m_error = QStringLiteral("Invalid position: '%1'").arg(position); + case StackElement::InjectCode: + if (!parseInjectCode(reader, topElement, element, &attributes)) return false; - } - - snip.language = lang; - bool in_file = false; - - QString file_name = attributes[QLatin1String("file")]; - - //Handler constructor.... - if (m_generate != TypeEntry::GenerateForSubclass && - m_generate != TypeEntry::GenerateNothing && - !file_name.isEmpty()) { - const QString resolved = m_database->modifiedTypesystemFilepath(file_name, m_currentPath); - if (QFile::exists(resolved)) { - QFile codeFile(resolved); - if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { - QString content = QLatin1String("// ========================================================================\n" - "// START of custom code block [file: "); - content += file_name; - content += QLatin1String("]\n"); - content += QString::fromUtf8(codeFile.readAll()); - content += QLatin1String("\n// END of custom code block [file: "); - content += file_name; - content += QLatin1String("]\n// ========================================================================\n"); - snip.addCode(content); - in_file = true; - } - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File for inject code not exist: " << QDir::toNativeSeparators(file_name); - } - - } - - if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) { - m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); + break; + case StackElement::Include: + if (!parseInclude(reader, topElement, element->entry, &attributes)) return false; - } - - if (topElement.type == StackElement::ModifyFunction || topElement.type == StackElement::AddFunction) { - FunctionModification mod = m_contextStack.top()->functionMods.constLast(); - if (snip.language == TypeSystem::ShellDeclaration) { - m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); - return false; - } - - m_contextStack.top()->functionMods.last().snips << snip; - if (in_file) - m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection; - element->type = StackElement::InjectCodeInFunction; - } else if (topElement.type == StackElement::Root) { - element->entry->addCodeSnip(snip); - } else if (topElement.type != StackElement::Root) { - m_contextStack.top()->codeSnips << snip; - } - - } - break; - case StackElement::Include: { - const QString location = attributes.value(locationAttribute()); - const Include::IncludeType loc = locationFromAttribute(location); - if (loc == Include::InvalidInclude) { - m_error = QStringLiteral("Location not recognized: '%1'").arg(location); + break; + case StackElement::Rejection: + if (!addRejection(m_database, &attributes, &m_error)) return false; - } - - Include inc(loc, attributes[QLatin1String("file-name")]); - - ComplexTypeEntry *ctype = static_cast(element->entry); - if (topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { - element->entry->setInclude(inc); - } else if (topElement.type == StackElement::ExtraIncludes) { - element->entry->addExtraInclude(inc); - } else { - m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + break; + case StackElement::Template: { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); return false; } - - inc = ctype->include(); - IncludeList lst = ctype->extraIncludes(); - ctype = ctype->designatedInterface(); - if (ctype) { - ctype->setExtraIncludes(lst); - ctype->setInclude(inc); - } + element->value.templateEntry = + new TemplateEntry(attributes.takeAt(nameIndex).value().toString()); } - break; - case StackElement::Rejection: - if (!addRejection(m_database, attributes, &m_error)) - return false; - break; - case StackElement::Template: - element->value.templateEntry = new TemplateEntry(attributes.value(nameAttribute())); break; case StackElement::TemplateInstanceEnum: - if (!(topElement.type & StackElement::CodeSnipMask) && - (topElement.type != StackElement::Template) && - (topElement.type != StackElement::CustomMetaConstructor) && - (topElement.type != StackElement::CustomMetaDestructor) && - (topElement.type != StackElement::NativeToTarget) && - (topElement.type != StackElement::AddConversion) && - (topElement.type != StackElement::ConversionRule)) { - m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ - "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); + element->value.templateInstance = + parseTemplateInstanceEnum(reader, topElement, &attributes); + if (!element->value.templateInstance) return false; - } - element->value.templateInstance = new TemplateInstance(attributes.value(nameAttribute())); break; case StackElement::Replace: - if (topElement.type != StackElement::TemplateInstanceEnum) { - m_error = QLatin1String("Can only insert replace rules into insert-template."); + if (!parseReplace(reader, topElement, element, &attributes)) return false; - } - element->parent->value.templateInstance->addReplaceRule(attributes[QLatin1String("from")], attributes[QLatin1String("to")]); break; default: break; // nada }; } + if (!attributes.isEmpty()) { + const QString message = msgUnusedAttributes(tagName, attributes); + qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message))); + } + m_current = element; return true; } -- cgit v1.2.3