diff options
Diffstat (limited to 'src/qmlcompiler')
27 files changed, 374 insertions, 272 deletions
diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h index 481c0e27ae..f4d8696f69 100644 --- a/src/qmlcompiler/qdeferredpointer_p.h +++ b/src/qmlcompiler/qdeferredpointer_p.h @@ -50,15 +50,15 @@ public: QDeferredSharedPointer() = default; QDeferredSharedPointer(QSharedPointer<T> data) - : m_data(data) + : m_data(std::move(data)) {} QDeferredSharedPointer(QWeakPointer<T> data) - : m_data(data) + : m_data(std::move(data)) {} QDeferredSharedPointer(QSharedPointer<T> data, QSharedPointer<Factory> factory) - : m_data(data), m_factory(factory) + : m_data(std::move(data)), m_factory(std::move(factory)) { // You have to provide a valid pointer if you provide a factory. We cannot allocate the // pointer for you because then two copies of the same QDeferredSharedPointer will diverge diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp index a7bbd5326c..093b11684c 100644 --- a/src/qmlcompiler/qqmljsbasicblocks.cpp +++ b/src/qmlcompiler/qqmljsbasicblocks.cpp @@ -45,14 +45,14 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run( m_function = function; m_annotations = annotations; - for (int i = 0, end = function->argumentTypes.length(); i != end; ++i) { + for (int i = 0, end = function->argumentTypes.size(); i != end; ++i) { InstructionAnnotation annotation; annotation.changedRegisterIndex = FirstArgument + i; annotation.changedRegister = function->argumentTypes[i]; m_annotations[-annotation.changedRegisterIndex] = annotation; } - for (int i = 0, end = function->registerTypes.length(); i != end; ++i) { + for (int i = 0, end = function->registerTypes.size(); i != end; ++i) { InstructionAnnotation annotation; annotation.changedRegisterIndex = firstRegisterIndex() + i; annotation.changedRegister = function->registerTypes[i]; @@ -62,7 +62,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run( m_basicBlocks.insert_or_assign(m_annotations.begin().key(), BasicBlock()); const QByteArray byteCode = function->code; - decode(byteCode.constData(), static_cast<uint>(byteCode.length())); + decode(byteCode.constData(), static_cast<uint>(byteCode.size())); if (m_hadBackJumps) { // We may have missed some connections between basic blocks if there were back jumps. // Fill them in via a second pass. @@ -75,7 +75,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSBasicBlocks::run( } reset(); - decode(byteCode.constData(), static_cast<uint>(byteCode.length())); + decode(byteCode.constData(), static_cast<uint>(byteCode.size())); for (auto it = m_basicBlocks.begin(), end = m_basicBlocks.end(); it != end; ++it) deduplicate(it->second.jumpOrigins); } @@ -415,7 +415,7 @@ void QQmlJSBasicBlocks::adjustTypes() const InstructionAnnotation &annotation = m_annotations[instructionOffset]; - Q_ASSERT(it->trackedTypes.length() == 1); + Q_ASSERT(it->trackedTypes.size() == 1); Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister)); Q_ASSERT(!annotation.readRegisters.isEmpty()); @@ -430,7 +430,7 @@ void QQmlJSBasicBlocks::adjustTypes() valueType); } - for (const QList<int> &conversions : qAsConst(it->registerReadersAndConversions)) { + for (const QList<int> &conversions : std::as_const(it->registerReadersAndConversions)) { for (int conversion : conversions) liveConversions[conversion].append(it->trackedRegister); } @@ -439,14 +439,14 @@ void QQmlJSBasicBlocks::adjustTypes() } for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) { - for (const QList<int> &conversions : qAsConst(it->registerReadersAndConversions)) { + for (const QList<int> &conversions : std::as_const(it->registerReadersAndConversions)) { for (int conversion : conversions) liveConversions[conversion].append(it->trackedRegister); } // There is always one first occurrence of any tracked type. Conversions don't change // the type. - if (it->trackedTypes.length() != 1) + if (it->trackedTypes.size() != 1) continue; m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()); diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index f9455e7815..a14ea33e14 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -35,6 +35,15 @@ using namespace Qt::StringLiterals; m_body += u"// "_s + QStringLiteral(#function) + u'\n'; \ } + +static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type) +{ + return !type.isNull() + && !resolver->equals(type, resolver->nullType()) + && !resolver->equals(type, resolver->emptyListType()) + && !resolver->equals(type, resolver->voidType()); +} + QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) const { return type->augmentedInternalName(); @@ -43,9 +52,8 @@ QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) c QQmlJSCodeGenerator::QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext, const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver, - QQmlJSLogger *logger, const QStringList &sourceCodeLines) + QQmlJSLogger *logger) : QQmlJSCompilePass(unitGenerator, typeResolver, logger) - , m_sourceCodeLines(sourceCodeLines) , m_context(compilerContext) {} @@ -75,14 +83,6 @@ QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType) return QString(); } -static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type) -{ - return !type.isNull() - && !resolver->equals(type, resolver->nullType()) - && !resolver->equals(type, resolver->emptyListType()) - && !resolver->equals(type, resolver->voidType()); -} - QQmlJSAotFunction QQmlJSCodeGenerator::run( const Function *function, const InstructionAnnotations *annotations, QQmlJS::DiagnosticMessage *error) @@ -103,7 +103,7 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run( auto ¤tRegisterNames = registerNames[registerIndex]; QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)]; if (name.isEmpty()) - name = u"r%1_%2"_s.arg(registerIndex).arg(currentRegisterNames.count()); + name = u"r%1_%2"_s.arg(registerIndex).arg(currentRegisterNames.size()); typesForRegisters[seenType] = name; } }; @@ -123,7 +123,7 @@ QT_WARNING_POP // ensure we have m_labels for loops for (const auto loopLabel : m_context->labelInfo) - m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.count())); + m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size())); // Initialize the first instruction's state to hold the arguments. // After this, the arguments (or whatever becomes of them) are carried @@ -131,11 +131,14 @@ QT_WARNING_POP m_state.State::operator=(initialState(m_function)); const QByteArray byteCode = function->code; - decode(byteCode.constData(), static_cast<uint>(byteCode.length())); + decode(byteCode.constData(), static_cast<uint>(byteCode.size())); QQmlJSAotFunction result; result.includes.swap(m_includes); + result.code += u"// %1 at line %2, column %3\n"_s + .arg(m_context->name).arg(m_context->line).arg(m_context->column); + QDuplicateTracker<QString> generatedVariables; for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend(); registerIt != registerEnd; ++registerIt) { @@ -204,7 +207,7 @@ QT_WARNING_POP result.code += m_body; - for (const QQmlJSRegisterContent &argType : qAsConst(function->argumentTypes)) { + for (const QQmlJSRegisterContent &argType : std::as_const(function->argumentTypes)) { if (argType.isValid()) { result.argumentTypes.append( m_typeResolver->originalType(argType.storedType()) @@ -309,17 +312,41 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index) { INJECT_TRACE_INFO(generate_LoadConst); - auto encodedConst = m_jsUnitGenerator->constant(index); - double value = QV4::StaticValue::fromReturnedValue(encodedConst).doubleValue(); - m_body += m_state.accumulatorVariableOut; + // You cannot actually get it to generate LoadConst for anything but double. We have + // a numer of specialized instructions for the other types, after all. However, let's + // play it safe. - // ### handle other types? - m_body += u" = "_s + conversion( - m_typeResolver->intType(), m_state.accumulatorOut().storedType(), - toNumericString(value)); + const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index); + const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(encodedConst); + const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(encodedConst); + + m_body += m_state.accumulatorVariableOut + u" = "_s; + if (type == m_typeResolver->realType()) { + m_body += conversion( + type, m_state.accumulatorOut().storedType(), + toNumericString(value.doubleValue())); + } else if (type == m_typeResolver->intType()) { + m_body += conversion( + type, m_state.accumulatorOut().storedType(), + QString::number(value.integerValue())); + } else if (type == m_typeResolver->boolType()) { + m_body += conversion( + type, m_state.accumulatorOut().storedType(), + value.booleanValue() ? u"true"_s : u"false"_s); + } else if (type == m_typeResolver->voidType()) { + m_body += conversion( + type, m_state.accumulatorOut().storedType(), + QString()); + } else if (type == m_typeResolver->nullType()) { + m_body += conversion( + type, m_state.accumulatorOut().storedType(), + u"nullptr"_s); + } else { + reject(u"unsupported constant type"_s); + } m_body += u";\n"_s; - generateOutputVariantConversion(m_typeResolver->intType()); + generateOutputVariantConversion(type); } void QQmlJSCodeGenerator::generate_LoadZero() @@ -2227,17 +2254,6 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction( || !m_state.accumulatorVariableOut.isEmpty() || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType())); - const int currentLine = currentSourceLocation().startLine; - if (currentLine != m_lastLineNumberUsed) { - const int nextLine = nextJSLine(currentLine); - for (auto line = currentLine - 1; line < nextLine - 1; ++line) { - m_body += u"// "_s; - m_body += m_sourceCodeLines.value(line).trimmed(); - m_body += u'\n'; - } - m_lastLineNumberUsed = currentLine; - } - // If the instruction has no side effects and doesn't write any register, it's dead. // We might still need the label, though, and the source code comment. if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty()) @@ -2291,9 +2307,18 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func const auto primitive = m_typeResolver->jsPrimitiveType(); if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) { - m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), - registerVariable(lhs) + (invert ? u" != "_s : u" == "_s) - + m_state.accumulatorVariableIn); + if (isTypeStorable(m_typeResolver, lhsType)) { + m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), + registerVariable(lhs) + (invert ? u" != "_s : u" == "_s) + + m_state.accumulatorVariableIn); + } else if (m_typeResolver->equals(lhsType, m_typeResolver->emptyListType())) { + // We cannot compare two empty lists, because we don't know whether it's + // the same instance or not. "[] === []" is false, but "var a = []; a === a" is true; + reject(u"comparison of two empty lists"_s); + } else { + // null === null and undefined === undefined + m_body += invert ? u"false"_s : u"true"_s; + } } else { m_body += conversion( m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), @@ -2462,7 +2487,7 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset if (relativeOffset) { auto labelIt = m_labels.find(absoluteOffset); if (labelIt == m_labels.end()) - labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.count())); + labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size())); conversionCode += u" goto "_s + *labelIt + u";\n"_s; } @@ -2510,24 +2535,24 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType(); const auto boolType = m_typeResolver->boolType(); - auto zeroBoolOrNumeric = [&](const QQmlJSScope::ConstPtr &to) { + auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) { if (m_typeResolver->equals(to, boolType)) return u"false"_s; if (m_typeResolver->equals(to, m_typeResolver->intType())) return u"0"_s; - if (m_typeResolver->equals(to, m_typeResolver->floatType())) - return u"0.0f"_s; - if (m_typeResolver->equals(to, m_typeResolver->realType())) - return u"0.0"_s; return QString(); }; if (m_typeResolver->equals(from, m_typeResolver->voidType())) { if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s; - const QString zero = zeroBoolOrNumeric(to); + const QString zero = zeroBoolOrInt(to); if (!zero.isEmpty()) return zero; + if (m_typeResolver->equals(to, m_typeResolver->floatType())) + return u"std::numeric_limits<float>::quiet_NaN()"_s; + if (m_typeResolver->equals(to, m_typeResolver->realType())) + return u"std::numeric_limits<double>::quiet_NaN()"_s; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return QQmlJSUtils::toLiteral(u"undefined"_s); if (m_typeResolver->equals(from, to)) @@ -2545,9 +2570,13 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s; if (m_typeResolver->equals(to, varType)) return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s; - const QString zero = zeroBoolOrNumeric(to); + const QString zero = zeroBoolOrInt(to); if (!zero.isEmpty()) return zero; + if (m_typeResolver->equals(to, m_typeResolver->floatType())) + return u"0.0f"_s; + if (m_typeResolver->equals(to, m_typeResolver->realType())) + return u"0.0"_s; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return QQmlJSUtils::toLiteral(u"null"_s); if (m_typeResolver->equals(from, to)) @@ -2702,21 +2731,6 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, return QString(); } -int QQmlJSCodeGenerator::nextJSLine(uint line) const -{ - auto findLine = [](uint line, const QV4::CompiledData::CodeOffsetToLine &entry) { - return entry.line > line; - }; - const auto codeToLine - = std::upper_bound(m_context->lineNumberMapping.constBegin(), - m_context->lineNumberMapping.constEnd(), - line, - findLine); - bool bNoNextLine = m_context->lineNumberMapping.constEnd() == codeToLine; - - return static_cast<int>(bNoNextLine ? -1 : codeToLine->line); -} - void QQmlJSCodeGenerator::reject(const QString &thing) { setError(u"Cannot generate efficient code for %1"_s.arg(thing)); diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h index fb54ac0ad2..6fdad57831 100644 --- a/src/qmlcompiler/qqmljscodegenerator_p.h +++ b/src/qmlcompiler/qqmljscodegenerator_p.h @@ -33,7 +33,7 @@ public: QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext, const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver, - QQmlJSLogger *logger, const QStringList &sourceCodeLines); + QQmlJSLogger *logger); ~QQmlJSCodeGenerator() = default; QQmlJSAotFunction run(const Function *function, const InstructionAnnotations *annotations, @@ -270,17 +270,12 @@ private: return m_typeResolver->jsGlobalObject()->property(u"Math"_s).type(); } - int nextJSLine(uint line) const; - - QStringList m_sourceCodeLines; - // map from instruction offset to sequential label number QHash<int, QString> m_labels; const QV4::Compiler::Context *m_context = nullptr; const InstructionAnnotations *m_annotations = nullptr; - int m_lastLineNumberUsed = -1; bool m_skipUntilNextLabel = false; QStringList m_includes; diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h index 40e61464f5..5c4f1c4637 100644 --- a/src/qmlcompiler/qqmljscompilepass_p.h +++ b/src/qmlcompiler/qqmljscompilepass_p.h @@ -166,7 +166,7 @@ protected: int firstRegisterIndex() const { - return FirstArgument + m_function->argumentTypes.count(); + return FirstArgument + m_function->argumentTypes.size(); } bool isArgument(int registerIndex) const @@ -184,11 +184,11 @@ protected: State initialState(const Function *function) { State state; - for (int i = 0, end = function->argumentTypes.length(); i < end; ++i) { + for (int i = 0, end = function->argumentTypes.size(); i < end; ++i) { state.registers[FirstArgument + i] = function->argumentTypes.at(i); Q_ASSERT(state.registers[FirstArgument + i].isValid()); } - for (int i = 0, end = function->registerTypes.length(); i != end; ++i) + for (int i = 0, end = function->registerTypes.size(); i != end; ++i) state.registers[firstRegisterIndex() + i] = function->registerTypes[i]; return state; } diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp index 76a7ed80e2..d2e43b6f0b 100644 --- a/src/qmlcompiler/qqmljscompiler.cpp +++ b/src/qmlcompiler/qqmljscompiler.cpp @@ -87,7 +87,7 @@ static void annotateListElements(QmlIR::Document *document) { QStringList listElementNames; - for (const QV4::CompiledData::Import *import : qAsConst(document->imports)) { + for (const QV4::CompiledData::Import *import : std::as_const(document->imports)) { const QString uri = document->stringAt(import->uriIndex); if (uri != QStringLiteral("QtQml.Models") && uri != QStringLiteral("QtQuick")) continue; @@ -104,7 +104,7 @@ static void annotateListElements(QmlIR::Document *document) if (listElementNames.isEmpty()) return; - for (QmlIR::Object *object : qAsConst(document->objects)) { + for (QmlIR::Object *object : std::as_const(document->objects)) { if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex))) continue; for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { @@ -118,7 +118,7 @@ static void annotateListElements(QmlIR::Document *document) static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc, QQmlJSCompileError *error) { - for (QmlIR::Object *object: qAsConst(doc.objects)) { + for (QmlIR::Object *object: std::as_const(doc.objects)) { for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { if (binding->type() != QV4::CompiledData::Binding::Type_Script) continue; @@ -227,7 +227,7 @@ bool qCompileQmlFile(QmlIR::Document &irDocument, const QString &inputFileName, aotCompiler->setDocument(&v4CodeGen, &irDocument); QHash<QmlIR::Object *, QmlIR::Object *> effectiveScopes; - for (QmlIR::Object *object: qAsConst(irDocument.objects)) { + for (QmlIR::Object *object: std::as_const(irDocument.objects)) { if (object->functionsAndExpressions->count == 0 && object->bindingCount() == 0) continue; @@ -438,7 +438,7 @@ void wrapCall(const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, { using return_type = std::invoke_result_t<Binding, const QQmlPrivate::AOTCompiledContext *, void **>; if constexpr (std::is_same_v<return_type, void>) { - Q_UNUSED(dataPtr); + Q_UNUSED(dataPtr) binding(aotContext, argumentsPtr); } else { if (dataPtr) { @@ -453,8 +453,8 @@ void wrapCall(const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, static const char *funcHeaderCode = R"( [](const QQmlPrivate::AOTCompiledContext *aotContext, void *dataPtr, void **argumentsPtr) { wrapCall(aotContext, dataPtr, argumentsPtr, [](const QQmlPrivate::AOTCompiledContext *aotContext, void **argumentsPtr) { -Q_UNUSED(aotContext); -Q_UNUSED(argumentsPtr); +Q_UNUSED(aotContext) +Q_UNUSED(argumentsPtr) )"; bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFileName, const QV4::CompiledData::SaveableUnitPointer &unit, const QQmlJSAotFunctionMap &aotFunctions, QString *errorString) @@ -509,7 +509,8 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile if (!writeStr(qQmlJSSymbolNamespaceForPath(inputFileName).toUtf8())) return false; - if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [] = {\n"))) + if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [];\n" + "extern const unsigned char qmlData alignas(16) [] = {\n"))) return false; unit.saveToDisk<uchar>([&writeStr](const uchar *begin, quint32 size) { @@ -549,10 +550,12 @@ bool qSaveQmlJSUnitAsCpp(const QString &inputFileName, const QString &outputFile writeStr(aotFunctions[FileScopeCodeIndex].code.toUtf8().constData()); if (aotFunctions.size() <= 1) { // FileScopeCodeIndex is always there, but it may be the only one. - writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, QMetaType::fromType<void>(), {}, nullptr } };"); + writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n" + "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = { { 0, QMetaType::fromType<void>(), {}, nullptr } };"); } else { writeStr(wrapCallCode); - writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n"); + writeStr("extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[];\n" + "extern const QQmlPrivate::AOTCompiledFunction aotBuiltFunctions[] = {\n"); QString footer = QStringLiteral("});}\n"); @@ -621,7 +624,6 @@ void QQmlJSAotCompiler::setDocument( m_logger->setFileName(resourcePathInfo.fileName()); m_logger->setCode(irDocument->code); m_unitGenerator = &irDocument->jsGenerator; - m_entireSourceCodeLines = irDocument->code.split(u'\n'); QQmlJSScope::Ptr target = QQmlJSScope::create(); QQmlJSImportVisitor visitor(target, m_importer, m_logger, resourcePathInfo.canonicalPath() + u'/', @@ -648,9 +650,8 @@ QQmlJS::DiagnosticMessage QQmlJSAotCompiler::diagnose( const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location) const { if (isStrict(m_document) - && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg) - && !m_logger->isCategoryIgnored(Log_Compiler) - && m_logger->categoryLevel(Log_Compiler) == QtCriticalMsg) { + && (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg) + && m_logger->isCategoryFatal(Log_Compiler)) { qFatal("%s:%d: (strict mode) %s", qPrintable(QFileInfo(m_resourcePath).fileName()), location.startLine, qPrintable(message)); @@ -763,8 +764,7 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile( return compileError(); QQmlJSCodeGenerator codegen( - context, m_unitGenerator, &m_typeResolver, m_logger, - m_entireSourceCodeLines); + context, m_unitGenerator, &m_typeResolver, m_logger); QQmlJSAotFunction result = codegen.run(function, &typePropagationResult, error); return error->isValid() ? compileError() : result; } diff --git a/src/qmlcompiler/qqmljscompiler_p.h b/src/qmlcompiler/qqmljscompiler_p.h index c6a60f5a94..45de4abaf1 100644 --- a/src/qmlcompiler/qqmljscompiler_p.h +++ b/src/qmlcompiler/qqmljscompiler_p.h @@ -75,7 +75,6 @@ protected: const QString &message, QtMsgType type, const QQmlJS::SourceLocation &location) const; QQmlJSTypeResolver m_typeResolver; - QStringList m_entireSourceCodeLines; const QString m_resourcePath; const QStringList m_qmldirFiles; diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp index 56d1d25124..528b4c0602 100644 --- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp +++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp @@ -66,7 +66,7 @@ void QQmlJSFunctionInitializer::populateSignature( arguments = ast->formals->formals(); if (function->argumentTypes.isEmpty()) { - for (const QQmlJS::AST::BoundName &argument : qAsConst(arguments)) { + for (const QQmlJS::AST::BoundName &argument : std::as_const(arguments)) { if (argument.typeAnnotation) { if (const auto type = m_typeResolver->typeFromAST(argument.typeAnnotation->type)) { function->argumentTypes.append( @@ -97,7 +97,7 @@ void QQmlJSFunctionInitializer::populateSignature( } } - for (int i = QQmlJSCompilePass::FirstArgument + function->argumentTypes.length(); + for (int i = QQmlJSCompilePass::FirstArgument + function->argumentTypes.size(); i < context->registerCountInFunction; ++i) { function->registerTypes.append(m_typeResolver->tracked( m_typeResolver->globalType(m_typeResolver->voidType()))); diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp index f0897de99f..739ad70b7e 100644 --- a/src/qmlcompiler/qqmljsimporter.cpp +++ b/src/qmlcompiler/qqmljsimporter.cpp @@ -81,7 +81,7 @@ void QQmlJSImporter::readQmltypes( QQmlJS::SourceLocation() }); - for (const QString &dependency : qAsConst(dependencyStrings)) { + for (const QString &dependency : std::as_const(dependencyStrings)) { const auto blank = dependency.indexOf(u' '); if (blank < 0) { dependencies->append(QQmlDirParser::Import(dependency, {}, @@ -265,11 +265,11 @@ void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import, { // Import the dependencies with an invalid prefix. The prefix will never be matched by actual // QML code but the C++ types will be visible. - for (auto const &dependency : qAsConst(import.dependencies)) + for (auto const &dependency : std::as_const(import.dependencies)) importHelper(dependency.module, types, QString(), dependency.version, true); bool hasOptionalImports = false; - for (auto const &import : qAsConst(import.imports)) { + for (auto const &import : std::as_const(import.imports)) { if (import.flags & QQmlDirParser::Import::Optional) { hasOptionalImports = true; if (!m_useOptionalImports) { @@ -576,7 +576,7 @@ void QQmlJSImporter::importQmldirs(const QStringList &qmldirFiles) m_seenQmldirFiles.insert(qmldirName, result); - for (const auto &object : qAsConst(result.objects)) { + for (const auto &object : std::as_const(result.objects)) { for (const auto &ex : object.exports) { m_seenImports.insert({ex.package(), ex.version()}, qmldirName); // We also have to handle the case that no version is provided @@ -696,7 +696,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types, if (modulePath.startsWith(u':')) { if (m_mapper) { const QString resourcePath = modulePath.mid( - 1, modulePath.endsWith(u'/') ? modulePath.length() - 2 : -1) + 1, modulePath.endsWith(u'/') ? modulePath.size() - 2 : -1) + SlashQmldir; const auto entry = m_mapper->entry( QQmlJSResourceFileMapper::resourceFileFilter(resourcePath)); diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 5128b0f69c..6f2a8ee265 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -283,8 +283,8 @@ void QQmlJSImportVisitor::resolveAliasesAndIds() if (doRequeue) requeue.enqueue(object); - if (objects.isEmpty() && requeue.length() < lastRequeueLength) { - lastRequeueLength = requeue.length(); + if (objects.isEmpty() && requeue.size() < lastRequeueLength) { + lastRequeueLength = requeue.size(); objects.swap(requeue); } } @@ -547,7 +547,7 @@ void QQmlJSImportVisitor::processDefaultProperties() const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName); - if (it.value().length() > 1 && !defaultProp.isList()) { + if (it.value().size() > 1 && !defaultProp.isList()) { m_logger->log( QStringLiteral("Cannot assign multiple objects to a default non-list property"), Log_Property, it.value().constFirst()->sourceLocation()); @@ -618,7 +618,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects() // literal bindings must already be added at this point. QSet<QPair<QQmlJSScope::Ptr, QString>> visited; for (const PendingPropertyObjectBinding &objectBinding : - qAsConst(m_pendingPropertyObjectBindings)) { + std::as_const(m_pendingPropertyObjectBindings)) { // unique because it's per-scope and per-property const auto uniqueBindingId = qMakePair(objectBinding.scope, objectBinding.name); if (visited.contains(uniqueBindingId)) @@ -640,7 +640,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects() QSet<QPair<QQmlJSScope::Ptr, QString>> foundValueSources; for (const PendingPropertyObjectBinding &objectBinding : - qAsConst(m_pendingPropertyObjectBindings)) { + std::as_const(m_pendingPropertyObjectBindings)) { const QString propertyName = objectBinding.name; QQmlJSScope::ConstPtr childScope = objectBinding.childScope; @@ -780,7 +780,7 @@ void QQmlJSImportVisitor::checkRequiredProperties() QStringList aliasExpression = property.aliasExpression().split(u'.'); - if (aliasExpression.length() != 2) + if (aliasExpression.size() != 2) continue; if (aliasExpression[0] == scopeId && aliasExpression[1] == propName) { @@ -793,8 +793,8 @@ void QQmlJSImportVisitor::checkRequiredProperties() if (propertyUsedInRootAlias) continue; - const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.length() > 1 - ? scopesToSearch.at(scopesToSearch.length() - 2) + const QQmlJSScope::ConstPtr propertyScope = scopesToSearch.size() > 1 + ? scopesToSearch.at(scopesToSearch.size() - 2) : QQmlJSScope::ConstPtr(); const QString propertyScopeName = !propertyScope.isNull() @@ -971,7 +971,7 @@ void QQmlJSImportVisitor::checkSignals() const QStringList signalParameters = signalMethod->parameterNames(); - if (pair.second.length() > signalParameters.length()) { + if (pair.second.size() > signalParameters.size()) { m_logger->log(QStringLiteral("Signal handler for \"%2\" has more formal" " parameters than the signal it handles.") .arg(pair.first), @@ -979,7 +979,7 @@ void QQmlJSImportVisitor::checkSignals() continue; } - for (qsizetype i = 0; i < pair.second.length(); i++) { + for (qsizetype i = 0; i < pair.second.size(); i++) { const QStringView handlerParameter = pair.second.at(i); const qsizetype j = signalParameters.indexOf(handlerParameter); if (j == i || j < 0) @@ -1043,7 +1043,7 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina for (QQmlJSScope::ConstPtr scope = originalScope; scope;) { if (scopes.contains(scope)) { QString inheritenceCycle; - for (const auto &seen : qAsConst(scopes)) { + for (const auto &seen : std::as_const(scopes)) { inheritenceCycle.append(seen->baseTypeName()); inheritenceCycle.append(QLatin1String(" -> ")); } @@ -1273,7 +1273,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::StringLiteral *sl) bool escaped = false; const QChar stringQuote = s[0]; - for (qsizetype i = 1; i < s.length() - 1; i++) { + for (qsizetype i = 1; i < s.size() - 1; i++) { const QChar c = s[i]; if (c == u'\\') { @@ -1287,7 +1287,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::StringLiteral *sl) } else { if (c == u'`') templateString += u'\\'; - if (c == u'$' && i + 1 < s.length() - 1 && s[i + 1] == u'{') + if (c == u'$' && i + 1 < s.size() - 1 && s[i + 1] == u'{') templateString += u'\\'; } @@ -1369,10 +1369,14 @@ bool QQmlJSImportVisitor::visit(UiInlineComponent *component) return true; } -void QQmlJSImportVisitor::endVisit(UiInlineComponent *) +void QQmlJSImportVisitor::endVisit(UiInlineComponent *component) { m_inlineComponentName = QStringView(); - Q_ASSERT(!m_nextIsInlineComponent); + if (m_nextIsInlineComponent) { + m_logger->log(u"Inline component declaration must be followed by a typename"_s, + Log_Syntax, component->firstSourceLocation()); + } + m_nextIsInlineComponent = false; // might have missed an inline component if file contains invalid QML } bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) @@ -1395,9 +1399,15 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) QString aliasExpr; const bool isAlias = (typeName == u"alias"_s); if (isAlias) { + auto tryParseAlias = [&]() { typeName.clear(); // type name is useless for alias here, so keep it empty + if (!publicMember->statement) { + m_logger->log(QStringLiteral("Invalid alias expression – an initalizer is needed."), + Log_Alias, publicMember->memberType->firstSourceLocation()); // TODO: extend warning to cover until endSourceLocation + return; + } const auto expression = cast<ExpressionStatement *>(publicMember->statement); - auto node = expression->expression; + auto node = expression ? expression->expression : nullptr; auto fex = cast<FieldMemberExpression *>(node); while (fex) { node = fex->base; @@ -1412,6 +1422,8 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) "member expressions can be aliased."), Log_Alias, expression->firstSourceLocation()); } + }; + tryParseAlias(); } else { const QString name = buildName(publicMember->memberType); if (m_rootScopeImports.contains(name) && !m_rootScopeImports[name].scope.isNull()) { diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp index 03eb481f5a..5c947cd321 100644 --- a/src/qmlcompiler/qqmljslinter.cpp +++ b/src/qmlcompiler/qqmljslinter.cpp @@ -223,9 +223,9 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger, if (!comment.startsWith(u" qmllint ") && !comment.startsWith(u"qmllint ")) continue; - QStringList words = comment.split(u' '); - if (words.constFirst().isEmpty()) - words.removeFirst(); + QStringList words = comment.split(u' ', Qt::SkipEmptyParts); + if (words.size() < 2) + continue; const QString command = words.at(1); @@ -246,21 +246,23 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger, } if (command == u"disable"_s) { - const QString line = lines[loc.startLine - 1]; - const QString preComment = line.left(line.indexOf(comment) - 2); - - bool lineHasContent = false; - for (qsizetype i = 0; i < preComment.size(); i++) { - if (!preComment[i].isSpace()) { - lineHasContent = true; - break; + if (const qsizetype lineIndex = loc.startLine - 1; lineIndex < lines.size()) { + const QString line = lines[loc.startLine - 1]; + const QString preComment = line.left(line.indexOf(comment) - 2); + + bool lineHasContent = false; + for (qsizetype i = 0; i < preComment.size(); i++) { + if (!preComment[i].isSpace()) { + lineHasContent = true; + break; + } } - } - if (lineHasContent) - oneLineDisablesPerLine[loc.startLine] |= categories; - else - disablesPerLine[loc.startLine] |= categories; + if (lineHasContent) + oneLineDisablesPerLine[loc.startLine] |= categories; + else + disablesPerLine[loc.startLine] |= categories; + } } else if (command == u"enable"_s) { enablesPerLine[loc.startLine + 1] |= categories; } else { diff --git a/src/qmlcompiler/qqmljslintercodegen.cpp b/src/qmlcompiler/qqmljslintercodegen.cpp index dfa474fc46..e362044779 100644 --- a/src/qmlcompiler/qqmljslintercodegen.cpp +++ b/src/qmlcompiler/qqmljslintercodegen.cpp @@ -28,7 +28,6 @@ void QQmlJSLinterCodegen::setDocument(const QmlIR::JSCodeGen *codegen, Q_UNUSED(codegen); m_document = document; m_unitGenerator = &document->jsGenerator; - m_entireSourceCodeLines = document->code.split(u'\n'); } std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage> diff --git a/src/qmlcompiler/qqmljsloadergenerator.cpp b/src/qmlcompiler/qqmljsloadergenerator.cpp index 21be215bba..494ecdadd0 100644 --- a/src/qmlcompiler/qqmljsloadergenerator.cpp +++ b/src/qmlcompiler/qqmljsloadergenerator.cpp @@ -49,7 +49,7 @@ QString mangledIdentifier(const QString &str) } } - for (int ei = str.length(); i != ei; ++i) { + for (int ei = str.size(); i != ei; ++i) { auto c = str.at(i).unicode(); if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || (c >= QLatin1Char('a') && c <= QLatin1Char('z')) @@ -68,7 +68,7 @@ QString qQmlJSSymbolNamespaceForPath(const QString &relativePath) { QFileInfo fi(relativePath); QString symbol = fi.path(); - if (symbol.length() == 1 && symbol.startsWith(QLatin1Char('.'))) { + if (symbol.size() == 1 && symbol.startsWith(QLatin1Char('.'))) { symbol.clear(); } else { symbol.replace(QLatin1Char('/'), QLatin1Char('_')); @@ -105,7 +105,7 @@ bool qQmlJSGenerateLoader(const QStringList &compiledFiles, const QString &outpu stream << "\n"; stream << "namespace QmlCacheGeneratedCode {\n"; - for (int i = 0; i < compiledFiles.count(); ++i) { + for (int i = 0; i < compiledFiles.size(); ++i) { const QString compiledFile = compiledFiles.at(i); const QString ns = qQmlJSSymbolNamespaceForPath(compiledFile); stream << "namespace " << ns << " { \n"; @@ -131,7 +131,7 @@ bool qQmlJSGenerateLoader(const QStringList &compiledFiles, const QString &outpu stream << "Registry::Registry() {\n"; - for (int i = 0; i < compiledFiles.count(); ++i) { + for (int i = 0; i < compiledFiles.size(); ++i) { const QString qrcFile = compiledFiles.at(i); const QString ns = qQmlJSSymbolNamespaceForPath(qrcFile); stream << " resourcePathToCachedUnit.insert(QStringLiteral(\"" << qrcFile << "\"), &QmlCacheGeneratedCode::" << ns << "::unit);\n"; diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp index d8b1fe096f..9c22f55287 100644 --- a/src/qmlcompiler/qqmljslogger.cpp +++ b/src/qmlcompiler/qqmljslogger.cpp @@ -223,7 +223,7 @@ void QQmlJSLogger::printContext(const QString &overrideFileName, int tabCount = issueLocationWithContext.beforeText().count(QLatin1Char('\t')); int locationLength = location.length == 0 ? 1 : location.length; - m_output.write(QString::fromLatin1(" ").repeated(issueLocationWithContext.beforeText().length() + m_output.write(QString::fromLatin1(" ").repeated(issueLocationWithContext.beforeText().size() - tabCount) + QString::fromLatin1("\t").repeated(tabCount) + QString::fromLatin1("^").repeated(locationLength) + QLatin1Char('\n')); @@ -278,9 +278,9 @@ void QQmlJSLogger::printFix(const FixSuggestion &fix) continue; m_output.write(u" "_s.repeated( - issueLocationWithContext.beforeText().length() - tabCount) + issueLocationWithContext.beforeText().size() - tabCount) + u"\t"_s.repeated(tabCount) - + u"^"_s.repeated(fixItem.replacementString.length()) + u'\n'); + + u"^"_s.repeated(fixItem.replacementString.size()) + u'\n'); } } diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h index 0302201eaf..9c89fd754a 100644 --- a/src/qmlcompiler/qqmljslogger_p.h +++ b/src/qmlcompiler/qqmljslogger_p.h @@ -201,6 +201,16 @@ public: m_categoryChanged[category] = true; } + bool isCategoryFatal(QQmlJSLoggerCategory category) const + { + return m_categoryFatal[category]; + } + void setCategoryFatal(QQmlJSLoggerCategory category, bool error) + { + m_categoryFatal[category] = error; + m_categoryChanged[category] = true; + } + bool wasCategoryChanged(QQmlJSLoggerCategory category) const { return m_categoryChanged[category]; @@ -257,6 +267,10 @@ private: bool m_categoryIgnored[QQmlJSLoggerCategory_Last + 1] = {}; bool m_categoryChanged[QQmlJSLoggerCategory_Last + 1] = {}; + // If true, triggers qFatal on documents with "pragma Strict" + // TODO: Works only for qmlCompiler category so far. + bool m_categoryFatal[QQmlJSLoggerCategory_Last + 1] = {}; + QList<Message> m_infos; QList<Message> m_warnings; QList<Message> m_errors; diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index 75f635d5d0..e2987984fb 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -40,7 +40,7 @@ QT_BEGIN_NAMESPACE class QQmlJSTypeResolver; class QQmlJSScope; -class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSMetaEnum +class QQmlJSMetaEnum { QStringList m_keys; QList<int> m_values; // empty if values unknown. @@ -98,7 +98,7 @@ public: } }; -class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSMetaMethod +class QQmlJSMetaMethod { public: enum Type { @@ -158,7 +158,7 @@ public: } void setParameterTypes(const QList<QSharedPointer<const QQmlJSScope>> &types) { - Q_ASSERT(types.length() == m_paramNames.length()); + Q_ASSERT(types.size() == m_paramNames.size()); m_paramTypes.clear(); for (const auto &type : types) m_paramTypes.append(type); @@ -262,7 +262,7 @@ private: bool m_isImplicitQmlPropertyChangeSignal = false; }; -class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSMetaProperty +class QQmlJSMetaProperty { QString m_propertyName; QString m_typeName; diff --git a/src/qmlcompiler/qqmljsresourcefilemapper.cpp b/src/qmlcompiler/qqmljsresourcefilemapper.cpp index b9ae292018..4213902fb3 100644 --- a/src/qmlcompiler/qqmljsresourcefilemapper.cpp +++ b/src/qmlcompiler/qqmljsresourcefilemapper.cpp @@ -101,7 +101,7 @@ void doFilter(const QList<QQmlJSResourceFileMapper::Entry> &qrcPathToFileSystemP if ((filter.flags & QQmlJSResourceFileMapper::Recurse) // Crude. But shall we really allow slashes in QRC file names? - || !candidate.mid(terminatedDirectory.length()).contains(u'/')) { + || !candidate.mid(terminatedDirectory.size()).contains(u'/')) { if (handler(*it)) return; } diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index 3e0cff0e52..03d47203ce 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -267,6 +267,9 @@ bool QQmlJSScope::causesImplicitComponentWrapping(const QQmlJSMetaProperty &prop Either because it has been implicitly wrapped, e.g. due to an assignment to a Component property, or because it is the first (and only) child of a Component. + For visitors: This method should only be called after implicit components + are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *) + was called. */ bool QQmlJSScope::isComponentRootElement() const { if (m_flags.testFlag(WrappedInImplicitComponent)) @@ -339,7 +342,7 @@ QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType( const QString outerTypeName = name.left(colonColon); const auto outerType = contextualTypes.constFind(outerTypeName); if (outerType != contextualTypes.constEnd()) { - for (const auto &innerType : qAsConst(outerType->scope->m_childScopes)) { + for (const auto &innerType : std::as_const(outerType->scope->m_childScopes)) { if (innerType->m_internalName == name) { if (usedTypes != nullptr) usedTypes->insert(name); @@ -394,10 +397,10 @@ QTypeRevision QQmlJSScope::resolveType( const auto paramTypeNames = it->parameterTypeNames(); QList<QSharedPointer<const QQmlJSScope>> paramTypes = it->parameterTypes(); - if (paramTypes.length() < paramTypeNames.length()) - paramTypes.resize(paramTypeNames.length()); + if (paramTypes.size() < paramTypeNames.size()) + paramTypes.resize(paramTypeNames.size()); - for (int i = 0, length = paramTypes.length(); i < length; ++i) { + for (int i = 0, length = paramTypes.size(); i < length; ++i) { auto ¶mType = paramTypes[i]; const auto paramTypeName = paramTypeNames[i]; if (!paramType && !paramTypeName.isEmpty()) { diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index 46aee20d90..6047d0c1d9 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -278,10 +278,30 @@ public: { using namespace Qt::StringLiterals; - QString suffix; - if (m_semantics == AccessSemantics::Reference) - suffix = u" *"_s; - return m_internalName + suffix; + switch (m_semantics) { + case AccessSemantics::Reference: + return m_internalName + " *"_L1; + case AccessSemantics::Value: + case AccessSemantics::Sequence: + break; + case AccessSemantics::None: + // If we got a namespace, it might still be a regular type, exposed as namespace. + // We may need to travel the inheritance chain all the way up to QObject to + // figure this out, since all other types may be exposed the same way. + for (QQmlJSScope::ConstPtr base = baseType(); base; base = base->baseType()) { + switch (base->accessSemantics()) { + case AccessSemantics::Reference: + return m_internalName + " *"_L1; + case AccessSemantics::Value: + case AccessSemantics::Sequence: + return m_internalName; + case AccessSemantics::None: + break; + } + } + break; + } + return m_internalName; } // This returns a more user readable version of internalName / baseTypeName diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp index 719eeaef31..086b778263 100644 --- a/src/qmlcompiler/qqmljsshadowcheck.cpp +++ b/src/qmlcompiler/qqmljsshadowcheck.cpp @@ -36,11 +36,14 @@ void QQmlJSShadowCheck::run( m_function = function; m_error = error; m_state = initialState(function); - decode(m_function->code.constData(), static_cast<uint>(m_function->code.length())); + decode(m_function->code.constData(), static_cast<uint>(m_function->code.size())); } void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex) { + if (!m_state.readsRegister(Accumulator)) + return; // enum lookup cannot be shadowed. + auto accumulatorIn = m_state.registers.find(Accumulator); if (accumulatorIn != m_state.registers.end()) checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->stringForIndex(nameIndex)); @@ -48,6 +51,9 @@ void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex) void QQmlJSShadowCheck::generate_GetLookup(int index) { + if (!m_state.readsRegister(Accumulator)) + return; // enum lookup cannot be shadowed. + auto accumulatorIn = m_state.registers.find(Accumulator); if (accumulatorIn != m_state.registers.end()) checkShadowing(accumulatorIn.value(), m_jsUnitGenerator->lookupName(index)); diff --git a/src/qmlcompiler/qqmljsstreamwriter.cpp b/src/qmlcompiler/qqmljsstreamwriter.cpp index 093d15dbd4..e435b8df92 100644 --- a/src/qmlcompiler/qqmljsstreamwriter.cpp +++ b/src/qmlcompiler/qqmljsstreamwriter.cpp @@ -161,7 +161,7 @@ void QQmlJSStreamWriter::flushPotentialLinesWithNewlines() { if (m_maybeOneline) m_stream->write("\n"); - for (const QByteArray &line : qAsConst(m_pendingLines)) { + for (const QByteArray &line : std::as_const(m_pendingLines)) { writeIndent(); m_stream->write(line); m_stream->write("\n"); diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index bab06d1035..9469380bba 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -50,7 +50,7 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run( m_state.State::operator=(initialState(m_function)); reset(); - decode(m_function->code.constData(), static_cast<uint>(m_function->code.length())); + decode(m_function->code.constData(), static_cast<uint>(m_function->code.size())); // If we have found unresolved backwards jumps, we need to start over with a fresh state. // Mind that m_jumpOriginRegisterStateByTargetInstructionOffset is retained in that case. @@ -287,20 +287,20 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM if (isMethod) { if (isCallingProperty(m_function->qmlScope, name)) return; - } else if (isMissingPropertyType(m_function->qmlScope, name)) { + } else if (propertyResolution(m_function->qmlScope, name) != PropertyMissing) { return; } std::optional<FixSuggestion> suggestion; auto childScopes = m_function->qmlScope->childScopes(); - for (qsizetype i = 0; i < m_function->qmlScope->childScopes().length(); i++) { + for (qsizetype i = 0; i < m_function->qmlScope->childScopes().size(); i++) { auto &scope = childScopes[i]; if (location.offset > scope->sourceLocation().offset) { - if (i + 1 < childScopes.length() + if (i + 1 < childScopes.size() && childScopes.at(i + 1)->sourceLocation().offset < location.offset) continue; - if (scope->childScopes().length() == 0) + if (scope->childScopes().size() == 0) continue; const auto jsId = scope->childScopes().first()->findJSIdentifier(name); @@ -481,18 +481,20 @@ bool QQmlJSTypePropagator::isRestricted(const QString &propertyName) const } // Only to be called once a lookup has already failed -bool QQmlJSTypePropagator::isMissingPropertyType(QQmlJSScope::ConstPtr scope, - const QString &propertyName) const +QQmlJSTypePropagator::PropertyResolution QQmlJSTypePropagator::propertyResolution( + QQmlJSScope::ConstPtr scope, const QString &propertyName) const { auto property = scope->property(propertyName); if (!property.isValid()) - return false; + return PropertyMissing; QString errorType; if (property.type().isNull()) errorType = u"found"_s; else if (!property.type()->isFullyResolved()) errorType = u"fully resolved"_s; + else + return PropertyFullyResolved; Q_ASSERT(!errorType.isEmpty()); @@ -501,7 +503,7 @@ bool QQmlJSTypePropagator::isMissingPropertyType(QQmlJSScope::ConstPtr scope, .arg(property.typeName(), propertyName, errorType), Log_Type, getCurrentSourceLocation()); - return true; + return PropertyTypeUnresolved; } bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const @@ -664,6 +666,10 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index) addReadAccumulator(jsValue); addReadRegister(base, jsValue); addReadRegister(index, jsValue); + + // Writing to a JS array can have side effects all over the place since it's + // passed by reference. + m_state.setHasSideEffects(true); return; } @@ -777,7 +783,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName) auto baseType = m_typeResolver->containedType(m_state.accumulatorIn()); // Warn separately when a property is only not found because of a missing type - if (isMissingPropertyType(baseType, propertyName)) + if (propertyResolution(baseType, propertyName) != PropertyMissing) return; std::optional<FixSuggestion> fixSuggestion; @@ -808,7 +814,7 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName) return; } - if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().length() != 1) { + if (m_state.accumulatorOut().isMethod() && m_state.accumulatorOut().method().size() != 1) { setError(u"Cannot determine overloaded method on loadProperty"_s); return; } @@ -1026,8 +1032,9 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar if (m_passManager != nullptr) { // TODO: Should there be an analyzeCall() in the future? (w. corresponding onCall in Pass) - m_passManager->analyzeRead(m_typeResolver->containedType(m_state.accumulatorIn()), - propertyName, m_function->qmlScope, getCurrentSourceLocation()); + m_passManager->analyzeRead( + m_typeResolver->containedType(callBase), + propertyName, m_function->qmlScope, getCurrentSourceLocation()); } addReadRegister(base, callBase); @@ -1159,8 +1166,8 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods, const QQmlJSMetaMethod match = bestMatchForCall(methods, argc, argv, &errors); if (!match.isValid()) { - Q_ASSERT(errors.length() == methods.length()); - if (methods.length() == 1) + Q_ASSERT(errors.size() == methods.size()); + if (methods.size() == 1) setError(errors.first()); else setError(u"No matching override found. Candidates:\n"_s + errors.join(u'\n')); @@ -1180,7 +1187,7 @@ void QQmlJSTypePropagator::propagateCall(const QList<QQmlJSMetaMethod> &methods, m_state.setHasSideEffects(true); const auto types = match.parameterTypes(); for (int i = 0; i < argc; ++i) { - if (i < types.length()) { + if (i < types.size()) { const QQmlJSScope::ConstPtr type = match.isJavaScriptFunction() ? m_typeResolver->jsValueType() : QQmlJSScope::ConstPtr(types.at(i)); @@ -2156,12 +2163,12 @@ QString QQmlJSTypePropagator::registerName(int registerIndex) const if (registerIndex == Accumulator) return u"accumulator"_s; if (registerIndex >= FirstArgument - && registerIndex < FirstArgument + m_function->argumentTypes.count()) { + && registerIndex < FirstArgument + m_function->argumentTypes.size()) { return u"argument %1"_s.arg(registerIndex - FirstArgument); } return u"temporary register %1"_s.arg( - registerIndex - FirstArgument - m_function->argumentTypes.count()); + registerIndex - FirstArgument - m_function->argumentTypes.size()); } QQmlJSRegisterContent QQmlJSTypePropagator::checkedInputRegister(int reg) diff --git a/src/qmlcompiler/qqmljstypepropagator_p.h b/src/qmlcompiler/qqmljstypepropagator_p.h index 30967413b9..06fc2c9793 100644 --- a/src/qmlcompiler/qqmljstypepropagator_p.h +++ b/src/qmlcompiler/qqmljstypepropagator_p.h @@ -191,7 +191,14 @@ private: void checkDeprecated(QQmlJSScope::ConstPtr scope, const QString &name, bool isMethod) const; bool isRestricted(const QString &propertyName) const; bool isCallingProperty(QQmlJSScope::ConstPtr scope, const QString &name) const; - bool isMissingPropertyType(QQmlJSScope::ConstPtr scope, const QString &type) const; + + enum PropertyResolution { + PropertyMissing, + PropertyTypeUnresolved, + PropertyFullyResolved + }; + + PropertyResolution propertyResolution(QQmlJSScope::ConstPtr scope, const QString &type) const; QQmlJS::SourceLocation getCurrentSourceLocation() const; QQmlJS::SourceLocation getCurrentBindingSourceLocation() const; diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 046a79ce01..eeb5f605f0 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -38,6 +38,10 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer) m_varType = builtinTypes[u"QVariant"_s].scope; m_jsValueType = builtinTypes[u"QJSValue"_s].scope; + QQmlJSScope::Ptr emptyType = QQmlJSScope::create(); + emptyType->setAccessSemantics(QQmlJSScope::AccessSemantics::None); + m_emptyType = emptyType; + QQmlJSScope::Ptr emptyListType = QQmlJSScope::create(); emptyListType->setInternalName(u"void*"_s); emptyListType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence); @@ -70,7 +74,7 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer) m_jsGlobalObject = importer->jsGlobalObject(); auto numberMethods = m_jsGlobalObject->methods(u"Number"_s); - Q_ASSERT(numberMethods.length() == 1); + Q_ASSERT(numberMethods.size() == 1); m_numberPrototype = numberMethods[0].returnType()->baseType(); Q_ASSERT(m_numberPrototype); Q_ASSERT(m_numberPrototype->internalName() == u"NumberPrototype"_s); @@ -169,7 +173,10 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeForConst(QV4::ReturnedValue rv) co return realType(); if (value.isNull()) - return jsPrimitiveType(); + return nullType(); + + if (value.isEmpty()) + return emptyType(); return {}; } @@ -396,6 +403,64 @@ QQmlJSRegisterContent QQmlJSTypeResolver::transformed( return {}; } +QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName( + const QString &name, const QQmlJSScope::ConstPtr &scopeType, + bool hasObjectModulePrefix) const +{ + QQmlJSScope::ConstPtr type = typeForName(name); + if (!type) + return QQmlJSRegisterContent(); + + if (type->isSingleton()) + return QQmlJSRegisterContent::create(storedType(type), type, + QQmlJSRegisterContent::Singleton, scopeType); + + if (type->isScript()) + return QQmlJSRegisterContent::create(storedType(type), type, + QQmlJSRegisterContent::Script, scopeType); + + if (const auto attached = type->attachedType()) { + if (!genericType(attached)) { + m_logger->log(u"Cannot resolve generic base of attached %1"_s.arg( + attached->internalName()), + Log_Compiler, attached->sourceLocation()); + return {}; + } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { + m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_s.arg( + type->internalName()), + Log_Compiler, type->sourceLocation()); + return {}; + } else { + // We don't know yet whether we need the attached or the plain object. In direct + // mode, we will figure this out using the scope type and access any enums of the + // plain type directly. In indirect mode, we can use enum lookups. + return QQmlJSRegisterContent::create( + storedType(attached), attached, + hasObjectModulePrefix + ? QQmlJSRegisterContent::ObjectAttached + : QQmlJSRegisterContent::ScopeAttached, type); + } + } + + switch (type->accessSemantics()) { + case QQmlJSScope::AccessSemantics::None: + case QQmlJSScope::AccessSemantics::Reference: + // A plain reference to a non-singleton, non-attached type. + // We may still need the plain type reference for enum lookups, + // Store it as QMetaObject. + // This only works with namespaces and object types. + return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(), + QQmlJSRegisterContent::MetaType, type); + case QQmlJSScope::AccessSemantics::Sequence: + case QQmlJSScope::AccessSemantics::Value: + // This is not actually a type reference. You cannot get the metaobject + // of a value type in QML and sequences don't even have metaobjects. + break; + } + + return QQmlJSRegisterContent(); +} + QQmlJSRegisterContent QQmlJSTypeResolver::original(const QQmlJSRegisterContent &type) const { return transformed(type, &QQmlJSTypeResolver::originalType); @@ -643,7 +708,8 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt return m_metaObjectType; if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) { - for (auto base = type; base; base = base->baseType()) { + QString unresolvedBaseTypeName; + for (auto base = type; base;) { // QObject and QQmlComponent are the two required base types. // Any QML type system has to define those, or use the ones from builtins. // As QQmlComponent is derived from QObject, we can restrict ourselves to the latter. @@ -655,10 +721,19 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt && base->internalName() == u"QQmlComponent"_s) { return base; } + + if (auto baseBase = base->baseType()) { + base = baseBase; + } else { + unresolvedBaseTypeName = base->baseTypeName(); + break; + } } - m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent"_s.arg( - type->internalName()), + m_logger->log(u"Object type %1 is not derived from QObject or QQmlComponent. " + "You may need to fully qualify all names in C++ so that moc can see them. " + "You may also need to add qt_extract_metatypes(<target containing %2>)."_s + .arg(type->internalName(), unresolvedBaseTypeName), Log_Compiler, type->sourceLocation()); // Reference types that are not QObject or QQmlComponent are likely JavaScript objects. @@ -685,6 +760,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt return type; if (const QQmlJSScope::ConstPtr valueType = type->valueType()) return listType(genericType(valueType), UseQObjectList); + return m_variantListType; } return m_varType; @@ -802,51 +878,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr } } - if (QQmlJSScope::ConstPtr type = typeForName(name)) { - if (type->isSingleton()) - return QQmlJSRegisterContent::create(storedType(type), type, - QQmlJSRegisterContent::Singleton); - - if (type->isScript()) - return QQmlJSRegisterContent::create(storedType(type), type, - QQmlJSRegisterContent::Script); - - if (const auto attached = type->attachedType()) { - if (!genericType(attached)) { - m_logger->log(u"Cannot resolve generic base of attached %1"_s.arg( - attached->internalName()), - Log_Compiler, attached->sourceLocation()); - return {}; - } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { - m_logger->log(u"Cannot retrieve attached object for non-reference type %1"_s.arg( - type->internalName()), - Log_Compiler, type->sourceLocation()); - return {}; - } else { - // We don't know yet whether we need the attached or the plain object. In direct - // mode, we will figure this out using the scope type and access any enums of the - // plain type directly. In indirect mode, we can use enum lookups. - return QQmlJSRegisterContent::create(storedType(attached), attached, - QQmlJSRegisterContent::ScopeAttached, type); - } - } - - switch (type->accessSemantics()) { - case QQmlJSScope::AccessSemantics::None: - case QQmlJSScope::AccessSemantics::Reference: - // A plain reference to a non-singleton, non-attached type. - // We may still need the plain type reference for enum lookups, - // Store it as QMetaObject. - // This only works with namespaces and object types. - return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(), - QQmlJSRegisterContent::MetaType, type); - case QQmlJSScope::AccessSemantics::Sequence: - case QQmlJSScope::AccessSemantics::Value: - // This is not actually a type reference. You cannot get the metaobject - // of a value type in QML and sequences don't even have metaobjects. - break; - } - } + QQmlJSRegisterContent result = referenceTypeForName(name); + if (result.isValid()) + return result; if (m_jsGlobalObject->hasProperty(name)) { return QQmlJSRegisterContent::create(jsValueType(), m_jsGlobalObject->property(name), @@ -987,6 +1021,10 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr { QQmlJSRegisterContent result; + // If we got a plain type reference we have to check the enums of the _scope_. + if (equals(type, metaObjectType())) + return {}; + if (equals(type, jsValueType())) { QQmlJSMetaProperty prop; prop.setPropertyName(name); @@ -1134,34 +1172,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent return {}; } - if (QQmlJSScope::ConstPtr result = typeForName(name)) { - QQmlJSScope::ConstPtr attached = result->attachedType(); - if (attached && genericType(attached)) { - return QQmlJSRegisterContent::create( - storedType(attached), attached, - type.variant() == QQmlJSRegisterContent::ObjectModulePrefix - ? QQmlJSRegisterContent::ObjectAttached - : QQmlJSRegisterContent::ScopeAttached, - result); - } - - if (result->isSingleton()) { - return QQmlJSRegisterContent::create( - storedType(result), result, - QQmlJSRegisterContent::Singleton, type.scopeType()); - } - - if (result->isScript()) { - return QQmlJSRegisterContent::create( - storedType(result), result, - QQmlJSRegisterContent::Script, type.scopeType()); - } - - return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(), - QQmlJSRegisterContent::MetaType, result); - } - - return {}; + return referenceTypeForName( + name, type.scopeType(), + type.variant() == QQmlJSRegisterContent::ObjectModulePrefix); } if (type.isConversion()) { const auto result = memberType(type.conversionResult(), name); @@ -1243,7 +1256,7 @@ bool QQmlJSTypeResolver::registerContains(const QQmlJSRegisterContent ®, : equals(type, prop.type()); } if (reg.isEnumeration()) - return equals(type, intType()); + return equals(type, reg.enumeration().type()); if (reg.isMethod()) return equals(type, jsValueType()); return false; diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index e05f5b3757..a56a85bbc6 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -39,6 +39,7 @@ public: void init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program); QQmlJSScope::ConstPtr voidType() const { return m_voidType; } + QQmlJSScope::ConstPtr emptyType() const { return m_emptyType; } QQmlJSScope::ConstPtr emptyListType() const { return m_emptyListType; } QQmlJSScope::ConstPtr nullType() const { return m_nullType; } QQmlJSScope::ConstPtr realType() const { return m_realType; } @@ -167,8 +168,14 @@ protected: const QQmlJSRegisterContent &origin, QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const; + QQmlJSRegisterContent referenceTypeForName( + const QString &name, + const QQmlJSScope::ConstPtr &scopeType = QQmlJSScope::ConstPtr(), + bool hasObjectModuelPrefix = false) const; + QQmlJSScope::ConstPtr m_voidType; QQmlJSScope::ConstPtr m_emptyListType; + QQmlJSScope::ConstPtr m_emptyType; QQmlJSScope::ConstPtr m_nullType; QQmlJSScope::ConstPtr m_numberPrototype; QQmlJSScope::ConstPtr m_realType; diff --git a/src/qmlcompiler/qqmljsutils.cpp b/src/qmlcompiler/qqmljsutils.cpp index c07c0b6845..8ef11444d7 100644 --- a/src/qmlcompiler/qqmljsutils.cpp +++ b/src/qmlcompiler/qqmljsutils.cpp @@ -25,17 +25,23 @@ resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property, QQmlJSUtils::ResolvedAlias result {}; result.owner = owner; - for (QQmlJSMetaProperty nextProperty = property; nextProperty.isAlias();) { - - // this is a special (seemingly useless) section which is necessary when - // we have an alias pointing to an alias. this way we avoid a check - // whether a property is an alias at the very end of the loop body + // TODO: one could optimize the generated alias code for aliases pointing to aliases + // e.g., if idA.myAlias -> idB.myAlias2 -> idC.myProp, then one could directly generate + // idA.myProp as pointing to idC.myProp. + // This gets complicated when idB.myAlias is in a different Component than where the + // idA.myAlias is defined: scopeForId currently only contains the ids of the current + // component and alias resolution on the ids of a different component fails then. + if (QQmlJSMetaProperty nextProperty = property; nextProperty.isAlias()) { QQmlJSScope::ConstPtr resultOwner = result.owner; result = QQmlJSUtils::ResolvedAlias {}; visitor.reset(); auto aliasExprBits = nextProperty.aliasExpression().split(u'.'); + // do not crash on invalid aliasexprbits when accessing aliasExprBits[0] + if (aliasExprBits.size() < 1) + return {}; + // resolve id first: resultOwner = scopeForId(aliasExprBits[0], resultOwner); if (!resultOwner) @@ -46,10 +52,8 @@ resolveAlias(ScopeForId scopeForId, const QQmlJSMetaProperty &property, aliasExprBits.removeFirst(); // Note: for simplicity, remove the <id> result.owner = resultOwner; result.kind = QQmlJSUtils::AliasTarget_Object; - // reset the property to avoid endless loop when aliasExprBits is empty - nextProperty = QQmlJSMetaProperty {}; - for (const QString &bit : qAsConst(aliasExprBits)) { + for (const QString &bit : std::as_const(aliasExprBits)) { nextProperty = resultOwner->property(bit); if (!nextProperty.isValid()) return {}; @@ -96,7 +100,7 @@ std::optional<FixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput, QQmlJS::SourceLocation location) { QString shortestDistanceWord; - int shortestDistance = userInput.length(); + int shortestDistance = userInput.size(); // Most of the time the candidates are keys() from QHash, which means that // running this function in the seemingly same setup might yield different @@ -114,14 +118,14 @@ std::optional<FixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput, * Roughly based on * https://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows. */ - QList<int> v0(candidate.length() + 1); - QList<int> v1(candidate.length() + 1); + QList<int> v0(candidate.size() + 1); + QList<int> v1(candidate.size() + 1); std::iota(v0.begin(), v0.end(), 0); - for (qsizetype i = 0; i < userInput.length(); i++) { + for (qsizetype i = 0; i < userInput.size(); i++) { v1[0] = i + 1; - for (qsizetype j = 0; j < candidate.length(); j++) { + for (qsizetype j = 0; j < candidate.size(); j++) { int deletionCost = v0[j + 1] + 1; int insertionCost = v1[j] + 1; int substitutionCost = userInput[i] == candidate[j] ? v0[j] : v0[j] + 1; @@ -130,7 +134,7 @@ std::optional<FixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput, std::swap(v0, v1); } - int distance = v0[candidate.length()]; + int distance = v0[candidate.size()]; if (distance < shortestDistance) { shortestDistanceWord = candidate; shortestDistance = distance; @@ -138,7 +142,7 @@ std::optional<FixSuggestion> QQmlJSUtils::didYouMean(const QString &userInput, } if (shortestDistance - < std::min(std::max(userInput.length() / 2, qsizetype(3)), userInput.length())) { + < std::min(std::max(userInput.size() / 2, qsizetype(3)), userInput.size())) { return FixSuggestion { { FixSuggestion::Fix { u"Did you mean \"%1\"?"_s.arg(shortestDistanceWord), location, shortestDistanceWord } } }; diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h index 8a1ee9c287..040e996cd4 100644 --- a/src/qmlcompiler/qqmljsutils_p.h +++ b/src/qmlcompiler/qqmljsutils_p.h @@ -104,7 +104,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils { if (handlerName.startsWith(u"on") && handlerName.size() > 2) { QString signal = handlerName.mid(2).toString(); - for (int i = 0; i < signal.length(); ++i) { + for (int i = 0; i < signal.size(); ++i) { QChar &ch = signal[i]; if (ch.isLower()) return {}; |