From 51cb9304647fed901f3a19849d6758a757ce2d62 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 5 May 2017 15:25:16 +0200 Subject: Generate #error when a minimal constructor could not be found The occurred in 3 places, one of which generated #error and the others used qFatal(). Change it to always generate #error with a bit of context to make it possible to detect the source of of the problem. Change-Id: Icd93b1beec908b57fa72457d6ec1e16a15e5ff14 Reviewed-by: Christian Tismer --- generator/shiboken2/cppgenerator.cpp | 6 +++++- generator/shiboken2/shibokengenerator.cpp | 27 +++++++++++++++++++++------ generator/shiboken2/shibokengenerator.h | 4 ++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/generator/shiboken2/cppgenerator.cpp b/generator/shiboken2/cppgenerator.cpp index b6da137..68b6247 100644 --- a/generator/shiboken2/cppgenerator.cpp +++ b/generator/shiboken2/cppgenerator.cpp @@ -688,7 +688,11 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (defaultReturnExpr.isEmpty()) defaultReturnExpr = minimalConstructor(func->type()); if (defaultReturnExpr.isEmpty()) { - QString errorMsg = QString::fromLatin1(MIN_CTOR_ERROR_MSG).arg(func->type()->cppSignature()); + QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); + if (const AbstractMetaClass *c = func->implementingClass()) + errorMsg += c->qualifiedCppName() + QLatin1String("::"); + errorMsg += func->signature(); + errorMsg = ShibokenGenerator::msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); qCWarning(lcShiboken).noquote().nospace() << errorMsg; s << endl << INDENT << "#error " << errorMsg << endl; } diff --git a/generator/shiboken2/shibokengenerator.cpp b/generator/shiboken2/shibokengenerator.cpp index dadc46e..7b664e1 100644 --- a/generator/shiboken2/shibokengenerator.cpp +++ b/generator/shiboken2/shibokengenerator.cpp @@ -2626,9 +2626,13 @@ void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const if (defaultCtor.isEmpty() && isCppPrimitive(type)) return; QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; - if (ctor.isEmpty()) - qFatal(qPrintable(QString::fromLatin1(MIN_CTOR_ERROR_MSG).arg(type->cppSignature())), NULL); - s << " = " << ctor; + if (ctor.isEmpty()) { + const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->cppSignature()); + qCWarning(lcShiboken()).noquote() << message; + s << ";\n#error " << message << '\n'; + } else { + s << " = " << ctor; + } } void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor) @@ -2636,9 +2640,14 @@ void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const if (defaultCtor.isEmpty() && isCppPrimitive(type)) return; QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; - if (ctor.isEmpty()) - qFatal(qPrintable(QString::fromLatin1(MIN_CTOR_ERROR_MSG).arg(type->qualifiedCppName())), NULL); - s << " = " << ctor; + + if (ctor.isEmpty()) { + const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName()); + qCWarning(lcShiboken()).noquote() << message; + s << ";\n#error " << message << endl; + } else { + s << " = " << ctor; + } } bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry* type) @@ -2657,3 +2666,9 @@ bool ShibokenGenerator::isCppIntegralPrimitive(const AbstractMetaType* type) { return isCppIntegralPrimitive(type->typeEntry()); } + +QString ShibokenGenerator::msgCouldNotFindMinimalConstructor(const QString &where, const QString &type) +{ + return where + QLatin1String(": Could not find a minimal constructor for type '") + type + + QLatin1String("'. This will result in a compilation error."); +} diff --git a/generator/shiboken2/shibokengenerator.h b/generator/shiboken2/shibokengenerator.h index 2a75a21..837e7d6 100644 --- a/generator/shiboken2/shibokengenerator.h +++ b/generator/shiboken2/shibokengenerator.h @@ -43,8 +43,6 @@ #define THREAD_STATE_SAVER_VAR "threadStateSaver" #define BEGIN_ALLOW_THREADS "PyThreadState* _save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS" #define END_ALLOW_THREADS "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS" -#define MIN_CTOR_ERROR_MSG "Could not find a minimal constructor for type '%1'. "\ - "This will result in a compilation error." #define PYTHON_TO_CPP_VAR "pythonToCpp" #define SMART_POINTER_GETTER "kSmartPointerGetter" @@ -535,6 +533,8 @@ protected: }; void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code); + static QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type); + private: bool m_useCtorHeuristic; bool m_userReturnValueHeuristic; -- cgit v1.2.3 From 35d5adf8ad14e7d62a1359ce4b8e198e33152ad9 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 17 May 2017 10:29:03 +0200 Subject: Improve Clang detection Check LLVM_INSTALL_DIR, CLANG_INSTALL_DIR and llvm-config and make messages more verbose. Task-number: PYSIDE-323 Change-Id: Iaff24d7c80853f381d227ebdccbb3abc4497a199 Reviewed-by: Christian Tismer --- CMakeLists.txt | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7695df5..ddba62d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,11 +27,30 @@ else() find_package(PythonLibs 2.6) endif() -set(CLANG_DIR $ENV{CLANG_INSTALL_DIR}) +set(CLANG_DIR "") +set(CLANG_DIR_SOURCE "") + +if (DEFINED ENV{LLVM_INSTALL_DIR}) + set(CLANG_DIR $ENV{LLVM_INSTALL_DIR}) + set(CLANG_DIR_SOURCE "LLVM_INSTALL_DIR") +elseif (DEFINED ENV{CLANG_INSTALL_DIR}) + set(CLANG_DIR $ENV{CLANG_INSTALL_DIR}) + set(CLANG_DIR_SOURCE "CLANG_INSTALL_DIR") +else () + EXEC_PROGRAM("llvm-config" ARGS "--prefix" OUTPUT_VARIABLE CLANG_DIR) + set(CLANG_DIR_SOURCE "llvm-config") + if (NOT "${CLANG_DIR}" STREQUAL "") + EXEC_PROGRAM("llvm-config" ARGS "--version" OUTPUT_VARIABLE CLANG_VERSION) + if (CLANG_VERSION VERSION_LESS 3.9) + message(FATAL_ERROR "LLVM version 3.9 is required (llvm-config detected ${CLANG_VERSION} at ${CLANG_DIR}).") + endif() + endif() +endif() -if (NOT IS_DIRECTORY ${CLANG_DIR}) - message(FATAL_ERROR "CLANG_INSTALL_DIR is not set or does not point to a valid directory.") -else() +if ("${CLANG_DIR}" STREQUAL "") + message(FATAL_ERROR "Unable to detect CLANG location by checking LLVM_INSTALL_DIR, CLANG_INSTALL_DIR or running llvm-config.") +elseif (NOT IS_DIRECTORY ${CLANG_DIR}) + message(FATAL_ERROR "${CLANG_DIR} detected by ${CLANG_DIR_SOURCE} does not exist.") endif() set(CLANG_LIB_NAME "clang") @@ -44,7 +63,7 @@ if (NOT EXISTS ${CLANG_LIBRARY}) message(FATAL_ERROR "Unable to find Clang library ${CLANG_LIB_NAME} in ${CLANG_DIR}.") endif() -message(STATUS "CLANG: ${CLANG_DIR}, ${CLANG_LIBRARY}") +message(STATUS "CLANG: ${CLANG_DIR}, ${CLANG_LIBRARY} detected by ${CLANG_DIR_SOURCE}") set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include) set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY}) -- cgit v1.2.3 From 467095ad1ac08faf225e1bc8932bec791ce27ef6 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 12 May 2017 14:34:26 +0200 Subject: Improve rejection messages In AbstractMetaBuilderPrivate::traverseFunction(), store the unmodified signature with return type before resolving typedefs for the messages. Extend the error messages about parameters and output them to qWarning() and rejection logs consistently. So: Unmatched argument type - QOpenGLExtraFunctions::glIsSync(GLsync) -> GLboolean becomes: Unmatched argument type - GLboolean QOpenGLExtraFunctions::glIsSync(GLsync): unmatched type 'GLsync' in parameter #1 "sync" Task-number: PYSIDE-516 Change-Id: Ifd680af1cd6d403d9b73ba3e40d06abe274cf359 Reviewed-by: Alexandru Croitor --- ApiExtractor/abstractmetabuilder.cpp | 67 +++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/ApiExtractor/abstractmetabuilder.cpp b/ApiExtractor/abstractmetabuilder.cpp index 31c78ce..ef6a480 100644 --- a/ApiExtractor/abstractmetabuilder.cpp +++ b/ApiExtractor/abstractmetabuilder.cpp @@ -2038,16 +2038,35 @@ static QString functionSignature(FunctionModelItem functionItem) return functionItem->name() + QLatin1Char('(') + args.join(QLatin1Char(',')) + QLatin1Char(')'); } -static inline QString functionSignatureWithReturnType(FunctionModelItem functionItem) +static inline QString qualifiedFunctionSignatureWithType(const FunctionModelItem &functionItem, + const QString &className = QString()) { - return functionSignature(functionItem) - + QStringLiteral(" -> ") + functionItem->type().toString(); + QString result = functionItem->type().toString() + QLatin1Char(' '); + if (!className.isEmpty()) + result += className + colonColon(); + result += functionSignature(functionItem); + return result; } -static inline QString qualifiedFunctionSignatureWithType(const QString &className, - FunctionModelItem functionItem) +static inline QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n) { - return className + colonColon() + functionSignatureWithReturnType(functionItem); + QString result; + QTextStream str(&result); + str << "unmatched type '" << arg->type().toString() << "' in parameter #" + << (n + 1); + if (!arg->name().isEmpty()) + str << " \"" << arg->name() << '"'; + return result; +} + +static inline QString msgVoidParameterType(const ArgumentModelItem &arg, int n) +{ + QString result; + QTextStream str(&result); + str << "'void' encountered at parameter #" << (n + 1); + if (!arg->name().isEmpty()) + str << " \"" << arg->name() << '"'; + return result; } AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModelItem functionItem) @@ -2056,7 +2075,6 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel return nullptr; QString functionName = functionItem->name(); QString className; - QString rejectedFunctionSignature; if (m_currentClass) { // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT // and overridden metaObject(), QGADGET helpers @@ -2069,15 +2087,17 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel return nullptr; } + // Store original signature with unresolved typedefs for message/log purposes + const QString originalQualifiedSignatureWithReturn = + qualifiedFunctionSignatureWithType(functionItem, className); + if (TypeDatabase::instance()->isFunctionRejected(className, functionName)) { - rejectedFunctionSignature = qualifiedFunctionSignatureWithType(className, functionItem); - m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::GenerationDisabled); + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::GenerationDisabled); return 0; } else if (TypeDatabase::instance()->isFunctionRejected(className, functionSignature(functionItem))) { - rejectedFunctionSignature = qualifiedFunctionSignatureWithType(className, functionItem); - m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::GenerationDisabled); + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::GenerationDisabled); return 0; } @@ -2144,11 +2164,10 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel if (!ok) { Q_ASSERT(type == 0); qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("skipping function '%1::%2', unmatched return type '%3'") - .arg(className, functionItem->name(), + << QStringLiteral("skipping function '%1', unmatched return type '%2'") + .arg(originalQualifiedSignatureWithReturn, functionItem->type().toString()); - rejectedFunctionSignature = qualifiedFunctionSignatureWithType(className, functionItem); - m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::UnmatchedReturnType); + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::UnmatchedReturnType); metaFunction->setInvalid(true); return metaFunction; } @@ -2179,22 +2198,24 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel AbstractMetaType* metaType = translateType(arg->type(), &ok); if (!ok) { Q_ASSERT(metaType == 0); + const QString reason = msgUnmatchedParameterType(arg, i); qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("skipping function '%1::%2', unmatched parameter type '%3'") - .arg(className, functionItem->name(), arg->type().toString()); - rejectedFunctionSignature = qualifiedFunctionSignatureWithType(className, functionItem); + << QStringLiteral("skipping function '%1', %2") + .arg(originalQualifiedSignatureWithReturn, reason); + const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn + + QLatin1String(": ") + reason; m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::UnmatchedArgumentType); metaFunction->setInvalid(true); return metaFunction; } if (metaType == Q_NULLPTR) { + const QString reason = msgVoidParameterType(arg, i); qCWarning(lcShiboken).noquote().nospace() - << QString::fromLatin1("skipping function '%1::%2', 'void' encountered at parameter " - "position %3, but it can only be the the first and only " - "parameter") - .arg(className, functionItem->name()).arg(i); - rejectedFunctionSignature = qualifiedFunctionSignatureWithType(className, functionItem); + << QString::fromLatin1("skipping function '%1': %2") + .arg(originalQualifiedSignatureWithReturn, reason); + const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn + + QLatin1String(": ") + reason; m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::UnmatchedArgumentType); metaFunction->setInvalid(true); return metaFunction; -- cgit v1.2.3 From f6bc1cb55603728a0b15fd9482cccc2af86539eb Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 12 May 2017 16:14:41 +0200 Subject: TypeRejection: Use QRegularExpression Refactor TypeRejection to use one regular expression for the class name and one for the various strings to be matched depending on the match type enumeration instead of 4 fixed string fields. Task-number: PYSIDE-516 Change-Id: Ifb945e3be39fbedfd802c4d32de7de443cf53f49 Reviewed-by: Alexandru Croitor --- ApiExtractor/abstractmetabuilder.cpp | 19 ++++---- ApiExtractor/typedatabase.cpp | 88 +++++++++++++++++++++++------------ ApiExtractor/typedatabase.h | 14 +++--- ApiExtractor/typesystem.cpp | 89 ++++++++++++++++++++++++++++++------ ApiExtractor/typesystem.h | 21 +++++++-- 5 files changed, 168 insertions(+), 63 deletions(-) diff --git a/ApiExtractor/abstractmetabuilder.cpp b/ApiExtractor/abstractmetabuilder.cpp index ef6a480..13e1461 100644 --- a/ApiExtractor/abstractmetabuilder.cpp +++ b/ApiExtractor/abstractmetabuilder.cpp @@ -1083,10 +1083,11 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte if (m_currentClass) className = m_currentClass->typeEntry()->qualifiedCppName(); - if (TypeDatabase::instance()->isEnumRejected(className, enumName)) { + QString rejectReason; + if (TypeDatabase::instance()->isEnumRejected(className, enumName, &rejectReason)) { if (typeEntry) typeEntry->setCodeGeneration(TypeEntry::GenerateNothing); - m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::GenerationDisabled); + m_rejectedEnums.insert(qualifiedName + rejectReason, AbstractMetaBuilder::GenerationDisabled); return 0; } @@ -1416,8 +1417,9 @@ AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(VariableModelItem f if (field->accessPolicy() == CodeModel::Private) return 0; - if (TypeDatabase::instance()->isFieldRejected(className, fieldName)) { - m_rejectedFields.insert(qualifiedFieldSignatureWithType(className, field), + QString rejectReason; + if (TypeDatabase::instance()->isFieldRejected(className, fieldName, &rejectReason)) { + m_rejectedFields.insert(qualifiedFieldSignatureWithType(className, field) + rejectReason, AbstractMetaBuilder::GenerationDisabled); return 0; } @@ -2091,13 +2093,14 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel const QString originalQualifiedSignatureWithReturn = qualifiedFunctionSignatureWithType(functionItem, className); - if (TypeDatabase::instance()->isFunctionRejected(className, functionName)) { - m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::GenerationDisabled); + QString rejectReason; + if (TypeDatabase::instance()->isFunctionRejected(className, functionName, &rejectReason)) { + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); return 0; } else if (TypeDatabase::instance()->isFunctionRejected(className, - functionSignature(functionItem))) { - m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::GenerationDisabled); + functionSignature(functionItem), &rejectReason)) { + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); return 0; } diff --git a/ApiExtractor/typedatabase.cpp b/ApiExtractor/typedatabase.cpp index 8530d15..b397f19 100644 --- a/ApiExtractor/typedatabase.cpp +++ b/ApiExtractor/typedatabase.cpp @@ -31,6 +31,7 @@ #include "typesystem_p.h" #include +#include #include #include #include @@ -226,65 +227,92 @@ ContainerTypeEntryList TypeDatabase::containerTypes() const } return returned; } -void TypeDatabase::addRejection(const QString& className, const QString& functionName, - const QString& fieldName, const QString& enumName) + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const TypeRejection &r) { - TypeRejection r; - r.class_name = className; - r.function_name = functionName; - r.field_name = fieldName; - r.enum_name = enumName; + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "TypeRejection(type=" << r.matchType << ", class=" + << r.className.pattern() << ", pattern=" << r.pattern.pattern() << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM +void TypeDatabase::addRejection(const TypeRejection &r) +{ m_rejections << r; } -bool TypeDatabase::isClassRejected(const QString& className) const +static inline QString msgRejectReason(const TypeRejection &r, const QString &needle = QString()) +{ + QString result; + QTextStream str(&result); + switch (r.matchType) { + case TypeRejection::ExcludeClass: + str << " matches class exclusion \"" << r.className.pattern() << '"'; + break; + case TypeRejection::Function: + case TypeRejection::Field: + case TypeRejection::Enum: + str << " matches class \"" << r.className.pattern() << "\" and \"" << r.pattern.pattern() << '"'; + break; + } + return result; +} + +// Match class name only +bool TypeDatabase::isClassRejected(const QString& className, QString *reason) const { for (const TypeRejection& r : m_rejections) { - if (r.class_name == className && r.function_name == QLatin1String("*") - && r.field_name == QLatin1String("*") && r.enum_name == QLatin1String("*")) { + if (r.matchType == TypeRejection::ExcludeClass && r.className.match(className).hasMatch()) { + if (reason) + *reason = msgRejectReason(r); return true; } } return false; } -bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumName) const +// Match class name and function/enum/field +static bool findRejection(const QVector &rejections, + TypeRejection::MatchType matchType, + const QString& className, const QString& name, + QString *reason = nullptr) { - for (const TypeRejection& r : m_rejections) { - if (r.enum_name == enumName - && (r.class_name == className || r.class_name == QLatin1String("*"))) { + Q_ASSERT(matchType != TypeRejection::ExcludeClass); + for (const TypeRejection& r : rejections) { + if (r.matchType == matchType && r.pattern.match(name).hasMatch() + && r.className.match(className).hasMatch()) { + if (reason) + *reason = msgRejectReason(r, name); return true; } } - return false; } +bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumName, QString *reason) const +{ + return findRejection(m_rejections, TypeRejection::Enum, className, enumName, reason); +} + void TypeDatabase::addType(TypeEntry *e) { m_entries[e->qualifiedCppName()].append(e); } -bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName) const +bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName, + QString *reason) const { - for (const TypeRejection &r : m_rejections) { - if (r.function_name == functionName && - (r.class_name == className || r.class_name == QLatin1String("*"))) - return true; - } - return false; + return findRejection(m_rejections, TypeRejection::Function, className, functionName, reason); } - -bool TypeDatabase::isFieldRejected(const QString& className, const QString& fieldName) const +bool TypeDatabase::isFieldRejected(const QString& className, const QString& fieldName, + QString *reason) const { - for (const TypeRejection &r : m_rejections) { - if (r.field_name == fieldName && - (r.class_name == className || r.class_name == QLatin1String("*"))) - return true; - } - return false; + return findRejection(m_rejections, TypeRejection::Field, className, fieldName, reason); } FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const diff --git a/ApiExtractor/typedatabase.h b/ApiExtractor/typedatabase.h index 870002b..a4f8adc 100644 --- a/ApiExtractor/typedatabase.h +++ b/ApiExtractor/typedatabase.h @@ -100,12 +100,14 @@ public: ContainerTypeEntryList containerTypes() const; - void addRejection(const QString& className, const QString& functionName, - const QString& fieldName, const QString& enumName); - bool isClassRejected(const QString& className) const; - bool isFunctionRejected(const QString& className, const QString& functionName) const; - bool isFieldRejected(const QString& className, const QString& fieldName) const; - bool isEnumRejected(const QString& className, const QString& enumName) const; + void addRejection(const TypeRejection &); + bool isClassRejected(const QString& className, QString *reason = nullptr) const; + bool isFunctionRejected(const QString& className, const QString& functionName, + QString *reason = nullptr) const; + bool isFieldRejected(const QString& className, const QString& fieldName, + QString *reason = nullptr) const; + bool isEnumRejected(const QString& className, const QString& enumName, + QString *reason = nullptr) const; void addType(TypeEntry* e); diff --git a/ApiExtractor/typesystem.cpp b/ApiExtractor/typesystem.cpp index 33bd790..ed68161 100644 --- a/ApiExtractor/typesystem.cpp +++ b/ApiExtractor/typesystem.cpp @@ -48,9 +48,77 @@ static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote- static inline QString nameAttribute() { return QStringLiteral("name"); } static inline QString sinceAttribute() { return QStringLiteral("since"); } static inline QString flagsAttribute() { return QStringLiteral("flags"); } +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 enumNameAttribute() { return QStringLiteral("enum-name"); } static QVector customConversionsForReview; +// Set a regular expression for rejection from text. By legacy, those are fixed +// strings, except for '*' meaning 'match all'. Enclosing in "^..$" +// indicates regular expression. +static bool setRejectionRegularExpression(const QString &patternIn, + QRegularExpression *re, + QString *errorMessage) +{ + QString pattern; + if (patternIn.startsWith(QLatin1Char('^')) && patternIn.endsWith(QLatin1Char('$'))) + pattern = patternIn; + else if (patternIn == QLatin1String("*")) + pattern = QStringLiteral("^.*$"); + else + pattern = QLatin1Char('^') + QRegularExpression::escape(patternIn) + QLatin1Char('$'); + re->setPattern(pattern); + if (!re->isValid()) { + *errorMessage = QLatin1String("Invalid pattern \"") + patternIn + + QLatin1String("\": ") + re->errorString(); + return false; + } + 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}}; + + // 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; +} + + Handler::Handler(TypeDatabase* database, bool generate) : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) { @@ -1182,10 +1250,10 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts attributes.insert(QLatin1String("to"), QString()); break; case StackElement::Rejection: - attributes.insert(QLatin1String("class"), QLatin1String("*")); - attributes.insert(QLatin1String("function-name"), QLatin1String("*")); - attributes.insert(QLatin1String("field-name"), QLatin1String("*")); - attributes.insert(QLatin1String("enum-name"), QLatin1String("*")); + attributes.insert(classAttribute(), QString()); + attributes.insert(functionNameAttribute(), QString()); + attributes.insert(fieldNameAttribute(), QString()); + attributes.insert(enumNameAttribute(), QString()); break; case StackElement::Removal: attributes.insert(QLatin1String("class"), QLatin1String("all")); @@ -1935,18 +2003,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } } break; - case StackElement::Rejection: { - QString cls = attributes[QLatin1String("class")]; - QString function = attributes[QLatin1String("function-name")]; - QString field = attributes[QLatin1String("field-name")]; - QString enum_ = attributes[QLatin1String("enum-name")]; - if (cls == QLatin1String("*") && function == QLatin1String("*") && field == QLatin1String("*") && enum_ == QLatin1String("*")) { - m_error = QLatin1String("bad reject entry, neither 'class', 'function-name' nor " - "'field' specified"); + case StackElement::Rejection: + if (!addRejection(m_database, attributes, &m_error)) return false; - } - m_database->addRejection(cls, function, field, enum_); - } break; case StackElement::Template: element->value.templateEntry = new TemplateEntry(attributes[nameAttribute()], since); diff --git a/ApiExtractor/typesystem.h b/ApiExtractor/typesystem.h index 480ca95..cc51250 100644 --- a/ApiExtractor/typesystem.h +++ b/ApiExtractor/typesystem.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,7 @@ class Indentor; class AbstractMetaType; QT_BEGIN_NAMESPACE +class QDebug; class QTextStream; QT_END_NAMESPACE @@ -1893,12 +1895,23 @@ private: struct TypeRejection { - QString class_name; - QString function_name; - QString field_name; - QString enum_name; + enum MatchType + { + ExcludeClass, // Match className only + Function, // Match className and function name + Field, // Match className and field name + Enum // Match className and enum name + }; + + QRegularExpression className; + QRegularExpression pattern; + MatchType matchType; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const TypeRejection &r); +#endif + QString fixCppTypeName(const QString &name); class CustomConversion -- cgit v1.2.3 From 47e3a993965c0bc47e006914abecf98c5ad796fe Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 16 May 2017 11:07:40 +0200 Subject: Add rejection by function return type and argument type Extend TypeRejection accordingly. Task-number: PYSIDE-516 Change-Id: Ibf97f3c16498b26e63f4bde5b0ae21acc3dd0c44 Reviewed-by: Alexandru Croitor --- ApiExtractor/abstractmetabuilder.cpp | 13 +++++++++++++ ApiExtractor/typedatabase.cpp | 17 +++++++++++++++++ ApiExtractor/typedatabase.h | 4 ++++ ApiExtractor/typesystem.cpp | 9 ++++++++- ApiExtractor/typesystem.h | 4 +++- 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/ApiExtractor/abstractmetabuilder.cpp b/ApiExtractor/abstractmetabuilder.cpp index 13e1461..3b2ecf8 100644 --- a/ApiExtractor/abstractmetabuilder.cpp +++ b/ApiExtractor/abstractmetabuilder.cpp @@ -2152,6 +2152,13 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel strippedClassName = strippedClassName.mid(cc_pos + 2); TypeInfo functionType = functionItem->type(); + + if (TypeDatabase::instance()->isReturnTypeRejected(className, functionType.toString(), &rejectReason)) { + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); + delete metaFunction; + return nullptr; + } + if (functionName.startsWith(QLatin1Char('~'))) { metaFunction->setFunctionType(AbstractMetaFunction::DestructorFunction); metaFunction->setInvalid(true); @@ -2197,6 +2204,12 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel for (int i = 0; i < arguments.size(); ++i) { ArgumentModelItem arg = arguments.at(i); + if (TypeDatabase::instance()->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) { + m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); + delete metaFunction; + return nullptr; + } + bool ok; AbstractMetaType* metaType = translateType(arg->type(), &ok); if (!ok) { diff --git a/ApiExtractor/typedatabase.cpp b/ApiExtractor/typedatabase.cpp index b397f19..a1b2807 100644 --- a/ApiExtractor/typedatabase.cpp +++ b/ApiExtractor/typedatabase.cpp @@ -258,6 +258,11 @@ static inline QString msgRejectReason(const TypeRejection &r, const QString &nee case TypeRejection::Enum: str << " matches class \"" << r.className.pattern() << "\" and \"" << r.pattern.pattern() << '"'; break; + case TypeRejection::ArgumentType: + case TypeRejection::ReturnType: + str << " matches class \"" << r.className.pattern() << "\" and \"" << needle + << "\" matches \"" << r.pattern.pattern() << '"'; + break; } return result; } @@ -315,6 +320,18 @@ bool TypeDatabase::isFieldRejected(const QString& className, const QString& fiel return findRejection(m_rejections, TypeRejection::Field, className, fieldName, reason); } +bool TypeDatabase::isArgumentTypeRejected(const QString& className, const QString& typeName, + QString *reason) const +{ + return findRejection(m_rejections, TypeRejection::ArgumentType, className, typeName, reason); +} + +bool TypeDatabase::isReturnTypeRejected(const QString& className, const QString& typeName, + QString *reason) const +{ + return findRejection(m_rejections, TypeRejection::ReturnType, className, typeName, reason); +} + FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const { TypeEntry *fte = findType(name); diff --git a/ApiExtractor/typedatabase.h b/ApiExtractor/typedatabase.h index a4f8adc..86f9334 100644 --- a/ApiExtractor/typedatabase.h +++ b/ApiExtractor/typedatabase.h @@ -108,6 +108,10 @@ public: QString *reason = nullptr) const; bool isEnumRejected(const QString& className, const QString& enumName, QString *reason = nullptr) const; + bool isArgumentTypeRejected(const QString& className, const QString& typeName, + QString *reason = nullptr) const; + bool isReturnTypeRejected(const QString& className, const QString& typeName, + QString *reason = nullptr) const; void addType(TypeEntry* e); diff --git a/ApiExtractor/typesystem.cpp b/ApiExtractor/typesystem.cpp index ed68161..3ec82c5 100644 --- a/ApiExtractor/typesystem.cpp +++ b/ApiExtractor/typesystem.cpp @@ -52,6 +52,8 @@ 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 enumNameAttribute() { return QStringLiteral("enum-name"); } +static inline QString argumentTypeAttribute() { return QStringLiteral("argument-type"); } +static inline QString returnTypeAttribute() { return QStringLiteral("return-type"); } static QVector customConversionsForReview; @@ -92,7 +94,10 @@ static bool addRejection(TypeDatabase *database, const QHash & static const AttributeMatchTypePair attributeMatchTypeMapping[] = {{functionNameAttribute(), TypeRejection::Function}, {fieldNameAttribute(), TypeRejection::Field}, - {enumNameAttribute(), TypeRejection::Enum}}; + {enumNameAttribute(), TypeRejection::Enum}, + {argumentTypeAttribute(), TypeRejection::ArgumentType}, + {returnTypeAttribute(), TypeRejection::ReturnType} + }; // Search for non-empty attribute (function, field, enum) const auto aend = attributes.cend(); @@ -1254,6 +1259,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts 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")); diff --git a/ApiExtractor/typesystem.h b/ApiExtractor/typesystem.h index cc51250..ac0b40f 100644 --- a/ApiExtractor/typesystem.h +++ b/ApiExtractor/typesystem.h @@ -1900,7 +1900,9 @@ struct TypeRejection ExcludeClass, // Match className only Function, // Match className and function name Field, // Match className and field name - Enum // Match className and enum name + Enum, // Match className and enum name + ArgumentType, // Match className and argument type + ReturnType // Match className and return type }; QRegularExpression className; -- cgit v1.2.3