diff options
| author | Olivier De Cannière <olivier.decanniere@qt.io> | 2023-05-05 09:30:27 +0200 |
|---|---|---|
| committer | Olivier De Cannière <olivier.decanniere@qt.io> | 2023-05-30 13:42:35 +0200 |
| commit | cdd7fe05f676ed1664a156beaf63093237a3beac (patch) | |
| tree | 8f7adccde1adc0e8404a96a895c5170b84f3f0cc | |
| parent | 65cb77165ba18442a524faf44f712ae26661965c (diff) | |
QQmlSA: Create an abstraction layer for static analysis
This patch adds abstractions for QML Elements, Bindings, Methods and
Properties. This abstraction layer avoids exposing internal details and
should be more suited for static analysis tasks. It is now possible to
write qmllint plugins without including private headers.
As a drive-by, change tst_qmllint:verifyJsRoot to open files in text
mode instead of binary. This fixes an issue where line endings cause
issues on Windows.
Fixes: QTBUG-102276
Change-Id: I6b6e53f1e0078734a18f3aa51807fbe875b375f0
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
49 files changed, 3563 insertions, 1086 deletions
diff --git a/src/plugins/qmllint/quick/quicklintplugin.cpp b/src/plugins/qmllint/quick/quicklintplugin.cpp index 1fa44f13be..8e496a6af8 100644 --- a/src/plugins/qmllint/quick/quicklintplugin.cpp +++ b/src/plugins/qmllint/quick/quicklintplugin.cpp @@ -3,21 +3,18 @@ #include "quicklintplugin.h" -#include <QtQmlCompiler/private/qqmljslogger_p.h> -#include <QtQmlCompiler/private/qqmljsutils_p.h> - QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -static constexpr LoggerWarningId quickLayoutPositioning { "Quick.layout-positioning" }; -static constexpr LoggerWarningId quickAttachedPropertyType { "Quick.attached-property-type" }; -static constexpr LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" }; -static constexpr LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" }; -static constexpr LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" }; -static constexpr LoggerWarningId quickPropertyChangesParsed { "Quick.property-changes-parsed" }; -static constexpr LoggerWarningId quickControlsAttachedPropertyReuse { "Quick.controls-attached-property-reuse" }; -static constexpr LoggerWarningId quickAttachedPropertyReuse { "Quick.attached-property-reuse" }; +static constexpr QQmlJS::LoggerWarningId quickLayoutPositioning { "Quick.layout-positioning" }; +static constexpr QQmlJS::LoggerWarningId quickAttachedPropertyType { "Quick.attached-property-type" }; +static constexpr QQmlJS::LoggerWarningId quickControlsNativeCustomize { "Quick.controls-native-customize" }; +static constexpr QQmlJS::LoggerWarningId quickAnchorCombinations { "Quick.anchor-combinations" }; +static constexpr QQmlJS::LoggerWarningId quickUnexpectedVarType { "Quick.unexpected-var-type" }; +static constexpr QQmlJS::LoggerWarningId quickPropertyChangesParsed { "Quick.property-changes-parsed" }; +static constexpr QQmlJS::LoggerWarningId quickControlsAttachedPropertyReuse { "Quick.controls-attached-property-reuse" }; +static constexpr QQmlJS::LoggerWarningId quickAttachedPropertyReuse { "Quick.attached-property-reuse" }; ForbiddenChildrenPropertyValidatorPass::ForbiddenChildrenPropertyValidatorPass( QQmlSA::PassManager *manager) @@ -37,11 +34,11 @@ void ForbiddenChildrenPropertyValidatorPass::addWarning(QAnyStringView moduleNam bool ForbiddenChildrenPropertyValidatorPass::shouldRun(const QQmlSA::Element &element) { - if (!element->parentScope()) + if (!element.parentScope()) return false; for (const auto pair : m_types.asKeyValueRange()) { - if (element->parentScope()->inherits(pair.first)) + if (element.parentScope().inherits(pair.first)) return true; } @@ -52,16 +49,16 @@ void ForbiddenChildrenPropertyValidatorPass::run(const QQmlSA::Element &element) { for (const auto elementPair : m_types.asKeyValueRange()) { const QQmlSA::Element &type = elementPair.first; - if (!element->parentScope()->inherits(type)) + if (!element.parentScope().inherits(type)) continue; for (const auto &warning : elementPair.second) { - if (!element->hasOwnPropertyBindings(warning.propertyName)) + if (!element.hasOwnPropertyBindings(warning.propertyName)) continue; - auto bindings = element->ownPropertyBindings(warning.propertyName); - - emitWarning(warning.message, quickLayoutPositioning, bindings.first->sourceLocation()); + const auto bindings = element.ownPropertyBindings(warning.propertyName); + const auto &firstBinding = bindings.constBegin().value(); + emitWarning(warning.message, quickLayoutPositioning, firstBinding.sourceLocation()); } break; } @@ -80,7 +77,7 @@ QString AttachedPropertyTypeValidatorPass::addWarning(TypeDescription attachType const QQmlSA::Element baseType = resolveType(attachType.module, attachType.name); - QString typeName = baseType->attachedTypeName(); + QString typeName = baseType.attachedTypeName(); for (const TypeDescription &desc : allowedTypes) { const QQmlSA::Element type = resolveType(desc.module, desc.name); @@ -97,28 +94,28 @@ QString AttachedPropertyTypeValidatorPass::addWarning(TypeDescription attachType void AttachedPropertyTypeValidatorPass::checkWarnings(const QQmlSA::Element &element, const QQmlSA::Element &scopeUsedIn, - const QQmlJS::SourceLocation &location) + const QQmlSA::SourceLocation &location) { - auto warning = m_attachedTypes.constFind(element->internalName()); + auto warning = m_attachedTypes.constFind(element.internalName()); if (warning == m_attachedTypes.cend()) return; for (const QQmlSA::Element &type : warning->allowedTypes) { - if (scopeUsedIn->inherits(type)) + if (scopeUsedIn.inherits(type)) return; } if (warning->allowInDelegate) { - if (scopeUsedIn->isPropertyRequired(u"index"_s) - || scopeUsedIn->isPropertyRequired(u"model"_s)) + if (scopeUsedIn.isPropertyRequired(u"index"_s) + || scopeUsedIn.isPropertyRequired(u"model"_s)) return; // If the scope is at the root level, we cannot know whether it will be used // as a delegate or not. - if (!scopeUsedIn->parentScope() || scopeUsedIn->parentScope()->internalName() == u"global"_s) - return; + if (!scopeUsedIn.parentScope() || scopeUsedIn.parentScope().internalName() == u"global"_s) + return; - for (const QQmlJSMetaPropertyBinding &binding : - scopeUsedIn->parentScope()->propertyBindings(u"delegate"_s)) { + for (const QQmlSA::Binding &binding : + scopeUsedIn.parentScope().propertyBindings(u"delegate"_s)) { if (!binding.hasObject()) continue; if (binding.objectType() == scopeUsedIn) @@ -131,7 +128,7 @@ void AttachedPropertyTypeValidatorPass::checkWarnings(const QQmlSA::Element &ele void AttachedPropertyTypeValidatorPass::onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) { @@ -140,17 +137,17 @@ void AttachedPropertyTypeValidatorPass::onBinding(const QQmlSA::Element &element Q_UNUSED(bindingScope) Q_UNUSED(value) - checkWarnings(bindingScope->baseType(), element, binding.sourceLocation()); + checkWarnings(bindingScope.baseType(), element, binding.sourceLocation()); } void AttachedPropertyTypeValidatorPass::onRead(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &readScope, - QQmlJS::SourceLocation location) + QQmlSA::SourceLocation location) { // If the attachment does not have such a property or method then // it's either a more general error or an enum. Enums are fine. - if (element->hasProperty(propertyName) || element->hasMethod(propertyName)) + if (element.hasProperty(propertyName) || element.hasMethod(propertyName)) checkWarnings(element, readScope, location); } @@ -158,7 +155,7 @@ void AttachedPropertyTypeValidatorPass::onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) + QQmlSA::SourceLocation location) { Q_UNUSED(propertyName) Q_UNUSED(value) @@ -209,7 +206,7 @@ ControlsNativeValidatorPass::ControlsNativeValidatorPass(QQmlSA::PassManager *ma if (type.isNull()) continue; - element.inheritsControl = !element.isControl && type->inherits(control); + element.inheritsControl = !element.isControl && type.inherits(control); element.element = type; } @@ -225,7 +222,7 @@ bool ControlsNativeValidatorPass::shouldRun(const QQmlSA::Element &element) // If our element inherits control, we don't have to individually check for them here. if (controlElement.inheritsControl) continue; - if (element->inherits(controlElement.element)) + if (element.inherits(controlElement.element)) return true; } return false; @@ -234,16 +231,16 @@ bool ControlsNativeValidatorPass::shouldRun(const QQmlSA::Element &element) void ControlsNativeValidatorPass::run(const QQmlSA::Element &element) { for (const ControlElement &controlElement : m_elements) { - if (element->inherits(controlElement.element)) { + if (element.inherits(controlElement.element)) { for (const QString &propertyName : controlElement.restrictedProperties) { - if (element->hasOwnPropertyBindings(propertyName)) { + if (element.hasOwnPropertyBindings(propertyName)) { emitWarning(QStringLiteral("Not allowed to override \"%1\" because native " "styles cannot be customized: See " "https://doc-snapshots.qt.io/qt6-dev/" "qtquickcontrols-customize.html#customization-" "reference for more information.") .arg(propertyName), - quickControlsNativeCustomize, element->sourceLocation()); + quickControlsNativeCustomize, element.sourceLocation()); } } // Since all the different types we have rules for don't inherit from each other (except @@ -263,8 +260,8 @@ AnchorsValidatorPass::AnchorsValidatorPass(QQmlSA::PassManager *manager) bool AnchorsValidatorPass::shouldRun(const QQmlSA::Element &element) { - return !m_item.isNull() && element->inherits(m_item) - && element->hasOwnPropertyBindings(u"anchors"_s); + return !m_item.isNull() && element.inherits(m_item) + && element.hasOwnPropertyBindings(u"anchors"_s); } void AnchorsValidatorPass::run(const QQmlSA::Element &element) @@ -276,21 +273,22 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element) u"top"_s, u"bottom"_s, u"verticalCenter"_s, u"baseline"_s }; - QList<QQmlJSMetaPropertyBinding> anchorBindings = element->propertyBindings(u"anchors"_s); + QList<QQmlSA::Binding> anchorBindings = element.propertyBindings(u"anchors"_s); for (qsizetype i = anchorBindings.size() - 1; i >= 0; i--) { auto groupType = anchorBindings[i].groupType(); - if (groupType == nullptr) + if (groupType.isNull()) continue; for (const QString &name : properties) { - auto pair = groupType->ownPropertyBindings(name); - if (pair.first == pair.second) + + const auto &propertyBindings = groupType.ownPropertyBindings(name); + if (propertyBindings.begin() == propertyBindings.end()) continue; + bool isUndefined = false; - for (auto it = pair.first; it != pair.second; it++) { - if (it->bindingType() == QQmlJSMetaPropertyBinding::Script - && it->scriptValueType() == QQmlJSMetaPropertyBinding::ScriptValue_Undefined) { + for (const auto &propertyBinding : propertyBindings) { + if (propertyBinding.hasUndefinedScriptValue()) { isUndefined = true; break; } @@ -303,14 +301,15 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element) } } - auto ownSourceLocation = [&](QStringList properties) { - QQmlJS::SourceLocation warnLoc; + auto ownSourceLocation = [&](QStringList properties) -> QQmlSA::SourceLocation { + QQmlSA::SourceLocation warnLoc; + for (const QString &name : properties) { if (bindings[name] & Own) { - QQmlSA::Element groupType = anchorBindings[0].groupType(); - auto bindingRange = groupType->ownPropertyBindings(name); - Q_ASSERT(bindingRange.first != bindingRange.second); - warnLoc = bindingRange.first->sourceLocation(); + QQmlSA::Element groupType = QQmlSA::Element{ anchorBindings[0].groupType() }; + auto bindings = groupType.ownPropertyBindings(name); + Q_ASSERT(bindings.begin() != bindings.end()); + warnLoc = bindings.begin().value().sourceLocation(); break; } } @@ -318,7 +317,7 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element) }; if ((bindings[u"left"_s] & bindings[u"right"_s] & bindings[u"horizontalCenter"_s]) & Exists) { - QQmlJS::SourceLocation warnLoc = + QQmlSA::SourceLocation warnLoc = ownSourceLocation({ u"left"_s, u"right"_s, u"horizontalCenter"_s }); if (warnLoc.isValid()) { @@ -329,7 +328,7 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element) } if ((bindings[u"top"_s] & bindings[u"bottom"_s] & bindings[u"verticalCenter"_s]) & Exists) { - QQmlJS::SourceLocation warnLoc = + QQmlSA::SourceLocation warnLoc = ownSourceLocation({ u"top"_s, u"bottom"_s, u"verticalCenter"_s }); if (warnLoc.isValid()) { emitWarning("Cannot specify top, bottom, and verticalCenter anchors at the same time.", @@ -339,7 +338,7 @@ void AnchorsValidatorPass::run(const QQmlSA::Element &element) if ((bindings[u"baseline"_s] & (bindings[u"bottom"_s] | bindings[u"verticalCenter"_s])) & Exists) { - QQmlJS::SourceLocation warnLoc = + QQmlSA::SourceLocation warnLoc = ownSourceLocation({ u"baseline"_s, u"bottom"_s, u"verticalCenter"_s }); if (warnLoc.isValid()) { emitWarning("Baseline anchor cannot be used in conjunction with top, bottom, or " @@ -357,31 +356,30 @@ ControlsSwipeDelegateValidatorPass::ControlsSwipeDelegateValidatorPass(QQmlSA::P bool ControlsSwipeDelegateValidatorPass::shouldRun(const QQmlSA::Element &element) { - return !m_swipeDelegate.isNull() && element->inherits(m_swipeDelegate); + return !m_swipeDelegate.isNull() && element.inherits(m_swipeDelegate); } void ControlsSwipeDelegateValidatorPass::run(const QQmlSA::Element &element) { for (const auto &property : { u"background"_s, u"contentItem"_s }) { - auto bindings = element->ownPropertyBindings(property); - for (auto it = bindings.first; it != bindings.second; it++) { - if (!it->hasObject()) + for (const auto &binding : element.ownPropertyBindings(property)) { + if (!binding.hasObject()) continue; - const QQmlSA::Element element = it->objectType(); - const auto bindings = element->propertyBindings(u"anchors"_s); + const QQmlSA::Element element = QQmlSA::Element{ binding.objectType() }; + const auto &bindings = element.propertyBindings(u"anchors"_s); if (bindings.isEmpty()) continue; - if (bindings.first().bindingType() != QQmlJSMetaPropertyBinding::GroupProperty) + if (bindings.first().bindingType() != QQmlSA::BindingType::GroupProperty) continue; auto anchors = bindings.first().groupType(); for (const auto &disallowed : { u"fill"_s, u"centerIn"_s, u"left"_s, u"right"_s }) { - if (anchors->hasPropertyBindings(disallowed)) { - QQmlJS::SourceLocation location; - auto ownBindings = anchors->ownPropertyBindings(disallowed); - if (ownBindings.first != ownBindings.second) { - location = ownBindings.first->sourceLocation(); + if (anchors.hasPropertyBindings(disallowed)) { + QQmlSA::SourceLocation location; + const auto &ownBindings = anchors.ownPropertyBindings(disallowed); + if (ownBindings.begin() != ownBindings.end()) { + location = ownBindings.begin().value().sourceLocation(); } emitWarning( @@ -395,30 +393,31 @@ void ControlsSwipeDelegateValidatorPass::run(const QQmlSA::Element &element) } } - auto swipe = element->ownPropertyBindings(u"swipe"_s); - if (swipe.first == swipe.second) + const auto &swipe = element.ownPropertyBindings(u"swipe"_s); + if (swipe.begin() == swipe.end()) return; - if (swipe.first->bindingType() != QQmlJSMetaPropertyBinding::GroupProperty) + const auto &firstSwipe = swipe.begin().value(); + if (firstSwipe.bindingType() != QQmlSA::BindingType::GroupProperty) return; - auto group = swipe.first->groupType(); + auto group = firstSwipe.groupType(); - const std::array ownDirBindings = { group->ownPropertyBindings(u"right"_s), - group->ownPropertyBindings(u"left"_s), - group->ownPropertyBindings(u"behind"_s) }; + const std::array ownDirBindings = { group.ownPropertyBindings(u"right"_s), + group.ownPropertyBindings(u"left"_s), + group.ownPropertyBindings(u"behind"_s) }; auto ownBindingIterator = std::find_if(ownDirBindings.begin(), ownDirBindings.end(), - [](const auto &pair) { return pair.first != pair.second; }); + [](const auto &bindings) { return bindings.begin() != bindings.end(); }); if (ownBindingIterator == ownDirBindings.end()) return; - if (group->hasPropertyBindings(u"behind"_s) - && (group->hasPropertyBindings(u"right"_s) || group->hasPropertyBindings(u"left"_s))) { + if (group.hasPropertyBindings(u"behind"_s) + && (group.hasPropertyBindings(u"right"_s) || group.hasPropertyBindings(u"left"_s))) { emitWarning("SwipeDelegate: Cannot set both behind and left/right properties", - quickAnchorCombinations, ownBindingIterator->first->sourceLocation()); + quickAnchorCombinations, ownBindingIterator->begin().value().sourceLocation()); } } @@ -427,7 +426,7 @@ VarBindingTypeValidatorPass::VarBindingTypeValidatorPass( const QMultiHash<QString, TypeDescription> &expectedPropertyTypes) : QQmlSA::PropertyPass(manager) { - QMultiHash<QString, QQmlJSScope::ConstPtr> propertyTypes; + QMultiHash<QString, QQmlSA::Element> propertyTypes; for (const auto pair : expectedPropertyTypes.asKeyValueRange()) { QQmlSA::Element propType; @@ -437,9 +436,7 @@ VarBindingTypeValidatorPass::VarBindingTypeValidatorPass( if (propType.isNull()) continue; } else { - auto scope = QQmlJSScope::create(); - scope->setInternalName(pair.second.name); - propType = scope; + propType = QQmlSA::Element{ pair.second.name }; } propertyTypes.insert(pair.first, propType); @@ -450,7 +447,7 @@ VarBindingTypeValidatorPass::VarBindingTypeValidatorPass( void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) { @@ -467,14 +464,14 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element, if (!value.isNull()) { bindingType = value; } else { - if (QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType())) { + if (QQmlSA::Binding::isLiteralBinding(binding.bindingType())) { bindingType = resolveLiteralType(binding); } else { switch (binding.bindingType()) { - case QQmlJSMetaPropertyBinding::Object: - bindingType = binding.objectType(); + case QQmlSA::BindingType::Object: + bindingType = QQmlSA::Element{ binding.objectType() }; break; - case QQmlJSMetaPropertyBinding::Script: + case QQmlSA::BindingType::Script: break; default: return; @@ -483,11 +480,11 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element, } if (std::find_if(range.first, range.second, - [&](const QQmlSA::Element &scope) { return bindingType->inherits(scope); }) + [&](const QQmlSA::Element &scope) { return bindingType.inherits(scope); }) == range.second) { - const bool bindingTypeIsComposite = bindingType->isComposite(); - if (bindingTypeIsComposite && !bindingType->baseType()) { + const bool bindingTypeIsComposite = bindingType.isComposite(); + if (bindingTypeIsComposite && !bindingType.baseType()) { /* broken module or missing import, there is nothing we can really check here, as something is amiss. We simply skip this binding, and assume that whatever @@ -496,14 +493,13 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element, */ return; } - const QString bindingTypeName = QQmlJSScope::prettyName( - bindingTypeIsComposite - ? bindingType->baseType()->internalName() - : bindingType->internalName()); + const QString bindingTypeName = QQmlSA::Element::prettyName( + bindingTypeIsComposite ? bindingType.baseType().internalName() + : bindingType.internalName()); QStringList expectedTypeNames; for (auto it = range.first; it != range.second; it++) - expectedTypeNames << QQmlJSScope::prettyName(it.value()->internalName()); + expectedTypeNames << QQmlSA::Element::prettyName(it.value().internalName()); emitWarning(u"Unexpected type for property \"%1\" expected %2 got %3"_s.arg( propertyName, expectedTypeNames.join(u", "_s), bindingTypeName), @@ -511,9 +507,9 @@ void VarBindingTypeValidatorPass::onBinding(const QQmlSA::Element &element, } } -void AttachedPropertyReuse::onRead( - const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) +void AttachedPropertyReuse::onRead(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Element &readScope, + QQmlSA::SourceLocation location) { const auto range = usedAttachedTypes.equal_range(readScope); const auto attachedTypeAndLocation = std::find_if( @@ -521,16 +517,15 @@ void AttachedPropertyReuse::onRead( return elementAndLocation.element == element; }); if (attachedTypeAndLocation != range.second) { - const QQmlJS::SourceLocation attachedLocation = attachedTypeAndLocation->location; + const QQmlSA::SourceLocation attachedLocation = attachedTypeAndLocation->location; // Ignore enum accesses, as these will not cause the attached object to be created. // Also ignore anything we cannot determine. - if (!element->hasProperty(propertyName) && !element->hasMethod(propertyName)) + if (!element.hasProperty(propertyName) && !element.hasMethod(propertyName)) return; - for (QQmlJSScope::ConstPtr scope = readScope->parentScope(); !scope.isNull(); - scope = scope->parentScope()) { - + for (QQmlSA::Element scope = readScope.parentScope(); !scope.isNull(); + scope = scope.parentScope()) { const auto range = usedAttachedTypes.equal_range(scope); bool found = false; for (auto it = range.first; it != range.second; ++it) { @@ -543,41 +538,36 @@ void AttachedPropertyReuse::onRead( continue; const QString id = resolveElementToId(scope, readScope); - const QQmlJS::SourceLocation idInsertLocation { - attachedLocation.offset, 0, - attachedLocation.startLine, attachedLocation.startColumn - }; - QQmlJSFixSuggestion suggestion { - "Reference it by id instead:"_L1, - idInsertLocation, - id.isEmpty() ? "<id>."_L1 : (id + '.'_L1) - }; + const QQmlSA::SourceLocation idInsertLocation{ attachedLocation.offset(), 0, + attachedLocation.startLine(), + attachedLocation.startColumn() }; + QQmlSA::FixSuggestion suggestion{ "Reference it by id instead:"_L1, idInsertLocation, + id.isEmpty() ? "<id>."_L1 : (id + '.'_L1) }; if (id.isEmpty()) suggestion.setHint("You first have to give the element an id"_L1); else suggestion.setAutoApplicable(); - emitWarning("Using attached type %1 already initialized in a parent scope."_L1 - .arg(element->internalName()), + emitWarning("Using attached type %1 already initialized in a parent scope."_L1.arg( + element.internalName()), category, attachedLocation, suggestion); } return; } - - if (element->hasProperty(propertyName)) + if (element.hasProperty(propertyName)) return; // an actual property QQmlSA::Element type = resolveTypeInFileScope(propertyName); - if (!type || type->attachedTypeName().isEmpty()) + if (!type || type.attachedTypeName().isEmpty()) return; - const QQmlSA::Element attached = type->attachedType(); + const QQmlSA::Element attached = type.attachedType(); if (category == quickControlsAttachedPropertyReuse) { - for (QQmlSA::Element parent = attached; parent; parent = parent->baseType()) { - if (parent->internalName() == "QQuickAttachedPropertyPropagator"_L1) { + for (QQmlSA::Element parent = attached; parent; parent = parent.baseType()) { + if (parent.internalName() == "QQuickAttachedPropertyPropagator"_L1) { usedAttachedTypes.insert(readScope, {attached, location}); break; } @@ -588,10 +578,9 @@ void AttachedPropertyReuse::onRead( } } -void AttachedPropertyReuse::onWrite( - const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) +void AttachedPropertyReuse::onWrite(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Element &value, const QQmlSA::Element &writeScope, + QQmlSA::SourceLocation location) { Q_UNUSED(value); onRead(element, propertyName, writeScope, location); @@ -600,7 +589,7 @@ void AttachedPropertyReuse::onWrite( void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) { - const LoggerWarningId attachedReuseCategory = [manager](){ + const QQmlJS::LoggerWarningId attachedReuseCategory = [manager]() { if (manager->isCategoryEnabled(quickAttachedPropertyReuse)) return quickAttachedPropertyReuse; if (manager->isCategoryEnabled(qmlAttachedPropertyReuse)) @@ -750,32 +739,35 @@ PropertyChangesValidatorPass::PropertyChangesValidatorPass(QQmlSA::PassManager * bool PropertyChangesValidatorPass::shouldRun(const QQmlSA::Element &element) { - return !m_propertyChanges.isNull() && element->inherits(m_propertyChanges); + return !m_propertyChanges.isNull() && element.inherits(m_propertyChanges); } void PropertyChangesValidatorPass::run(const QQmlSA::Element &element) { - const QMultiHash<QString, QQmlJSMetaPropertyBinding> bindings = element->ownPropertyBindings(); + const QQmlSA::Binding::Bindings bindings = element.ownPropertyBindings(); - const auto target = bindings.find(u"target"_s); - if (target == bindings.end()) + const auto target = + std::find_if(bindings.constBegin(), bindings.constEnd(), + [](const auto binding) { return binding.propertyName() == u"target"_s; }); + if (target == bindings.constEnd()) return; QString targetId = u"<id>"_s; - const QString targetBinding = sourceCode(target->sourceLocation()); + const QString targetBinding = sourceCode(target.value().sourceLocation()); const QQmlSA::Element targetElement = resolveIdToElement(targetBinding, element); if (!targetElement.isNull()) targetId = targetBinding; - for (auto it = bindings.begin(), end = bindings.end(); it != end; ++it) { - const QString name = it->propertyName(); - if (element->hasProperty(name)) + for (auto it = bindings.constBegin(); it != bindings.constEnd(); ++it) { + const auto &propertyName = it.key(); + const auto &propertyBinding = it.value(); + if (element.hasProperty(propertyName)) continue; - const QQmlJS::SourceLocation bindingLocation = it->sourceLocation(); - if (!targetElement.isNull() && !targetElement->hasProperty(name)) { + const QQmlSA::SourceLocation bindingLocation = propertyBinding.sourceLocation(); + if (!targetElement.isNull() && !targetElement.hasProperty(propertyName)) { emitWarning( - "Unknown property \"%1\" in PropertyChanges."_L1.arg(name), + "Unknown property \"%1\" in PropertyChanges."_L1.arg(propertyName), quickPropertyChangesParsed, bindingLocation); continue; } @@ -784,11 +776,10 @@ void PropertyChangesValidatorPass::run(const QQmlSA::Element &element) if (binding.length() > 16) binding = binding.left(13) + "..."_L1; - emitWarning( - "Property \"%1\" is custom-parsed in PropertyChanges. " - "You should phrase this binding as \"%2.%1: %3\""_L1 - .arg(name, targetId, binding), - quickPropertyChangesParsed, bindingLocation); + emitWarning("Property \"%1\" is custom-parsed in PropertyChanges. " + "You should phrase this binding as \"%2.%1: %3\""_L1.arg(propertyName, targetId, + binding), + quickPropertyChangesParsed, bindingLocation); } } diff --git a/src/plugins/qmllint/quick/quicklintplugin.h b/src/plugins/qmllint/quick/quicklintplugin.h index 85b44f376a..6bff7dac9e 100644 --- a/src/plugins/qmllint/quick/quicklintplugin.h +++ b/src/plugins/qmllint/quick/quicklintplugin.h @@ -9,7 +9,8 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qhash.h> -#include <QtQmlCompiler/private/qqmlsa_p.h> +#include <QtQmlCompiler/qqmlsa.h> +#include "qqmlsaconstants.h" QT_BEGIN_NAMESPACE @@ -58,17 +59,17 @@ public: bool allowInDelegate, QAnyStringView warning); void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override; void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override; + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override; void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) override; + QQmlSA::SourceLocation location) override; private: void checkWarnings(const QQmlSA::Element &element, const QQmlSA::Element &scopeUsedIn, - const QQmlJS::SourceLocation &location); + const QQmlSA::SourceLocation &location); struct Warning { @@ -132,7 +133,7 @@ public: const QMultiHash<QString, TypeDescription> &expectedPropertyTypes); void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override; private: @@ -159,24 +160,24 @@ public: RestrictToControls }; - AttachedPropertyReuse(QQmlSA::PassManager *manager, LoggerWarningId category) - : QQmlSA::PropertyPass(manager) - , category(category) + AttachedPropertyReuse(QQmlSA::PassManager *manager, QQmlJS::LoggerWarningId category) + : QQmlSA::PropertyPass(manager), category(category) {} void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override; + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override; void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) override; + QQmlSA::SourceLocation location) override; + private: struct ElementAndLocation { QQmlSA::Element element; - QQmlJS::SourceLocation location; + QQmlSA::SourceLocation location; }; QMultiHash<QQmlSA::Element, ElementAndLocation> usedAttachedTypes; - LoggerWarningId category; + QQmlJS::LoggerWarningId category; }; QT_END_NAMESPACE diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt index b3cd96e1d7..b7476a96c2 100644 --- a/src/qmlcompiler/CMakeLists.txt +++ b/src/qmlcompiler/CMakeLists.txt @@ -24,6 +24,7 @@ qt_internal_add_module(QmlCompilerPrivate qqmljsliteralbindingcheck.cpp qqmljsliteralbindingcheck_p.h qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h qqmljslogger_p.h qqmljslogger.cpp + qqmljsloggingutils.h qqmljsloggingutils.cpp qqmljsloggingutils_p.h qqmljsmetatypes_p.h qqmljsmetatypes.cpp qqmljsregistercontent.cpp qqmljsregistercontent_p.h qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h @@ -39,7 +40,9 @@ qt_internal_add_module(QmlCompilerPrivate qqmljsutils_p.h qqmljsutils.cpp qqmljslinter_p.h qqmljslinter.cpp qqmljslintercodegen_p.h qqmljslintercodegen.cpp - qqmlsa_p.h qqmlsa.cpp + qqmlsa_p.h qqmlsa.h qqmlsa.cpp + qqmlsaconstants.h + qqmlsasourcelocation.h qqmlsasourcelocation.cpp qqmlsasourcelocation_p.h PUBLIC_LIBRARIES Qt::CorePrivate Qt::QmlPrivate @@ -54,3 +57,7 @@ qt_internal_add_resource(QmlCompilerPrivate "builtins" "${CMAKE_CURRENT_SOURCE_DIR}/../imports/builtins/builtins.qmltypes" "${CMAKE_CURRENT_SOURCE_DIR}/../imports/builtins/jsroot.qmltypes" ) + +qt_internal_add_docs(QmlCompilerPrivate + doc/qtqmlcompiler.qdocconf +) diff --git a/src/qmlcompiler/doc/qtqmlcompiler-index.qdoc b/src/qmlcompiler/doc/qtqmlcompiler-index.qdoc new file mode 100644 index 0000000000..d9d9a4e2a9 --- /dev/null +++ b/src/qmlcompiler/doc/qtqmlcompiler-index.qdoc @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtqmlcompiler-index.html + \title Qt QML Compiler + \brief Provides tools for static analysis of QML code. + + The Qt QML Compiler module offers the QQmlSA framework which provides tools + for static analysis of QML code. These tools can help ensure syntactic + validity and warn about QML anti-patterns. + + Adding static analysis to a QML program is done by writing plugins. They + will run a collection of analysis passes over the elements and properties + of the QML code. The passes can be registered with a PassManager which + holds the passes and can be called to analyze an element and its children. + + A pass is a check for a certain rule or condition evaluated on elements or + properties. If the condition is met, the pass can warn the user of an + indentified issue in the code and maybe even suggest a fix. It is called a + pass because the analysis performed on elements and properties happens by + running a collection of passes on them in succesion. Each pass should be + responsible for identifying one specific issue only. Combining a set of + passes can perform more complex analysis and, together, form a plugin. + + Element passes are defined by two main components, namely \c shouldRun() + and \c run(). When performing the analysis, the pass manager will execute + the pass over every element it encounters while traversing the children of + the root element. For each element, if \c shouldRun() evaluated on that + element returns \c true then \c run() is executed on it. + + Passes on properties trigger on three different events, namely when the + property is bound, when it is read, and when it is written to. These can be + implemented by overriding the \c onBinding(), \c onRead() and \c onWrite() + functions respectively. + + As the code grows, so does the number of elements and properties. + Performing the static analysis passes on all of them can become expensive. + That's why it is good to be granular when deciding which elements and + properties to analyze. For elements, the \c shouldRun() is intended to be a + cheap check to determine if \c run(), which performs the real computation, + should be run. For properties, the selection is done when registering the + passes with the manager. The \c registerPropertyPass() function takes the + \c moduleName, \c typeName and \c propertyName strings as arguments. These + are used to filter down the set of properties affected by the registered + pass. + */ diff --git a/src/qmlcompiler/doc/qtqmlcompiler.qdocconf b/src/qmlcompiler/doc/qtqmlcompiler.qdocconf new file mode 100644 index 0000000000..826ef49c56 --- /dev/null +++ b/src/qmlcompiler/doc/qtqmlcompiler.qdocconf @@ -0,0 +1,27 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtQmlCompiler +description = Qt QML Compiler Reference Documentation +version = $QT_VERSION + +depends += \ + qtcore \ + qtnetwork \ + qtqmlmodels \ + qtqmlworkerscript \ + qtgui \ + qtquick \ + qtdoc \ + qtlinguist \ + qtwidgets \ + qtquickcontrols \ + qmake \ + qtcmake \ + qtqml + +headerdirs += .. + +sourcedirs += .. + +navigation.landingpage = "Qt QML Compiler" +navigation.cppclassespage = "Qt QML Compiler C++ Classes" diff --git a/src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc b/src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc new file mode 100644 index 0000000000..ed7ef11d40 --- /dev/null +++ b/src/qmlcompiler/doc/src/qtqmlcompiler-module.qdoc @@ -0,0 +1,10 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \module QtQmlCompiler + \modulestate Technical Preview + \title Qt QML Compiler C++ Classes + + \brief The Qt QML Compiler module provides tools for static analysis of QML code. +*/ diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index d3770cc512..346fbee034 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -2350,7 +2350,7 @@ QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content return u'&' + var; if (m_typeResolver->isNumeric(content.storedType()) - && m_typeResolver->containedType(content)->scopeType() == QQmlJSScope::EnumScope) { + && m_typeResolver->containedType(content)->scopeType() == QQmlSA::ScopeType::EnumScope) { return u'&' + var; } @@ -2377,7 +2377,7 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return metaTypeFromName(contained); - if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlJSScope::EnumScope) + if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlSA::ScopeType::EnumScope) return metaTypeFromType(contained->baseType()); if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty()) @@ -3217,7 +3217,7 @@ QString QQmlJSCodeGenerator::convertStored( const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) { return m_typeResolver->isNumeric(m_typeResolver->globalType(type)) || m_typeResolver->equals(type, m_typeResolver->boolType()) - || type->scopeType() == QQmlJSScope::EnumScope; + || type->scopeType() == QQmlSA::ScopeType::EnumScope; }; if (m_typeResolver->equals(from, m_typeResolver->realType()) diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp index a9322e90c8..3001af1152 100644 --- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp +++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp @@ -171,7 +171,7 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run( for (const auto &method : methods) { if (method.isCloned()) continue; - if (method.methodType() == QQmlJSMetaMethod::Signal) { + if (method.methodType() == QQmlJSMetaMethodType::Signal) { function.isSignalHandler = true; const auto arguments = method.parameters(); for (qsizetype i = 0, end = arguments.size(); i < end; ++i) { diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 4fa709178c..ab991c038a 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -18,6 +18,8 @@ #include <QtQml/private/qqmlirbuilder_p.h> #include "qqmljsscope_p.h" #include "qqmljsutils_p.h" +#include "qqmljsloggingutils.h" +#include "qqmlsaconstants.h" #include <algorithm> #include <limits> @@ -37,7 +39,8 @@ using namespace QQmlJS::AST; inline void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, const QString &name) { Q_ASSERT(scope); - if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope) + if (type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope) scope->setInternalName(name); else scope->setBaseTypeName(name); @@ -50,7 +53,8 @@ inline void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, c inline QString getScopeName(const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ScopeType type) { Q_ASSERT(scope); - if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope) + if (type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope) return scope->internalName(); return scope->baseTypeName(); @@ -81,7 +85,7 @@ QQmlJSImportVisitor::QQmlJSImportVisitor( QQmlJSImporter::ImportedTypes::QML, {}, importer->builtinInternalNames().arrayType()) { - m_currentScope->setScopeType(QQmlJSScope::JSFunctionScope); + m_currentScope->setScopeType(QQmlSA::ScopeType::JSFunctionScope); Q_ASSERT(logger); // must be valid m_globalScope = m_currentScope; @@ -147,8 +151,8 @@ bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location) { - Q_ASSERT(type == QQmlJSScope::GroupedPropertyScope - || type == QQmlJSScope::AttachedPropertyScope); + Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope); const auto pred = [&](const QQmlJSScope::ConstPtr &s) { // it's either attached or group property, so use internalName() @@ -187,7 +191,7 @@ bool QQmlJSImportVisitor::isTypeResolved(const QQmlJSScope::ConstPtr &type) static bool mayBeUnresolvedGeneralizedGroupedProperty(const QQmlJSScope::ConstPtr &scope) { - return scope->scopeType() == QQmlJSScope::GroupedPropertyScope && !scope->baseType(); + return scope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope && !scope->baseType(); } void QQmlJSImportVisitor::resolveAliasesAndIds() @@ -691,7 +695,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects() "incompatible type \"%3\"") .arg(propertyName) .arg(property.typeName()) - .arg(getScopeName(childScope, QQmlJSScope::QMLScope)), + .arg(getScopeName(childScope, QQmlSA::ScopeType::QMLScope)), qmlIncompatibleType, objectBinding.location); continue; } @@ -701,7 +705,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects() // unique because it's per-scope and per-property const auto uniqueBindingId = qMakePair(objectBinding.scope, objectBinding.name); - const QString typeName = getScopeName(childScope, QQmlJSScope::QMLScope); + const QString typeName = getScopeName(childScope, QQmlSA::ScopeType::QMLScope); if (objectBinding.onToken) { if (childScope->hasInterface(QStringLiteral("QQmlPropertyValueInterceptor"))) { @@ -809,11 +813,11 @@ void QQmlJSImportVisitor::checkRequiredProperties() : QQmlJSScope::ConstPtr(); const QString propertyScopeName = !propertyScope.isNull() - ? getScopeName(propertyScope, QQmlJSScope::QMLScope) + ? getScopeName(propertyScope, QQmlSA::ScopeType::QMLScope) : u"here"_s; const QString requiredScopeName = prevRequiredScope - ? getScopeName(prevRequiredScope, QQmlJSScope::QMLScope) + ? getScopeName(prevRequiredScope, QQmlSA::ScopeType::QMLScope) : u"here"_s; std::optional<QQmlJSFixSuggestion> suggestion; @@ -921,9 +925,8 @@ void QQmlJSImportVisitor::checkSignal( const auto signal = QQmlJSUtils::signalName(handlerName); std::optional<QQmlJSMetaMethod> signalMethod; - const auto setSignalMethod = [&](const QQmlJSScope::ConstPtr &scope, - const QString &name) { - const auto methods = scope->methods(name, QQmlJSMetaMethod::Signal); + const auto setSignalMethod = [&](const QQmlJSScope::ConstPtr &scope, const QString &name) { + const auto methods = scope->methods(name, QQmlJSMetaMethodType::Signal); if (!methods.isEmpty()) signalMethod = methods[0]; }; @@ -1102,7 +1105,7 @@ void QQmlJSImportVisitor::addDefaultProperties() // Note: in this specific code path, binding on default property // means an object binding (we work with pending objects here) QQmlJSMetaPropertyBinding binding(m_currentScope->sourceLocation(), defaultPropertyName); - binding.setObject(getScopeName(m_currentScope, QQmlJSScope::QMLScope), + binding.setObject(getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope), QQmlJSScope::ConstPtr(m_currentScope)); m_bindings.append(UnfinishedBinding { m_currentScope->parentScope(), [=]() { return binding; }, QQmlJSScope::UnnamedPropertyTarget }); @@ -1181,11 +1184,11 @@ void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr sc auto childScope = children.takeFirst(); const auto type = childScope->scopeType(); switch (type) { - case QQmlJSScope::GroupedPropertyScope: - case QQmlJSScope::AttachedPropertyScope: + case QQmlSA::ScopeType::GroupedPropertyScope: + case QQmlSA::ScopeType::AttachedPropertyScope: if (!childScope->baseType()) { m_logger->log(QStringLiteral("unknown %1 property scope %2.") - .arg(type == QQmlJSScope::GroupedPropertyScope + .arg(type == QQmlSA::ScopeType::GroupedPropertyScope ? QStringLiteral("grouped") : QStringLiteral("attached"), childScope->internalName()), @@ -1270,8 +1273,9 @@ int QQmlJSImportVisitor::synthesizeCompilationUnitRuntimeFunctionIndices( { const auto suitableScope = [](const QQmlJSScope::Ptr &scope) { const auto type = scope->scopeType(); - return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope - || type == QQmlJSScope::AttachedPropertyScope; + return type == QQmlSA::ScopeType::QMLScope + || type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope; }; if (!suitableScope(scope)) @@ -1320,7 +1324,7 @@ void QQmlJSImportVisitor::populateRuntimeFunctionIndicesForDocument() const bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast) { if (m_pendingSignalHandler.isValid()) { - enterEnvironment(QQmlJSScope::JSFunctionScope, u"signalhandler"_s, + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, u"signalhandler"_s, ast->firstSourceLocation()); flushPendingSignalParameters(); } @@ -1329,7 +1333,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::ExpressionStatement *ast) void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ExpressionStatement *) { - if (m_currentScope->scopeType() == QQmlJSScope::JSFunctionScope + if (m_currentScope->scopeType() == QQmlSA::ScopeType::JSFunctionScope && m_currentScope->baseTypeName() == u"signalhandler"_s) { leaveEnvironment(); } @@ -1392,9 +1396,11 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition) Q_ASSERT(!superType.isEmpty()); if (superType.front().isUpper()) { if (!isRoot) { - enterEnvironment(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation()); + enterEnvironment(QQmlSA::ScopeType::QMLScope, superType, + definition->firstSourceLocation()); } else { - enterRootScope(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation()); + enterRootScope(QQmlSA::ScopeType::QMLScope, superType, + definition->firstSourceLocation()); m_currentScope->setIsSingleton(m_rootIsSingleton); } @@ -1428,12 +1434,12 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition) } addDefaultProperties(); - Q_ASSERT(m_currentScope->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope); m_qmlTypes.append(m_currentScope); m_objectDefinitionScopes << m_currentScope; } else { - enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, superType, + enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, superType, definition->firstSourceLocation()); Q_ASSERT(rootScopeIsValid()); m_bindings.append(createNonUniqueScopeBinding(m_currentScope, superType, @@ -1486,7 +1492,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) } UiParameterList *param = publicMember->parameters; QQmlJSMetaMethod method; - method.setMethodType(QQmlJSMetaMethod::Signal); + method.setMethodType(QQmlJSMetaMethodType::Signal); method.setMethodName(publicMember->name.toString()); while (param) { method.addParameter( @@ -1579,7 +1585,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember) if (parseResult == BindingExpressionParseResult::Script) { Q_ASSERT(!m_savedBindingOuterScope); // automatically true due to grammar m_savedBindingOuterScope = m_currentScope; - enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("binding"), + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral("binding"), publicMember->statement->firstSourceLocation()); } @@ -1617,7 +1623,7 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp auto name = fexpr->name.toString(); if (!name.isEmpty()) { QQmlJSMetaMethod method(name); - method.setMethodType(QQmlJSMetaMethod::Method); + method.setMethodType(QQmlJSMetaMethodType::Method); if (!m_pendingMethodAnnotations.isEmpty()) { method.setAnnotations(m_pendingMethodAnnotations); @@ -1662,16 +1668,16 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp method.setJsFunctionIndex(addFunctionOrExpression(m_currentScope, method.methodName())); m_currentScope->addOwnMethod(method); - if (m_currentScope->scopeType() != QQmlJSScope::QMLScope) { + if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) { m_currentScope->insertJSIdentifier(name, { QQmlJSScope::JavaScriptIdentifier::LexicalScoped, fexpr->firstSourceLocation(), method.returnTypeName(), false }); } - enterEnvironment(QQmlJSScope::JSFunctionScope, name, fexpr->firstSourceLocation()); + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation()); } else { addFunctionOrExpression(m_currentScope, QStringLiteral("<anon>")); - enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("<anon>"), + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral("<anon>"), fexpr->firstSourceLocation()); } } @@ -1711,7 +1717,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassExpression *ast) QQmlJSMetaProperty prop; prop.setPropertyName(ast->name.toString()); m_currentScope->addOwnProperty(prop); - enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString(), + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(), ast->firstSourceLocation()); return true; } @@ -1772,7 +1778,7 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name, QQmlJSMetaPropertyBinding binding(location, name); binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name), - QQmlJSMetaPropertyBinding::Script_PropertyBinding); + QQmlSA::ScriptBindingKind::Script_PropertyBinding); m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } }); return BindingExpressionParseResult::Script; } @@ -1816,7 +1822,7 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name, binding.setStringLiteral(templateLit->value); } else { binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name), - QQmlJSMetaPropertyBinding::Script_PropertyBinding); + QQmlSA::ScriptBindingKind::Script_PropertyBinding); } break; } @@ -1834,16 +1840,15 @@ QQmlJSImportVisitor::parseBindingExpression(const QString &name, if (!binding.isValid()) { // consider this to be a script binding (see IRBuilder::setBindingValue) binding.setScriptBinding(addFunctionOrExpression(m_currentScope, name), - QQmlJSMetaPropertyBinding::Script_PropertyBinding, - isUndefinedBinding - ? QQmlJSMetaPropertyBinding::ScriptValue_Undefined - : QQmlJSMetaPropertyBinding::ScriptValue_Unknown); + QQmlSA::ScriptBindingKind::Script_PropertyBinding, + isUndefinedBinding ? ScriptBindingValueType::ScriptValue_Undefined + : ScriptBindingValueType::ScriptValue_Unknown); } m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } }); // translations are neither literal bindings nor script bindings - if (binding.bindingType() == QQmlJSMetaPropertyBinding::Translation - || binding.bindingType() == QQmlJSMetaPropertyBinding::TranslationById) { + if (binding.bindingType() == QQmlSA::BindingType::Translation + || binding.bindingType() == QQmlSA::BindingType::TranslationById) { return BindingExpressionParseResult::Translation; } if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType())) @@ -1912,12 +1917,11 @@ createNonUniqueScopeBinding(QQmlJSScope::Ptr &scope, const QString &name, { const auto createBinding = [=]() { const QQmlJSScope::ScopeType type = scope->scopeType(); - Q_ASSERT(type == QQmlJSScope::GroupedPropertyScope - || type == QQmlJSScope::AttachedPropertyScope); - const QQmlJSMetaPropertyBinding::BindingType bindingType = - (type == QQmlJSScope::GroupedPropertyScope) - ? QQmlJSMetaPropertyBinding::GroupProperty - : QQmlJSMetaPropertyBinding::AttachedProperty; + Q_ASSERT(type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope); + const QQmlSA::BindingType bindingType = (type == QQmlSA::ScopeType::GroupedPropertyScope) + ? QQmlSA::BindingType::GroupProperty + : QQmlSA::BindingType::AttachedProperty; const auto propertyBindings = scope->parentScope()->ownPropertyBindings(name); const bool alreadyHasBinding = std::any_of(propertyBindings.first, propertyBindings.second, @@ -1928,7 +1932,7 @@ createNonUniqueScopeBinding(QQmlJSScope::Ptr &scope, const QString &name, return QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {}); QQmlJSMetaPropertyBinding binding(srcLocation, name); - if (type == QQmlJSScope::GroupedPropertyScope) + if (type == QQmlSA::ScopeType::GroupedPropertyScope) binding.setGroupBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope)); else binding.setAttachedBinding(static_cast<QSharedPointer<QQmlJSScope>>(scope)); @@ -1964,11 +1968,11 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) const bool isAttachedProperty = name.front().isUpper(); if (isAttachedProperty) { // attached property - enterEnvironmentNonUnique(QQmlJSScope::AttachedPropertyScope, prefix + name, + enterEnvironmentNonUnique(QQmlSA::ScopeType::AttachedPropertyScope, prefix + name, group->firstSourceLocation()); } else { // grouped property - enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, prefix + name, + enterEnvironmentNonUnique(QQmlSA::ScopeType::GroupedPropertyScope, prefix + name, group->firstSourceLocation()); } m_bindings.append(createNonUniqueScopeBinding(m_currentScope, prefix + name, @@ -2001,7 +2005,7 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) } QQmlJSMetaMethod scopeSignal; - const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethod::Signal); + const auto methods = m_currentScope->methods(*signal, QQmlJSMetaMethodType::Signal); if (!methods.isEmpty()) scopeSignal = methods[0]; @@ -2026,19 +2030,18 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) signalParameters]() { // when encountering a signal handler, add it as a script binding Q_ASSERT(scope->isFullyResolved()); - QQmlJSMetaPropertyBinding::ScriptBindingKind kind = - QQmlJSMetaPropertyBinding::Script_Invalid; - const auto methods = scope->methods(signalName, QQmlJSMetaMethod::Signal); + QQmlSA::ScriptBindingKind kind = QQmlSA::ScriptBindingKind::Script_Invalid; + const auto methods = scope->methods(signalName, QQmlJSMetaMethodType::Signal); if (!methods.isEmpty()) { - kind = QQmlJSMetaPropertyBinding::Script_SignalHandler; + kind = QQmlSA::ScriptBindingKind::Script_SignalHandler; checkSignal(scope, groupLocation, name, signalParameters); } else if (QQmlJSUtils::changeHandlerProperty(scope, signalName).has_value()) { - kind = QQmlJSMetaPropertyBinding::Script_ChangeHandler; + kind = QQmlSA::ScriptBindingKind::Script_ChangeHandler; checkSignal(scope, groupLocation, name, signalParameters); } else if (scope->hasProperty(name)) { // Not a signal handler after all. // We can see this now because the type is fully resolved. - kind = QQmlJSMetaPropertyBinding::Script_PropertyBinding; + kind = QQmlSA::ScriptBindingKind::Script_PropertyBinding; m_signalHandlers.remove(firstSourceLocation); } else { // We already know it's bad, but let's allow checkSignal() to do its thing. @@ -2056,12 +2059,12 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding) // TODO: before leaving the scopes, we must create the binding. // Leave any group/attached scopes so that the binding scope doesn't see its properties. - while (m_currentScope->scopeType() == QQmlJSScope::GroupedPropertyScope - || m_currentScope->scopeType() == QQmlJSScope::AttachedPropertyScope) { + while (m_currentScope->scopeType() == QQmlSA::ScopeType::GroupedPropertyScope + || m_currentScope->scopeType() == QQmlSA::ScopeType::AttachedPropertyScope) { leaveEnvironment(); } - enterEnvironment(QQmlJSScope::JSFunctionScope, QStringLiteral("binding"), + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, QStringLiteral("binding"), scriptBinding->statement->firstSourceLocation()); return true; @@ -2086,7 +2089,7 @@ void QQmlJSImportVisitor::endVisit(UiScriptBinding *) bool QQmlJSImportVisitor::visit(UiArrayBinding *arrayBinding) { - enterEnvironment(QQmlJSScope::QMLScope, buildName(arrayBinding->qualifiedId), + enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(arrayBinding->qualifiedId), arrayBinding->firstSourceLocation()); m_currentScope->setIsArrayScope(true); @@ -2103,13 +2106,13 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding) // other expressions involving lists (e.g. `var p: [1,2,3]`) are considered // to be script bindings const auto children = m_currentScope->childScopes(); - const auto propertyName = getScopeName(m_currentScope, QQmlJSScope::QMLScope); + const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope); leaveEnvironment(); qsizetype i = 0; for (auto element = arrayBinding->members; element; element = element->next, ++i) { const auto &type = children[i]; - if ((type->scopeType() != QQmlJSScope::QMLScope)) { + if ((type->scopeType() != QQmlSA::ScopeType::QMLScope)) { m_logger->log(u"Declaring an object which is not an Qml object" " as a list member."_s, qmlSyntax, element->firstSourceLocation()); return; @@ -2118,7 +2121,8 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding) << PendingPropertyObjectBinding { m_currentScope, type, propertyName, element->firstSourceLocation(), false }; QQmlJSMetaPropertyBinding binding(element->firstSourceLocation(), propertyName); - binding.setObject(getScopeName(type, QQmlJSScope::QMLScope), QQmlJSScope::ConstPtr(type)); + binding.setObject(getScopeName(type, QQmlSA::ScopeType::QMLScope), + QQmlJSScope::ConstPtr(type)); m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; }, QQmlJSScope::ListPropertyTarget }); } @@ -2337,7 +2341,7 @@ void QQmlJSImportVisitor::throwRecursionDepthError() bool QQmlJSImportVisitor::visit(QQmlJS::AST::ClassDeclaration *ast) { - enterEnvironment(QQmlJSScope::JSFunctionScope, ast->name.toString(), + enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, ast->name.toString(), ast->firstSourceLocation()); return true; } @@ -2349,7 +2353,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassDeclaration *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForStatement *ast) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("forloop"), + enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("forloop"), ast->firstSourceLocation()); return true; } @@ -2361,7 +2365,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForStatement *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::ForEachStatement *ast) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("foreachloop"), + enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("foreachloop"), ast->firstSourceLocation()); return true; } @@ -2373,7 +2377,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ForEachStatement *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::Block *ast) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("block"), + enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("block"), ast->firstSourceLocation()); if (m_pendingSignalHandler.isValid()) @@ -2389,7 +2393,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Block *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::CaseBlock *ast) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("case"), + enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("case"), ast->firstSourceLocation()); return true; } @@ -2401,7 +2405,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::CaseBlock *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("catch"), + enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("catch"), catchStatement->firstSourceLocation()); m_currentScope->insertJSIdentifier( catchStatement->patternElement->bindingIdentifier.toString(), @@ -2418,7 +2422,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::Catch *) bool QQmlJSImportVisitor::visit(QQmlJS::AST::WithStatement *ast) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("with"), + enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("with"), ast->firstSourceLocation()); m_logger->log(QStringLiteral("with statements are strongly discouraged in QML " @@ -2490,8 +2494,8 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob) continue; } - const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope - : QQmlJSScope::GroupedPropertyScope; + const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope + : QQmlSA::ScopeType::GroupedPropertyScope; bool exists = enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation()); @@ -2513,7 +2517,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob) if (needsResolution) QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes); - enterEnvironment(QQmlJSScope::QMLScope, buildName(uiob->qualifiedTypeNameId), + enterEnvironment(QQmlSA::ScopeType::QMLScope, buildName(uiob->qualifiedTypeNameId), uiob->qualifiedTypeNameId->identifierToken); QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes); @@ -2544,8 +2548,8 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob) continue; } - const auto scopeKind = idName.front().isUpper() ? QQmlJSScope::AttachedPropertyScope - : QQmlJSScope::GroupedPropertyScope; + const auto scopeKind = idName.front().isUpper() ? QQmlSA::ScopeType::AttachedPropertyScope + : QQmlSA::ScopeType::GroupedPropertyScope; // definitely exists [[maybe_unused]] bool exists = enterEnvironmentNonUnique(scopeKind, prefix + idName, group->firstSourceLocation()); @@ -2593,14 +2597,14 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob) QQmlJSMetaPropertyBinding binding(uiob->firstSourceLocation(), propertyName); if (uiob->hasOnToken) { if (childScope->hasInterface(u"QQmlPropertyValueInterceptor"_s)) { - binding.setInterceptor(getScopeName(childScope, QQmlJSScope::QMLScope), + binding.setInterceptor(getScopeName(childScope, QQmlSA::ScopeType::QMLScope), QQmlJSScope::ConstPtr(childScope)); } else { // if (childScope->hasInterface(u"QQmlPropertyValueSource"_s)) - binding.setValueSource(getScopeName(childScope, QQmlJSScope::QMLScope), + binding.setValueSource(getScopeName(childScope, QQmlSA::ScopeType::QMLScope), QQmlJSScope::ConstPtr(childScope)); } } else { - binding.setObject(getScopeName(childScope, QQmlJSScope::QMLScope), + binding.setObject(getScopeName(childScope, QQmlSA::ScopeType::QMLScope), QQmlJSScope::ConstPtr(childScope)); } m_bindings.append(UnfinishedBinding { m_currentScope, [=]() { return binding; } }); @@ -2629,8 +2633,8 @@ void QQmlJSImportVisitor::endVisit(ExportDeclaration *) bool QQmlJSImportVisitor::visit(ESModule *module) { Q_ASSERT(!rootScopeIsValid()); - enterRootScope( - QQmlJSScope::JSLexicalScope, QStringLiteral("module"), module->firstSourceLocation()); + enterRootScope(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("module"), + module->firstSourceLocation()); m_currentScope->setIsScript(true); importBaseModules(); leaveEnvironment(); diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h index 0a23704e71..53715bced9 100644 --- a/src/qmlcompiler/qqmljsimportvisitor_p.h +++ b/src/qmlcompiler/qqmljsimportvisitor_p.h @@ -55,6 +55,7 @@ public: QQmlJSScope::Ptr result() const { return m_exportedRootScope; } + const QQmlJSLogger *logger() const { return m_logger; } QQmlJSLogger *logger() { return m_logger; } QQmlJSImporter::ImportedTypes imports() const { return m_rootScopeImports; } @@ -73,7 +74,9 @@ public: static QString implicitImportDirectory( const QString &localFile, QQmlJSResourceFileMapper *mapper); - QQmlJSImporter *importer() { return m_importer; } // ### should this be restricted? + // ### should this be restricted? + QQmlJSImporter *importer() { return m_importer; } + const QQmlJSImporter *importer() const { return m_importer; } struct UnfinishedBinding { diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp index b69e2996cc..58ae981be4 100644 --- a/src/qmlcompiler/qqmljslinter.cpp +++ b/src/qmlcompiler/qqmljslinter.cpp @@ -19,6 +19,7 @@ #include <QtCore/qscopedpointer.h> #include <QtQmlCompiler/private/qqmlsa_p.h> +#include <QtQmlCompiler/private/qqmljsloggingutils_p.h> #if QT_CONFIG(library) # include <QtCore/qdiriterator.h> @@ -184,13 +185,9 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl const QString categoryId = (m_isInternal ? u""_s : u"Plugin."_s) + m_name + u'.' + object[u"name"].toString(); - m_categories << QQmlJSLogger::Category { - categoryId, - categoryId, - object["description"_L1].toString(), - QtWarningMsg, - ignored - }; + m_categories << QQmlJS::LoggerCategory{ categoryId, categoryId, + object["description"_L1].toString(), QtWarningMsg, + ignored }; } return true; @@ -198,7 +195,6 @@ bool QQmlJSLinter::Plugin::parseMetaData(const QJsonObject &metaData, QString pl std::vector<QQmlJSLinter::Plugin> QQmlJSLinter::loadPlugins(QStringList paths) { - std::vector<Plugin> plugins; QDuplicateTracker<QString> seenPlugins; @@ -273,7 +269,7 @@ void QQmlJSLinter::parseComments(QQmlJSLogger *logger, const QString category = words.at(i); const auto categoryExists = std::any_of( loggerCategories.cbegin(), loggerCategories.cend(), - [&](const QQmlJSLogger::Category &cat) { return cat.id().name() == category; }); + [&](const QQmlJS::LoggerCategory &cat) { return cat.id().name() == category; }); if (categoryExists) categories << category; @@ -422,7 +418,7 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename, QJsonArray *json, const QStringList &qmlImportPaths, const QStringList &qmldirFiles, const QStringList &resourceFiles, - const QList<QQmlJSLogger::Category> &categories) + const QList<QQmlJS::LoggerCategory> &categories) { // Make sure that we don't expose an old logger if we return before a new one is created. m_logger.reset(); @@ -519,17 +515,17 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename, if (m_enablePlugins) { for (const Plugin &plugin : m_plugins) { - for (const QQmlJSLogger::Category &category : plugin.categories()) + for (const QQmlJS::LoggerCategory &category : plugin.categories()) m_logger->registerCategory(category); } } for (auto it = categories.cbegin(); it != categories.cend(); ++it) { - if (!it->changed) + if (auto logger = *it; !QQmlJS::LoggerCategoryPrivate::get(&logger)->hasChanged()) continue; - m_logger->setCategoryIgnored(it->id(), it->ignored); - m_logger->setCategoryLevel(it->id(), it->level); + m_logger->setCategoryIgnored(it->id(), it->isIgnored()); + m_logger->setCategoryLevel(it->id(), it->level()); } parseComments(m_logger.get(), engine.comments()); @@ -563,10 +559,11 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename, QQmlSA::LintPlugin *instance = plugin.m_instance; Q_ASSERT(instance); - instance->registerPasses(passMan.get(), v.result()); + instance->registerPasses(passMan.get(), + QQmlJSScope::createQQmlSAElement(v.result())); } - passMan->analyze(v.result()); + passMan->analyze(QQmlJSScope::createQQmlSAElement(v.result())); } success = !m_logger->hasWarnings() && !m_logger->hasErrors(); diff --git a/src/qmlcompiler/qqmljslinter_p.h b/src/qmlcompiler/qqmljslinter_p.h index 6bab33e514..5bef11c55b 100644 --- a/src/qmlcompiler/qqmljslinter_p.h +++ b/src/qmlcompiler/qqmljslinter_p.h @@ -65,7 +65,7 @@ public: const QString &description() const { return m_description; } const QString &version() const { return m_version; } const QString &author() const { return m_author; } - const QList<QQmlJSLogger::Category> categories() const + const QList<QQmlJS::LoggerCategory> categories() const { return m_categories; } @@ -95,7 +95,7 @@ public: QString m_version; QString m_author; - QList<QQmlJSLogger::Category> m_categories; + QList<QQmlJS::LoggerCategory> m_categories; QQmlSA::LintPlugin *m_instance; std::unique_ptr<QPluginLoader> m_loader; bool m_isBuiltin; @@ -111,7 +111,7 @@ public: LintResult lintFile(const QString &filename, const QString *fileContents, const bool silent, QJsonArray *json, const QStringList &qmlImportPaths, const QStringList &qmldirFiles, const QStringList &resourceFiles, - const QList<QQmlJSLogger::Category> &categories); + const QList<QQmlJS::LoggerCategory> &categories); LintResult lintModule(const QString &uri, const bool silent, QJsonArray *json, const QStringList &qmlImportPaths, const QStringList &resourceFiles); diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp index a303f6cd87..ce6dc5d280 100644 --- a/src/qmlcompiler/qqmljslogger.cpp +++ b/src/qmlcompiler/qqmljslogger.cpp @@ -13,6 +13,8 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") QT_WARNING_POP #include "qqmljslogger_p.h" +#include "qqmljsloggingutils.h" +#include "qqmlsa_p.h" #include <QtCore/qfile.h> #include <QtCore/qfileinfo.h> @@ -21,192 +23,201 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -const LoggerWarningId qmlRequired { "required" }; -const LoggerWarningId qmlAliasCycle { "alias-cycle" }; -const LoggerWarningId qmlUnresolvedAlias { "unresolved-alias" }; -const LoggerWarningId qmlImport { "import" }; -const LoggerWarningId qmlRecursionDepthErrors { "recursion-depth-errors" }; -const LoggerWarningId qmlWith { "with" }; -const LoggerWarningId qmlInheritanceCycle { "inheritance-cycle" }; -const LoggerWarningId qmlDeprecated { "deprecated" }; -const LoggerWarningId qmlSignalParameters { "signal-handler-parameters" }; -const LoggerWarningId qmlMissingType { "missing-type" }; -const LoggerWarningId qmlUnresolvedType { "unresolved-type" }; -const LoggerWarningId qmlRestrictedType { "restricted-type" }; -const LoggerWarningId qmlPrefixedImportType { "prefixed-import-type" }; -const LoggerWarningId qmlIncompatibleType { "incompatible-type" }; -const LoggerWarningId qmlMissingProperty { "missing-property" }; -const LoggerWarningId qmlNonListProperty { "non-list-property" }; -const LoggerWarningId qmlReadOnlyProperty { "read-only-property" }; -const LoggerWarningId qmlDuplicatePropertyBinding { "duplicate-property-binding" }; -const LoggerWarningId qmlDuplicatedName { "duplicated-name" }; -const LoggerWarningId qmlDeferredPropertyId { "deferred-property-id" }; -const LoggerWarningId qmlUnqualified { "unqualified" }; -const LoggerWarningId qmlUnusedImports { "unused-imports" }; -const LoggerWarningId qmlMultilineStrings { "multiline-strings" }; -const LoggerWarningId qmlSyntax { "syntax" }; -const LoggerWarningId qmlSyntaxIdQuotation { "syntax.id-quotation" }; -const LoggerWarningId qmlSyntaxDuplicateIds { "syntax.duplicate-ids" }; -const LoggerWarningId qmlCompiler { "compiler" }; -const LoggerWarningId qmlAttachedPropertyReuse { "attached-property-reuse" }; -const LoggerWarningId qmlPlugin { "plugin" }; -const LoggerWarningId qmlVarUsedBeforeDeclaration { "var-used-before-declaration" }; -const LoggerWarningId qmlInvalidLintDirective { "invalid-lint-directive" }; -const LoggerWarningId qmlUseProperFunction { "use-proper-function" }; -const LoggerWarningId qmlAccessSingleton { "access-singleton-via-object" }; -const LoggerWarningId qmlTopLevelComponent { "top-level-component" }; -const LoggerWarningId qmlUncreatableType { "uncreatable-type" }; -const LoggerWarningId qmlMissingEnumEntry{ "missing-enum-entry" }; - -const QList<QQmlJSLogger::Category> &QQmlJSLogger::defaultCategories() +const QQmlJS::LoggerWarningId qmlRequired{ "required" }; +const QQmlJS::LoggerWarningId qmlAliasCycle{ "alias-cycle" }; +const QQmlJS::LoggerWarningId qmlUnresolvedAlias{ "unresolved-alias" }; +const QQmlJS::LoggerWarningId qmlImport{ "import" }; +const QQmlJS::LoggerWarningId qmlRecursionDepthErrors{ "recursion-depth-errors" }; +const QQmlJS::LoggerWarningId qmlWith{ "with" }; +const QQmlJS::LoggerWarningId qmlInheritanceCycle{ "inheritance-cycle" }; +const QQmlJS::LoggerWarningId qmlDeprecated{ "deprecated" }; +const QQmlJS::LoggerWarningId qmlSignalParameters{ "signal-handler-parameters" }; +const QQmlJS::LoggerWarningId qmlMissingType{ "missing-type" }; +const QQmlJS::LoggerWarningId qmlUnresolvedType{ "unresolved-type" }; +const QQmlJS::LoggerWarningId qmlRestrictedType{ "restricted-type" }; +const QQmlJS::LoggerWarningId qmlPrefixedImportType{ "prefixed-import-type" }; +const QQmlJS::LoggerWarningId qmlIncompatibleType{ "incompatible-type" }; +const QQmlJS::LoggerWarningId qmlMissingProperty{ "missing-property" }; +const QQmlJS::LoggerWarningId qmlNonListProperty{ "non-list-property" }; +const QQmlJS::LoggerWarningId qmlReadOnlyProperty{ "read-only-property" }; +const QQmlJS::LoggerWarningId qmlDuplicatePropertyBinding{ "duplicate-property-binding" }; +const QQmlJS::LoggerWarningId qmlDuplicatedName{ "duplicated-name" }; +const QQmlJS::LoggerWarningId qmlDeferredPropertyId{ "deferred-property-id" }; +const QQmlJS::LoggerWarningId qmlUnqualified{ "unqualified" }; +const QQmlJS::LoggerWarningId qmlUnusedImports{ "unused-imports" }; +const QQmlJS::LoggerWarningId qmlMultilineStrings{ "multiline-strings" }; +const QQmlJS::LoggerWarningId qmlSyntax{ "syntax" }; +const QQmlJS::LoggerWarningId qmlSyntaxIdQuotation{ "syntax.id-quotation" }; +const QQmlJS::LoggerWarningId qmlSyntaxDuplicateIds{ "syntax.duplicate-ids" }; +const QQmlJS::LoggerWarningId qmlCompiler{ "compiler" }; +const QQmlJS::LoggerWarningId qmlAttachedPropertyReuse{ "attached-property-reuse" }; +const QQmlJS::LoggerWarningId qmlPlugin{ "plugin" }; +const QQmlJS::LoggerWarningId qmlVarUsedBeforeDeclaration{ "var-used-before-declaration" }; +const QQmlJS::LoggerWarningId qmlInvalidLintDirective{ "invalid-lint-directive" }; +const QQmlJS::LoggerWarningId qmlUseProperFunction{ "use-proper-function" }; +const QQmlJS::LoggerWarningId qmlAccessSingleton{ "access-singleton-via-object" }; +const QQmlJS::LoggerWarningId qmlTopLevelComponent{ "top-level-component" }; +const QQmlJS::LoggerWarningId qmlUncreatableType{ "uncreatable-type" }; +const QQmlJS::LoggerWarningId qmlMissingEnumEntry{ "missing-enum-entry" }; + +QQmlJSLogger::QQmlJSLogger() { - static const QList<QQmlJSLogger::Category> cats = { - QQmlJSLogger::Category { qmlRequired.name().toString(), QStringLiteral("RequiredProperty"), - QStringLiteral("Warn about required properties"), QtWarningMsg }, - QQmlJSLogger::Category { qmlAliasCycle.name().toString(), - QStringLiteral("PropertyAliasCycles"), - QStringLiteral("Warn about alias cycles"), QtWarningMsg }, - QQmlJSLogger::Category { qmlUnresolvedAlias.name().toString(), - QStringLiteral("PropertyAliasCycles"), - QStringLiteral("Warn about unresolved aliases"), QtWarningMsg }, - QQmlJSLogger::Category { + static const QList<QQmlJS::LoggerCategory> cats = defaultCategories(); + + for (const QQmlJS::LoggerCategory &category : cats) + registerCategory(category); + + // setup color output + m_output.insertMapping(QtCriticalMsg, QColorOutput::RedForeground); + m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground); // Yellow? + m_output.insertMapping(QtInfoMsg, QColorOutput::BlueForeground); + m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground); // None? +} + +const QList<QQmlJS::LoggerCategory> &QQmlJSLogger::defaultCategories() +{ + static const QList<QQmlJS::LoggerCategory> cats = { + QQmlJS::LoggerCategory{ qmlRequired.name().toString(), QStringLiteral("RequiredProperty"), + QStringLiteral("Warn about required properties"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlAliasCycle.name().toString(), + QStringLiteral("PropertyAliasCycles"), + QStringLiteral("Warn about alias cycles"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlUnresolvedAlias.name().toString(), + QStringLiteral("PropertyAliasCycles"), + QStringLiteral("Warn about unresolved aliases"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlImport.name().toString(), QStringLiteral("ImportFailure"), QStringLiteral("Warn about failing imports and deprecated qmltypes"), QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlRecursionDepthErrors.name().toString(), QStringLiteral("ImportFailure"), QStringLiteral("Warn about failing imports and deprecated qmltypes"), QtWarningMsg, false, true }, - QQmlJSLogger::Category { - qmlWith.name().toString(), QStringLiteral("WithStatement"), - QStringLiteral("Warn about with statements as they can cause false " - "positives when checking for unqualified access"), - QtWarningMsg }, - QQmlJSLogger::Category { qmlInheritanceCycle.name().toString(), - QStringLiteral("InheritanceCycle"), - QStringLiteral("Warn about inheritance cycles"), QtWarningMsg }, - QQmlJSLogger::Category { qmlDeprecated.name().toString(), QStringLiteral("Deprecated"), - QStringLiteral("Warn about deprecated properties and types"), - QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlWith.name().toString(), QStringLiteral("WithStatement"), + QStringLiteral("Warn about with statements as they can cause false " + "positives when checking for unqualified access"), + QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlInheritanceCycle.name().toString(), + QStringLiteral("InheritanceCycle"), + QStringLiteral("Warn about inheritance cycles"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlDeprecated.name().toString(), QStringLiteral("Deprecated"), + QStringLiteral("Warn about deprecated properties and types"), + QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlSignalParameters.name().toString(), QStringLiteral("BadSignalHandlerParameters"), QStringLiteral("Warn about bad signal handler parameters"), QtWarningMsg }, - QQmlJSLogger::Category { qmlMissingType.name().toString(), QStringLiteral("MissingType"), - QStringLiteral("Warn about missing types"), QtWarningMsg }, - QQmlJSLogger::Category { qmlUnresolvedType.name().toString(), - QStringLiteral("UnresolvedType"), - QStringLiteral("Warn about unresolved types"), QtWarningMsg }, - QQmlJSLogger::Category { qmlRestrictedType.name().toString(), - QStringLiteral("RestrictedType"), - QStringLiteral("Warn about restricted types"), QtWarningMsg }, - QQmlJSLogger::Category { qmlPrefixedImportType.name().toString(), - QStringLiteral("PrefixedImportType"), - QStringLiteral("Warn about prefixed import types"), QtWarningMsg }, - QQmlJSLogger::Category { qmlIncompatibleType.name().toString(), - QStringLiteral("IncompatibleType"), - QStringLiteral("Warn about missing types"), QtWarningMsg }, - QQmlJSLogger::Category { qmlMissingProperty.name().toString(), - QStringLiteral("MissingProperty"), - QStringLiteral("Warn about missing properties"), QtWarningMsg }, - QQmlJSLogger::Category { qmlNonListProperty.name().toString(), - QStringLiteral("NonListProperty"), - QStringLiteral("Warn about non-list properties"), QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlMissingType.name().toString(), QStringLiteral("MissingType"), + QStringLiteral("Warn about missing types"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlUnresolvedType.name().toString(), + QStringLiteral("UnresolvedType"), + QStringLiteral("Warn about unresolved types"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlRestrictedType.name().toString(), + QStringLiteral("RestrictedType"), + QStringLiteral("Warn about restricted types"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlPrefixedImportType.name().toString(), + QStringLiteral("PrefixedImportType"), + QStringLiteral("Warn about prefixed import types"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlIncompatibleType.name().toString(), + QStringLiteral("IncompatibleType"), + QStringLiteral("Warn about missing types"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlMissingProperty.name().toString(), + QStringLiteral("MissingProperty"), + QStringLiteral("Warn about missing properties"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlNonListProperty.name().toString(), + QStringLiteral("NonListProperty"), + QStringLiteral("Warn about non-list properties"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlReadOnlyProperty.name().toString(), QStringLiteral("ReadOnlyProperty"), QStringLiteral("Warn about writing to read-only properties"), QtWarningMsg }, - QQmlJSLogger::Category { qmlDuplicatePropertyBinding.name().toString(), - QStringLiteral("DuplicatePropertyBinding"), - QStringLiteral("Warn about duplicate property bindings"), - QtWarningMsg }, - QQmlJSLogger::Category { qmlDuplicatedName.name().toString(), - QStringLiteral("DuplicatedName"), - QStringLiteral("Warn about duplicated property/signal names"), - QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlDuplicatePropertyBinding.name().toString(), + QStringLiteral("DuplicatePropertyBinding"), + QStringLiteral("Warn about duplicate property bindings"), + QtWarningMsg }, + QQmlJS::LoggerCategory{ + qmlDuplicatedName.name().toString(), QStringLiteral("DuplicatedName"), + QStringLiteral("Warn about duplicated property/signal names"), QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlDeferredPropertyId.name().toString(), QStringLiteral("DeferredPropertyId"), QStringLiteral( "Warn about making deferred properties immediate by giving them an id."), QtInfoMsg, true, true }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlUnqualified.name().toString(), QStringLiteral("UnqualifiedAccess"), QStringLiteral("Warn about unqualified identifiers and how to fix them"), QtWarningMsg }, - QQmlJSLogger::Category { qmlUnusedImports.name().toString(), - QStringLiteral("UnusedImports"), - QStringLiteral("Warn about unused imports"), QtInfoMsg }, - QQmlJSLogger::Category { qmlMultilineStrings.name().toString(), - QStringLiteral("MultilineStrings"), - QStringLiteral("Warn about multiline strings"), QtInfoMsg }, - QQmlJSLogger::Category { qmlSyntax.name().toString(), QString(), - QStringLiteral("Syntax errors"), QtWarningMsg, false, true }, - QQmlJSLogger::Category { qmlSyntaxIdQuotation.name().toString(), QString(), - QStringLiteral("ID quotation"), QtWarningMsg, false, true }, - QQmlJSLogger::Category { qmlSyntaxDuplicateIds.name().toString(), QString(), - QStringLiteral("ID duplication"), QtCriticalMsg, false, true }, - QQmlJSLogger::Category { qmlCompiler.name().toString(), QStringLiteral("CompilerWarnings"), - QStringLiteral("Warn about compiler issues"), QtWarningMsg, true }, - QQmlJSLogger::Category { qmlAttachedPropertyReuse.name().toString(), - QStringLiteral("AttachedPropertyReuse"), - QStringLiteral("Warn if attached types from parent components " - "aren't reused. This is handled by the QtQuick " - "lint plugin. Use Quick.AttachedPropertyReuse instead."), - QtCriticalMsg, true }, - QQmlJSLogger::Category { qmlPlugin.name().toString(), QStringLiteral("LintPluginWarnings"), - QStringLiteral("Warn if a qmllint plugin finds an issue"), - QtWarningMsg, true }, - QQmlJSLogger::Category { qmlVarUsedBeforeDeclaration.name().toString(), - QStringLiteral("VarUsedBeforeDeclaration"), - QStringLiteral("Warn if a variable is used before declaration"), - QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlUnusedImports.name().toString(), QStringLiteral("UnusedImports"), + QStringLiteral("Warn about unused imports"), QtInfoMsg }, + QQmlJS::LoggerCategory{ qmlMultilineStrings.name().toString(), + QStringLiteral("MultilineStrings"), + QStringLiteral("Warn about multiline strings"), QtInfoMsg }, + QQmlJS::LoggerCategory{ qmlSyntax.name().toString(), QString(), + QStringLiteral("Syntax errors"), QtWarningMsg, false, true }, + QQmlJS::LoggerCategory{ qmlSyntaxIdQuotation.name().toString(), QString(), + QStringLiteral("ID quotation"), QtWarningMsg, false, true }, + QQmlJS::LoggerCategory{ qmlSyntaxDuplicateIds.name().toString(), QString(), + QStringLiteral("ID duplication"), QtCriticalMsg, false, true }, + QQmlJS::LoggerCategory{ qmlCompiler.name().toString(), QStringLiteral("CompilerWarnings"), + QStringLiteral("Warn about compiler issues"), QtWarningMsg, true }, + QQmlJS::LoggerCategory{ + qmlAttachedPropertyReuse.name().toString(), QStringLiteral("AttachedPropertyReuse"), + QStringLiteral("Warn if attached types from parent components " + "aren't reused. This is handled by the QtQuick " + "lint plugin. Use Quick.AttachedPropertyReuse instead."), + QtCriticalMsg, true }, + QQmlJS::LoggerCategory{ qmlPlugin.name().toString(), QStringLiteral("LintPluginWarnings"), + QStringLiteral("Warn if a qmllint plugin finds an issue"), + QtWarningMsg, true }, + QQmlJS::LoggerCategory{ qmlVarUsedBeforeDeclaration.name().toString(), + QStringLiteral("VarUsedBeforeDeclaration"), + QStringLiteral("Warn if a variable is used before declaration"), + QtWarningMsg }, + QQmlJS::LoggerCategory{ qmlInvalidLintDirective.name().toString(), QStringLiteral("InvalidLintDirective"), QStringLiteral("Warn if an invalid qmllint comment is found"), QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlUseProperFunction.name().toString(), QStringLiteral("UseProperFunction"), QStringLiteral("Warn if var is used for storing functions"), QtWarningMsg }, - QQmlJSLogger::Category { + QQmlJS::LoggerCategory{ qmlAccessSingleton.name().toString(), QStringLiteral("AccessSingletonViaObject"), QStringLiteral("Warn if a singleton is accessed via an object"), QtWarningMsg }, - QQmlJSLogger::Category { - qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"), - QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg }, - QQmlJSLogger::Category { - qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"), - QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg } + QQmlJS::LoggerCategory{ + qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"), + QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg }, + QQmlJS::LoggerCategory{ + qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"), + QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg } }; return cats; } -const QList<QQmlJSLogger::Category> QQmlJSLogger::categories() const +bool QQmlJSFixSuggestion::operator==(const QQmlJSFixSuggestion &other) const { - return m_categories.values(); + return m_location == other.m_location && m_fixDescription == other.m_fixDescription + && m_replacement == other.m_replacement && m_filename == other.m_filename + && m_hint == other.m_hint && m_autoApplicable == other.m_autoApplicable; } -void QQmlJSLogger::registerCategory(const QQmlJSLogger::Category &category) +bool QQmlJSFixSuggestion::operator!=(const QQmlJSFixSuggestion &other) const { - if (m_categories.contains(category.name)) { - qWarning() << "Trying to re-register existing logger category" << category.name; - return; - } - - m_categoryLevels[category.name] = category.level; - m_categoryIgnored[category.name] = category.ignored; - m_categories.insert(category.name, category); + return !(*this == other); } -QQmlJSLogger::QQmlJSLogger() +QList<QQmlJS::LoggerCategory> QQmlJSLogger::categories() const { - static const QList<QQmlJSLogger::Category> cats = defaultCategories(); + return m_categories.values(); +} - for (const QQmlJSLogger::Category &category : cats) - registerCategory(category); +void QQmlJSLogger::registerCategory(const QQmlJS::LoggerCategory &category) +{ + if (m_categories.contains(category.name())) { + qWarning() << "Trying to re-register existing logger category" << category.name(); + return; + } - // setup color output - m_output.insertMapping(QtCriticalMsg, QColorOutput::RedForeground); - m_output.insertMapping(QtWarningMsg, QColorOutput::PurpleForeground); // Yellow? - m_output.insertMapping(QtInfoMsg, QColorOutput::BlueForeground); - m_output.insertMapping(QtDebugMsg, QColorOutput::GreenForeground); // None? + m_categoryLevels[category.name()] = category.level(); + m_categoryIgnored[category.name()] = category.isIgnored(); + m_categories.insert(category.name(), category); } static bool isMsgTypeLess(QtMsgType a, QtMsgType b) @@ -219,7 +230,7 @@ static bool isMsgTypeLess(QtMsgType a, QtMsgType b) return level[a] < level[b]; } -void QQmlJSLogger::log(const QString &message, LoggerWarningId id, +void QQmlJSLogger::log(const QString &message, QQmlJS::LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext, bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion, const QString overrideFileName) @@ -277,7 +288,7 @@ void QQmlJSLogger::log(const QString &message, LoggerWarningId id, } void QQmlJSLogger::processMessages(const QList<QQmlJS::DiagnosticMessage> &messages, - LoggerWarningId id) + QQmlJS::LoggerWarningId id) { if (messages.isEmpty() || isCategoryIgnored(id)) return; @@ -380,4 +391,11 @@ void QQmlJSLogger::printFix(const QQmlJSFixSuggestion &fixItem) + u"^"_s.repeated(replacement.size()) + u'\n'); } +QQmlJSFixSuggestion::QQmlJSFixSuggestion(const QString &fixDescription, + const QQmlJS::SourceLocation &location, + const QString &replacement) + : m_location{ location }, m_fixDescription{ fixDescription }, m_replacement{ replacement } +{ +} + QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h index 9df321fb94..f7ac8fdea4 100644 --- a/src/qmlcompiler/qqmljslogger_p.h +++ b/src/qmlcompiler/qqmljslogger_p.h @@ -18,6 +18,7 @@ #include <private/qtqmlcompilerexports_p.h> #include "qcoloroutput_p.h" +#include "qqmljsloggingutils.h" #include <private/qqmljsdiagnosticmessage_p.h> @@ -34,7 +35,7 @@ QT_BEGIN_NAMESPACE /*! \internal - Used to print the the line containing the location of a certain error + Used to print the line containing the location of a certain error */ class Q_QMLCOMPILER_PRIVATE_EXPORT IssueLocationWithContext { @@ -75,11 +76,7 @@ class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSFixSuggestion public: QQmlJSFixSuggestion() = default; QQmlJSFixSuggestion(const QString &fixDescription, const QQmlJS::SourceLocation &location, - const QString &replacement = QString()) - : m_location(location) - , m_fixDescription(fixDescription) - , m_replacement(replacement) - {} + const QString &replacement = QString()); QString fixDescription() const { return m_fixDescription; } QQmlJS::SourceLocation location() const { return m_location; } @@ -94,6 +91,9 @@ public: void setAutoApplicable(bool autoApply = true) { m_autoApplicable = autoApply; } bool isAutoApplicable() const { return m_autoApplicable; } + bool operator==(const QQmlJSFixSuggestion &) const; + bool operator!=(const QQmlJSFixSuggestion &) const; + private: QQmlJS::SourceLocation m_location; QString m_fixDescription; @@ -103,64 +103,6 @@ private: bool m_autoApplicable = false; }; -class Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId -{ -public: - constexpr LoggerWarningId(QAnyStringView name) : m_name(name) { } - - const QAnyStringView name() const { return m_name; } - -private: - friend bool operator==(const LoggerWarningId &a, const LoggerWarningId &b) - { - return a.m_name == b.m_name; - } - - friend bool operator!=(const LoggerWarningId &a, const LoggerWarningId &b) - { - return a.m_name != b.m_name; - } - - const QAnyStringView m_name; -}; - -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlRequired; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnresolvedAlias; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAliasCycle; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlImport; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlRecursionDepthErrors; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlWith; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlInheritanceCycle; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDeprecated; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSignalParameters; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMissingType; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnresolvedType; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlIncompatibleType; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMissingProperty; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlRestrictedType; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlPrefixedImportType; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlNonListProperty; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlReadOnlyProperty; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDuplicatePropertyBinding; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDuplicatedName; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlDeferredPropertyId; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnqualified; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUnusedImports; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMultilineStrings; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSyntax; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSyntaxIdQuotation; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlSyntaxDuplicateIds; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlCompiler; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAttachedPropertyReuse; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlPlugin; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlVarUsedBeforeDeclaration; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlInvalidLintDirective; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUseProperFunction; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAccessSingleton; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlTopLevelComponent; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUncreatableType; -extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlMissingEnumEntry; - struct Message : public QQmlJS::DiagnosticMessage { // This doesn't need to be an owning-reference since the string is expected to outlive any @@ -173,60 +115,10 @@ class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSLogger { Q_DISABLE_COPY_MOVE(QQmlJSLogger) public: - struct Category - { - QString name; - QString settingsName; - QString description; - QtMsgType level; - bool ignored = false; - bool isDefault = false; // Whether or not the category can be disabled - bool changed = false; - - bool operator==(const LoggerWarningId warningId) const { return warningId.name() == name; } - - LoggerWarningId id() const { return LoggerWarningId(name); } - - QString levelToString() const { - // TODO: this only makes sense to qmllint - Q_ASSERT(ignored || level != QtCriticalMsg); - if (ignored) - return QStringLiteral("disable"); - - switch (level) { - case QtInfoMsg: - return QStringLiteral("info"); - case QtWarningMsg: - return QStringLiteral("warning"); - default: - Q_UNREACHABLE(); - break; - } - } - - bool setLevel(const QString &level) { - if (level == QStringLiteral("disable")) { - this->level = QtCriticalMsg; // TODO: only so for consistency with previous logic - this->ignored = true; - } else if (level == QStringLiteral("info")) { - this->level = QtInfoMsg; - this->ignored = false; - } else if (level == QStringLiteral("warning")) { - this->level = QtWarningMsg; - this->ignored = false; - } else { - return false; - } - - this->changed = true; - return true; - } - }; - - const QList<Category> categories() const; - static const QList<Category> &defaultCategories(); - - void registerCategory(const Category &category); + QList<QQmlJS::LoggerCategory> categories() const; + static const QList<QQmlJS::LoggerCategory> &defaultCategories(); + + void registerCategory(const QQmlJS::LoggerCategory &category); QQmlJSLogger(); ~QQmlJSLogger() = default; @@ -238,37 +130,37 @@ public: const QList<Message> &warnings() const { return m_warnings; } const QList<Message> &errors() const { return m_errors; } - QtMsgType categoryLevel(LoggerWarningId id) const + QtMsgType categoryLevel(QQmlJS::LoggerWarningId id) const { return m_categoryLevels[id.name().toString()]; } - void setCategoryLevel(LoggerWarningId id, QtMsgType level) + void setCategoryLevel(QQmlJS::LoggerWarningId id, QtMsgType level) { m_categoryLevels[id.name().toString()] = level; m_categoryChanged[id.name().toString()] = true; } - bool isCategoryIgnored(LoggerWarningId id) const + bool isCategoryIgnored(QQmlJS::LoggerWarningId id) const { return m_categoryIgnored[id.name().toString()]; } - void setCategoryIgnored(LoggerWarningId id, bool error) + void setCategoryIgnored(QQmlJS::LoggerWarningId id, bool error) { m_categoryIgnored[id.name().toString()] = error; m_categoryChanged[id.name().toString()] = true; } - bool isCategoryFatal(LoggerWarningId id) const + bool isCategoryFatal(QQmlJS::LoggerWarningId id) const { return m_categoryFatal[id.name().toString()]; } - void setCategoryFatal(LoggerWarningId id, bool error) + void setCategoryFatal(QQmlJS::LoggerWarningId id, bool error) { m_categoryFatal[id.name().toString()] = error; m_categoryChanged[id.name().toString()] = true; } - bool wasCategoryChanged(LoggerWarningId id) const + bool wasCategoryChanged(QQmlJS::LoggerWarningId id) const { return m_categoryChanged[id.name().toString()]; } @@ -280,9 +172,9 @@ public: \sa setCategoryLevel */ - void log(const QString &message, LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, - bool showContext = true, bool showFileName = true, - const std::optional<QQmlJSFixSuggestion> &suggestion = {}, + void log(const QString &message, QQmlJS::LoggerWarningId id, + const QQmlJS::SourceLocation &srcLocation, bool showContext = true, + bool showFileName = true, const std::optional<QQmlJSFixSuggestion> &suggestion = {}, const QString overrideFileName = QString()) { log(message, id, srcLocation, m_categoryLevels[id.name().toString()], showContext, @@ -290,7 +182,7 @@ public: } void processMessages(const QList<QQmlJS::DiagnosticMessage> &messages, - const LoggerWarningId id); + const QQmlJS::LoggerWarningId id); void ignoreWarnings(uint32_t line, const QSet<QString> &categories) { @@ -307,14 +199,15 @@ public: QString fileName() const { return m_fileName; } private: - QMap<QString, Category> m_categories; + QMap<QString, QQmlJS::LoggerCategory> m_categories; void printContext(const QString &overrideFileName, const QQmlJS::SourceLocation &location); void printFix(const QQmlJSFixSuggestion &fix); - void log(const QString &message, LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, - QtMsgType type, bool showContext, bool showFileName, - const std::optional<QQmlJSFixSuggestion> &suggestion, const QString overrideFileName); + void log(const QString &message, QQmlJS::LoggerWarningId id, + const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext, + bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion, + const QString overrideFileName); QString m_fileName; QString m_code; diff --git a/src/qmlcompiler/qqmljsloggingutils.cpp b/src/qmlcompiler/qqmljsloggingutils.cpp new file mode 100644 index 0000000000..2ce5eeb439 --- /dev/null +++ b/src/qmlcompiler/qqmljsloggingutils.cpp @@ -0,0 +1,128 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qqmljsloggingutils.h" +#include "qqmljsloggingutils_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +LoggerCategory::LoggerCategory() : d_ptr{ new LoggerCategoryPrivate } { } + +LoggerCategory::LoggerCategory(QString name, QString settingsName, QString description, + QtMsgType level, bool ignored, bool isDefault) + : d_ptr{ new LoggerCategoryPrivate } +{ + Q_D(LoggerCategory); + d->m_name = name; + d->m_settingsName = settingsName; + d->m_description = description; + d->m_level = level; + d->m_ignored = ignored; + d->m_isDefault = isDefault; +} + +LoggerCategory::LoggerCategory(const LoggerCategory &other) + : d_ptr{ new LoggerCategoryPrivate{ *other.d_func() } } +{ +} + +LoggerCategory::LoggerCategory(LoggerCategory &&) = default; + +LoggerCategory &LoggerCategory::operator=(const LoggerCategory &other) +{ + *d_func() = *other.d_func(); + return *this; +} + +LoggerCategory &LoggerCategory::operator=(LoggerCategory &&) = default; + +LoggerCategory::~LoggerCategory() = default; + +QString LoggerCategory::name() const +{ + Q_D(const LoggerCategory); + return d->m_name; +} + +QString LoggerCategory::settingsName() const +{ + Q_D(const LoggerCategory); + return d->m_settingsName; +} + +QString LoggerCategory::description() const +{ + Q_D(const LoggerCategory); + return d->m_description; +} + +QtMsgType LoggerCategory::level() const +{ + Q_D(const LoggerCategory); + return d->m_level; +} + +bool LoggerCategory::isIgnored() const +{ + Q_D(const LoggerCategory); + return d->m_ignored; +} + +bool LoggerCategory::isDefault() const +{ + Q_D(const LoggerCategory); + return d->m_isDefault; +} + +LoggerWarningId LoggerCategory::id() const +{ + Q_D(const LoggerCategory); + return d->id(); +} + +void LoggerCategory::setLevel(QtMsgType type) +{ + Q_D(LoggerCategory); + d->setLevel(type); +} + +void LoggerCategoryPrivate::setLevel(QtMsgType type) +{ + if (m_level == type) + return; + + m_level = type; + m_changed = true; +} + +void LoggerCategory::setIgnored(bool isIgnored) +{ + Q_D(LoggerCategory); + d->setIgnored(isIgnored); +} + +void LoggerCategoryPrivate::setIgnored(bool isIgnored) +{ + if (m_ignored == isIgnored) + return; + + m_ignored = isIgnored; + m_changed = true; +} + +bool LoggerCategoryPrivate::hasChanged() const +{ + return m_changed; +} + +LoggerCategoryPrivate *LoggerCategoryPrivate::get(LoggerCategory *loggerCategory) +{ + Q_ASSERT(loggerCategory); + return loggerCategory->d_func(); +} + +} // namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljsloggingutils.h b/src/qmlcompiler/qqmljsloggingutils.h new file mode 100644 index 0000000000..029d2848f6 --- /dev/null +++ b/src/qmlcompiler/qqmljsloggingutils.h @@ -0,0 +1,118 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef QQMLJSLOGGER_H +#define QQMLJSLOGGER_H + +#include <QtCore/QFileInfo> +#include <qtqmlcompilerexports.h> + +QT_BEGIN_NAMESPACE + +class QQmlJSLoggerPrivate; +class QQmlJSScope; + +namespace QQmlSA { +class SourceLocation; +} + +namespace QQmlJS { +class LoggerCategoryPrivate; + +class Q_QMLCOMPILER_EXPORT LoggerWarningId +{ +public: + constexpr LoggerWarningId(QAnyStringView name) : m_name(name) { } + + const QAnyStringView name() const { return m_name; } + +private: + friend bool operator==(const LoggerWarningId &a, const LoggerWarningId &b) + { + return a.m_name == b.m_name; + } + + friend bool operator!=(const LoggerWarningId &a, const LoggerWarningId &b) + { + return a.m_name != b.m_name; + } + const QAnyStringView m_name; +}; + +class Q_QMLCOMPILER_EXPORT LoggerCategory +{ + Q_DECLARE_PRIVATE(LoggerCategory) + +public: + LoggerCategory(); + LoggerCategory(QString name, QString settingsName, QString description, QtMsgType level, + bool ignored = false, bool isDefault = false); + LoggerCategory(const LoggerCategory &); + LoggerCategory(LoggerCategory &&); + LoggerCategory &operator=(const LoggerCategory &); + LoggerCategory &operator=(LoggerCategory &&); + ~LoggerCategory(); + + QString name() const; + QString settingsName() const; + QString description() const; + QtMsgType level() const; + bool isIgnored() const; + bool isDefault() const; + + LoggerWarningId id() const; + + void setLevel(QtMsgType); + void setIgnored(bool); + + friend bool operator==(const LoggerCategory &category, const LoggerWarningId &warningId) + { + return category.name() == warningId.name(); + } + +private: + std::unique_ptr<QQmlJS::LoggerCategoryPrivate> d_ptr; +}; + +} // namespace QQmlJS + +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlRequired; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlUnresolvedAlias; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlAliasCycle; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlImport; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlRecursionDepthErrors; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlWith; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlInheritanceCycle; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlDeprecated; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlSignalParameters; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlMissingType; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlUnresolvedType; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlIncompatibleType; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlMissingProperty; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlRestrictedType; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlPrefixedImportType; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlNonListProperty; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlReadOnlyProperty; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlDuplicatePropertyBinding; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlDuplicatedName; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlDeferredPropertyId; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlUnqualified; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlUnusedImports; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlMultilineStrings; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlSyntax; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlSyntaxIdQuotation; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlSyntaxDuplicateIds; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlCompiler; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlAttachedPropertyReuse; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlPlugin; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlVarUsedBeforeDeclaration; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlInvalidLintDirective; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlUseProperFunction; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlAccessSingleton; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlTopLevelComponent; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlUncreatableType; +extern const Q_QMLCOMPILER_EXPORT QQmlJS::LoggerWarningId qmlMissingEnumEntry; + +QT_END_NAMESPACE + +#endif // QQMLJSLOGGER_H diff --git a/src/qmlcompiler/qqmljsloggingutils_p.h b/src/qmlcompiler/qqmljsloggingutils_p.h new file mode 100644 index 0000000000..06957c2ad1 --- /dev/null +++ b/src/qmlcompiler/qqmljsloggingutils_p.h @@ -0,0 +1,73 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef QQMLJSLOGGINGUTILS_P_H +#define QQMLJSLOGGINGUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtqmlcompilerexports_p.h> + +#include "qqmljsloggingutils.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +class LoggerCategoryPrivate +{ + friend class QT_PREPEND_NAMESPACE(QQmlJS::LoggerCategory); + +public: + LoggerWarningId id() const { return LoggerWarningId(m_name); } + + void setLevel(QtMsgType); + void setIgnored(bool); + + QString name() const; + QString settingsName() const; + QString description() const; + QtMsgType level() const; + bool isIgnored() const; + bool isDefault() const; + bool hasChanged() const; + + static LoggerCategoryPrivate *get(LoggerCategory *); + + friend bool operator==(const LoggerCategoryPrivate &lhs, const LoggerCategoryPrivate &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const LoggerCategoryPrivate &lhs, const LoggerCategoryPrivate &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + + bool operator==(const LoggerWarningId warningId) const { return warningId.name() == m_name; } + +private: + static bool operatorEqualsImpl(const LoggerCategoryPrivate &, const LoggerCategoryPrivate &); + + QString m_name; + QString m_settingsName; + QString m_description; + QtMsgType m_level; + bool m_ignored = false; + bool m_isDefault = false; // Whether or not the category can be disabled + bool m_changed = false; +}; + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QQMLJSLOGGINGUTILS_P_H diff --git a/src/qmlcompiler/qqmljsmetatypes.cpp b/src/qmlcompiler/qqmljsmetatypes.cpp index 9ba7d2ca54..a9d680bc22 100644 --- a/src/qmlcompiler/qqmljsmetatypes.cpp +++ b/src/qmlcompiler/qqmljsmetatypes.cpp @@ -13,7 +13,10 @@ QT_BEGIN_NAMESPACE A binding is valid when it has both a target (m_propertyName is set) and some content set (m_bindingType != Invalid). */ -bool QQmlJSMetaPropertyBinding::isValid() const { return !m_propertyName.isEmpty() && bindingType() != Invalid; } +bool QQmlJSMetaPropertyBinding::isValid() const +{ + return !m_propertyName.isEmpty() && bindingType() != QQmlSA::BindingType::Invalid; +} QString QQmlJSMetaPropertyBinding::literalTypeName() const { @@ -93,28 +96,30 @@ QSharedPointer<const QQmlJSScope> QQmlJSMetaPropertyBinding::literalType(const Q { Q_ASSERT(resolver); switch (bindingType()) { - case QQmlJSMetaPropertyBinding::BoolLiteral: + case BindingType::BoolLiteral: return resolver->boolType(); - case QQmlJSMetaPropertyBinding::NumberLiteral: + case BindingType::NumberLiteral: return resolver->typeForName(QLatin1String("double")); - case QQmlJSMetaPropertyBinding::Translation: // translations are strings - case QQmlJSMetaPropertyBinding::TranslationById: - case QQmlJSMetaPropertyBinding::StringLiteral: + case BindingType::Translation: // translations are strings + case BindingType::TranslationById: + case BindingType::StringLiteral: return resolver->stringType(); - case QQmlJSMetaPropertyBinding::RegExpLiteral: + case BindingType::RegExpLiteral: return resolver->typeForName(QLatin1String("regexp")); - case QQmlJSMetaPropertyBinding::Null: + case BindingType::Null: return resolver->nullType(); - case QQmlJSMetaPropertyBinding::Invalid: - case QQmlJSMetaPropertyBinding::Script: - case QQmlJSMetaPropertyBinding::Object: - case QQmlJSMetaPropertyBinding::Interceptor: - case QQmlJSMetaPropertyBinding::ValueSource: - case QQmlJSMetaPropertyBinding::AttachedProperty: - case QQmlJSMetaPropertyBinding::GroupProperty: + case BindingType::Invalid: + case BindingType::Script: + case BindingType::Object: + case BindingType::Interceptor: + case BindingType::ValueSource: + case BindingType::AttachedProperty: + case BindingType::GroupProperty: return {}; } Q_UNREACHABLE_RETURN({}); } +QQmlJSMetaPropertyBinding::QQmlJSMetaPropertyBinding() = default; + QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h index 6d6f9fef36..be45c65e60 100644 --- a/src/qmlcompiler/qqmljsmetatypes_p.h +++ b/src/qmlcompiler/qqmljsmetatypes_p.h @@ -23,9 +23,10 @@ #include <QtCore/qhash.h> #include <QtQml/private/qqmljssourcelocation_p.h> - #include <QtQml/private/qqmltranslation_p.h> +#include "qqmlsaconstants.h" +#include "qqmlsa.h" #include "qqmljsannotation_p.h" // MetaMethod and MetaProperty have both type names and actual QQmlJSScope types. @@ -40,6 +41,13 @@ QT_BEGIN_NAMESPACE +enum ScriptBindingValueType : unsigned int { + ScriptValue_Unknown, + ScriptValue_Undefined // property int p: undefined +}; + +using QQmlJSMetaMethodType = QQmlSA::MethodType; + class QQmlJSTypeResolver; class QQmlJSScope; class QQmlJSMetaEnum @@ -172,9 +180,8 @@ private: class QQmlJSMetaMethod { public: - enum Type { Signal, Slot, Method, StaticMethod }; - enum Access { Private, Protected, Public }; + using MethodType = QQmlJSMetaMethodType; public: /*! \internal @@ -195,9 +202,9 @@ public: QQmlJSMetaMethod() = default; explicit QQmlJSMetaMethod(QString name, QString returnType = QString()) - : m_name(std::move(name)) - , m_returnTypeName(std::move(returnType)) - , m_methodType(Method) + : m_name(std::move(name)), + m_returnTypeName(std::move(returnType)), + m_methodType(MethodType::Method) {} QString methodName() const { return m_name; } @@ -226,8 +233,8 @@ public: void addParameter(const QQmlJSMetaParameter &p) { m_parameters.append(p); } - int methodType() const { return m_methodType; } - void setMethodType(Type methodType) { m_methodType = methodType; } + QQmlJSMetaMethodType methodType() const { return m_methodType; } + void setMethodType(MethodType methodType) { m_methodType = methodType; } Access access() const { return m_methodAccess; } @@ -323,7 +330,7 @@ private: QList<QQmlJSMetaParameter> m_parameters; QList<QQmlJSAnnotation> m_annotations; - Type m_methodType = Signal; + MethodType m_methodType = MethodType::Signal; Access m_methodAccess = Public; int m_revision = 0; RelativeFunctionIndex m_relativeFunctionIndex = RelativeFunctionIndex::Invalid; @@ -450,37 +457,8 @@ public: */ class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSMetaPropertyBinding { -public: - enum BindingType : unsigned int { - Invalid, - BoolLiteral, - NumberLiteral, - StringLiteral, - RegExpLiteral, - Null, - Translation, - TranslationById, - Script, - Object, - Interceptor, - ValueSource, - AttachedProperty, - GroupProperty, - }; - - enum ScriptBindingKind : unsigned int { - Script_Invalid, - Script_PropertyBinding, // property int p: 1 + 1 - Script_SignalHandler, // onSignal: { ... } - Script_ChangeHandler, // onXChanged: { ... } - }; - - enum ScriptBindingValueType : unsigned int { - ScriptValue_Unknown, - ScriptValue_Undefined // property int p: undefined - }; - -private: + using BindingType = QQmlSA::BindingType; + using ScriptBindingKind = QQmlSA::ScriptBindingKind; // needs to be kept in sync with the BindingType enum struct Content { @@ -542,8 +520,8 @@ private: friend bool operator!=(Script a, Script b) { return !(a == b); } QQmlJSMetaMethod::RelativeFunctionIndex index = QQmlJSMetaMethod::RelativeFunctionIndex::Invalid; - ScriptBindingKind kind = Script_Invalid; - ScriptBindingValueType valueType = ScriptValue_Unknown; + ScriptBindingKind kind = ScriptBindingKind::Script_Invalid; + ScriptBindingValueType valueType = ScriptBindingValueType::ScriptValue_Unknown; }; struct Object { friend bool operator==(Object a, Object b) { return a.value == b.value && a.typeName == b.typeName; } @@ -645,6 +623,7 @@ public: || type == BindingType::Null; // special. we record it as literal } + QQmlJSMetaPropertyBinding(); QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation location) : m_sourceLocation(location) { } explicit QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation location, const QString &propName) : m_sourceLocation(location), m_propertyName(propName) @@ -671,8 +650,9 @@ public: m_bindingContent = Content::StringLiteral { value.toString() }; } - void setScriptBinding(QQmlJSMetaMethod::RelativeFunctionIndex value, ScriptBindingKind kind, - ScriptBindingValueType valueType = ScriptValue_Unknown) + void + setScriptBinding(QQmlJSMetaMethod::RelativeFunctionIndex value, ScriptBindingKind kind, + ScriptBindingValueType valueType = ScriptBindingValueType::ScriptValue_Unknown) { ensureSetBindingTypeOnce(); m_bindingContent = Content::Script { value, kind, valueType }; diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index 1b7811f1c9..a953a7ee89 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -5,6 +5,8 @@ #include "qqmljstypereader_p.h" #include "qqmljsimporter_p.h" #include "qqmljsutils_p.h" +#include "qqmlsa.h" +#include "qqmlsa_p.h" #include <QtCore/qqueue.h> #include <QtCore/qsharedpointer.h> @@ -33,6 +35,11 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +QQmlJSScope::QQmlJSScope(const QString &internalName) : QQmlJSScope{} +{ + m_internalName = internalName; +} + void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope) { if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef()) @@ -55,14 +62,14 @@ QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin) void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier) { - Q_ASSERT(m_scopeType != QQmlJSScope::QMLScope); + Q_ASSERT(m_scopeType != QQmlSA::ScopeType::QMLScope); if (identifier.kind == JavaScriptIdentifier::LexicalScoped - || identifier.kind == JavaScriptIdentifier::Injected - || m_scopeType == QQmlJSScope::JSFunctionScope) { + || identifier.kind == JavaScriptIdentifier::Injected + || m_scopeType == QQmlSA::ScopeType::JSFunctionScope) { m_jsIdentifiers.insert(name, identifier); } else { auto targetScope = parentScope(); - while (targetScope->m_scopeType != QQmlJSScope::JSFunctionScope) + while (targetScope->m_scopeType != QQmlSA::ScopeType::JSFunctionScope) targetScope = targetScope->parentScope(); targetScope->m_jsIdentifiers.insert(name, identifier); } @@ -72,7 +79,7 @@ void QQmlJSScope::insertPropertyIdentifier(const QQmlJSMetaProperty &property) { addOwnProperty(property); QQmlJSMetaMethod method(property.propertyName() + u"Changed"_s, u"void"_s); - method.setMethodType(QQmlJSMetaMethod::Signal); + method.setMethodType(QQmlJSMetaMethodType::Signal); method.setIsImplicitQmlPropertyChangeSignal(true); addOwnMethod(method); } @@ -133,7 +140,7 @@ QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const return results; } -QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethod::Type type) const +QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethodType type) const { QList<QQmlJSMetaMethod> results; @@ -283,7 +290,7 @@ bool QQmlJSScope::isComponentRootElement() const { bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const { - if (m_scopeType == QQmlJSScope::QMLScope) + if (m_scopeType == QQmlSA::ScopeType::QMLScope) return m_properties.contains(id) || m_methods.contains(id) || m_enumerations.contains(id); const auto qmlScope = findCurrentQMLScope(parentScope()); @@ -294,11 +301,12 @@ bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const bool QQmlJSScope::isIdInCurrentJSScopes(const QString &id) const { - if (m_scopeType != QQmlJSScope::QMLScope && m_jsIdentifiers.contains(id)) + if (m_scopeType != QQmlSA::ScopeType::QMLScope && m_jsIdentifiers.contains(id)) return true; for (auto jsScope = parentScope(); jsScope; jsScope = jsScope->parentScope()) { - if (jsScope->m_scopeType != QQmlJSScope::QMLScope && jsScope->m_jsIdentifiers.contains(id)) + if (jsScope->m_scopeType != QQmlSA::ScopeType::QMLScope + && jsScope->m_jsIdentifiers.contains(id)) return true; } @@ -315,8 +323,8 @@ std::optional<QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::findJSIdentifier(const QString &id) const { for (const auto *scope = this; scope; scope = scope->parentScope().data()) { - if (scope->m_scopeType == QQmlJSScope::JSFunctionScope - || scope->m_scopeType == QQmlJSScope::JSLexicalScope) { + if (scope->m_scopeType == QQmlSA::ScopeType::JSFunctionScope + || scope->m_scopeType == QQmlSA::ScopeType::JSLexicalScope) { auto it = scope->m_jsIdentifiers.find(id); if (it != scope->m_jsIdentifiers.end()) return *it; @@ -545,7 +553,7 @@ void QQmlJSScope::updateChildScope( const QQmlJSScope::ContextualTypes &contextualTypes, QSet<QString> *usedTypes) { switch (childScope->scopeType()) { - case QQmlJSScope::GroupedPropertyScope: + case QQmlSA::ScopeType::GroupedPropertyScope: QQmlJSUtils::searchBaseAndExtensionTypes( self.data(), [&](const QQmlJSScope *type, QQmlJSScope::ExtensionKind mode) { if (mode == QQmlJSScope::ExtensionNamespace) @@ -561,7 +569,7 @@ void QQmlJSScope::updateChildScope( return false; }); break; - case QQmlJSScope::AttachedPropertyScope: + case QQmlSA::ScopeType::AttachedPropertyScope: if (const auto attachedBase = findType( childScope->internalName(), contextualTypes, usedTypes).scope) { childScope->m_baseType.scope = attachedBase->attachedType(); @@ -627,13 +635,12 @@ void QQmlJSScope::resolveEnums( { // temporary hash to avoid messing up m_enumerations while iterators are active on it QHash<QString, QQmlJSMetaEnum> toBeAppended; - for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; - ++it) { + for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; ++it) { if (it->type()) continue; QQmlJSScope::Ptr enumScope = QQmlJSScope::create(); reparent(self, enumScope); - enumScope->m_scopeType = EnumScope; + enumScope->m_scopeType = QQmlSA::ScopeType::EnumScope; QString typeName = it->typeName(); if (typeName.isEmpty()) @@ -709,7 +716,7 @@ void QQmlJSScope::resolveGeneralizedGroup( QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope) { auto qmlScope = scope; - while (qmlScope && qmlScope->m_scopeType != QQmlJSScope::QMLScope) + while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope) qmlScope = qmlScope->parentScope(); return qmlScope; } @@ -1236,7 +1243,8 @@ QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineCompo bool QQmlJSScope::isCreatable() const { auto isCreatableNonRecursive = [](const QQmlJSScope *scope) { - return scope->hasCreatableFlag() && !scope->isSingleton() && scope->scopeType() == QMLScope; + return scope->hasCreatableFlag() && !scope->isSingleton() + && scope->scopeType() == QQmlSA::ScopeType::QMLScope; }; for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) { @@ -1252,4 +1260,26 @@ bool QQmlJSScope::isCreatable() const // no uncreatable bases found return false; } + +QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr) +{ + QQmlSA::Element element; + auto &wrappedPtr = reinterpret_cast<QQmlJSScope::ConstPtr &>(element.m_data); + wrappedPtr = ptr; + return element; +} + +QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr) +{ + QQmlSA::Element element; + auto &wrappedPtr = reinterpret_cast<QQmlJSScope::ConstPtr &>(element.m_data); + wrappedPtr = std::move(ptr); + return element; +} + +const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element) +{ + return reinterpret_cast<const QQmlJSScope::ConstPtr &>(element.m_data); +} + QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index f154a5cf39..053ba4127f 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -19,6 +19,8 @@ #include "qqmljsmetatypes_p.h" #include "qdeferredpointer_p.h" #include "qqmljsannotation_p.h" +#include "qqmlsaconstants.h" +#include "qqmlsa_p.h" #include <QtQml/private/qqmljssourcelocation_p.h> @@ -27,6 +29,7 @@ #include <QtCore/qset.h> #include <QtCore/qstring.h> #include <QtCore/qversionnumber.h> +#include "qqmlsaconstants.h" #include <optional> @@ -34,9 +37,69 @@ QT_BEGIN_NAMESPACE class QQmlJSImporter; +namespace QQmlJS { + +class ConstPtrWrapperIterator +{ +public: + using Ptr = QDeferredSharedPointer<QQmlJSScope>; + using ConstPtr = QDeferredSharedPointer<const QQmlJSScope>; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = ConstPtr; + using pointer = value_type *; + using reference = value_type &; + + ConstPtrWrapperIterator(QList<Ptr>::const_iterator iterator) : m_iterator(iterator) { } + + friend bool operator==(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b) + { + return a.m_iterator == b.m_iterator; + } + friend bool operator!=(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b) + { + return a.m_iterator != b.m_iterator; + } + + reference operator*() + { + if (!m_pointer) + m_pointer = *m_iterator; + return m_pointer; + } + pointer operator->() + { + if (!m_pointer) + m_pointer = *m_iterator; + return &m_pointer; + } + + ConstPtrWrapperIterator &operator++() + { + m_iterator++; + m_pointer = {}; + return *this; + } + ConstPtrWrapperIterator operator++(int) + { + auto before = *this; + ++(*this); + return before; + } + +private: + QList<Ptr>::const_iterator m_iterator; + ConstPtr m_pointer; +}; + +} // namespace QQmlJS + class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSScope { + friend QQmlSA::Element; + public: + explicit QQmlJSScope(const QString &internalName); QQmlJSScope(QQmlJSScope &&) = default; QQmlJSScope &operator=(QQmlJSScope &&) = default; @@ -45,6 +108,9 @@ public: using ConstPtr = QDeferredSharedPointer<const QQmlJSScope>; using WeakConstPtr = QDeferredWeakPointer<const QQmlJSScope>; + using AccessSemantics = QQmlSA::AccessSemantics; + using ScopeType = QQmlSA::ScopeType; + using InlineComponentNameType = QString; using RootDocumentNameType = std::monostate; // an empty type that has std::hash /*! @@ -53,23 +119,6 @@ public: using InlineComponentOrDocumentRootName = std::variant<InlineComponentNameType, RootDocumentNameType>; - enum ScopeType - { - JSFunctionScope, - JSLexicalScope, - QMLScope, - GroupedPropertyScope, - AttachedPropertyScope, - EnumScope - }; - - enum class AccessSemantics { - Reference, - Value, - None, - Sequence - }; - enum Flag { Creatable = 0x1, Composite = 0x2, @@ -86,60 +135,6 @@ public: Q_DECLARE_FLAGS(Flags, Flag) Q_FLAGS(Flags); - class ConstPtrWrapperIterator - { - public: - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = ConstPtr; - using pointer = value_type *; - using reference = value_type &; - - ConstPtrWrapperIterator(QList<QQmlJSScope::Ptr>::const_iterator iterator) - : m_iterator(iterator) - { - } - - friend bool operator==(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b) - { - return a.m_iterator == b.m_iterator; - } - friend bool operator!=(const ConstPtrWrapperIterator &a, const ConstPtrWrapperIterator &b) - { - return a.m_iterator != b.m_iterator; - } - - reference operator*() - { - if (!m_pointer) - m_pointer = *m_iterator; - return m_pointer; - } - pointer operator->() - { - if (!m_pointer) - m_pointer = *m_iterator; - return &m_pointer; - } - - ConstPtrWrapperIterator &operator++() - { - m_iterator++; - m_pointer = {}; - return *this; - } - ConstPtrWrapperIterator operator++(int) - { - auto before = *this; - ++(*this); - return before; - } - - private: - QList<QQmlJSScope::Ptr>::const_iterator m_iterator; - QQmlJSScope::ConstPtr m_pointer; - }; - class Import { public: @@ -292,6 +287,10 @@ public: }; static QQmlJSScope::Ptr create() { return QSharedPointer<QQmlJSScope>(new QQmlJSScope); } + static QQmlJSScope::Ptr create(const QString &internalName) + { + return QSharedPointer<QQmlJSScope>(new QQmlJSScope(internalName)); + } static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin); static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope); @@ -330,7 +329,7 @@ QT_WARNING_POP bool hasMethod(const QString &name) const; QHash<QString, QQmlJSMetaMethod> methods() const; QList<QQmlJSMetaMethod> methods(const QString &name) const; - QList<QQmlJSMetaMethod> methods(const QString &name, QQmlJSMetaMethod::Type type) const; + QList<QQmlJSMetaMethod> methods(const QString &name, QQmlJSMetaMethodType type) const; void addOwnEnumeration(const QQmlJSMetaEnum &enumeration) { m_enumerations.insert(enumeration.name(), enumeration); } QHash<QString, QQmlJSMetaEnum> ownEnumerations() const { return m_enumerations; } @@ -574,8 +573,8 @@ QT_WARNING_POP std::optional<JavaScriptIdentifier> findJSIdentifier(const QString &id) const; std::optional<JavaScriptIdentifier> JSIdentifier(const QString &id) const; - ConstPtrWrapperIterator childScopesBegin() const { return m_childScopes.constBegin(); } - ConstPtrWrapperIterator childScopesEnd() const { return m_childScopes.constEnd(); } + QQmlJS::ConstPtrWrapperIterator childScopesBegin() const { return m_childScopes.constBegin(); } + QQmlJS::ConstPtrWrapperIterator childScopesEnd() const { return m_childScopes.constEnd(); } void setInlineComponentName(const QString &inlineComponentName) { @@ -712,6 +711,11 @@ QT_WARNING_POP const ContextualTypes &contextualTypes, QSet<QString> *usedTypes = nullptr); + static QQmlSA::Element createQQmlSAElement(const ConstPtr &); + static QQmlSA::Element createQQmlSAElement(ConstPtr &&); + static const QQmlJSScope::ConstPtr &scope(const QQmlSA::Element &); + static constexpr qsizetype sizeofQQmlSAElement() { return QQmlSA::Element::sizeofElement; } + private: QQmlJSScope() = default; QQmlJSScope(const QQmlJSScope &) = default; @@ -753,7 +757,7 @@ private: // the only relation between two types where the revisions matter. ImportedScope<QQmlJSScope::WeakConstPtr> m_baseType; - ScopeType m_scopeType = QMLScope; + ScopeType m_scopeType = ScopeType::QMLScope; QStringList m_interfaceNames; QStringList m_ownDeferredNames; QStringList m_ownImmediateNames; diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp index f33fb75ec6..7a14970714 100644 --- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp +++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp @@ -269,9 +269,9 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod( QQmlJSMetaMethod metaMethod; // ### confusion between Method and Slot. Method should be removed. if (isMethod) - metaMethod.setMethodType(QQmlJSMetaMethod::Slot); + metaMethod.setMethodType(QQmlJSMetaMethodType::Slot); else - metaMethod.setMethodType(QQmlJSMetaMethod::Signal); + metaMethod.setMethodType(QQmlJSMetaMethodType::Signal); for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { UiObjectMember *member = it->member; diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 609f8d8100..3f5ea22988 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -9,6 +9,8 @@ #include <private/qv4compilerscanfunctions_p.h> +#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h> + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -59,10 +61,9 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run( return m_state.annotations; } -#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \ - setError(u"Instruction \"%1\" not implemented"_s \ - .arg(QString::fromUtf8(__func__))); \ - return; +#define INSTR_PROLOGUE_NOT_IMPLEMENTED() \ + setError(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__))); \ + return; #define INSTR_PROLOGUE_NOT_IMPLEMENTED_IGNORE() \ m_logger->log(u"Instruction \"%1\" not implemented"_s.arg(QString::fromUtf8(__func__)), \ @@ -72,9 +73,12 @@ QQmlJSCompilePass::InstructionAnnotations QQmlJSTypePropagator::run( void QQmlJSTypePropagator::generate_Ret() { if (m_passManager != nullptr && m_function->isProperty) { - m_passManager->analyzeBinding(m_function->qmlScope, - m_typeResolver->containedType(m_state.accumulatorIn()), - getCurrentBindingSourceLocation()); + m_passManager->d_func()->analyzeBinding( + QQmlJSScope::createQQmlSAElement(m_function->qmlScope), + QQmlJSScope::createQQmlSAElement( + m_typeResolver->containedType(m_state.accumulatorIn())), + QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + getCurrentBindingSourceLocation())); } if (m_function->isSignalHandler) { @@ -500,15 +504,17 @@ bool QQmlJSTypePropagator::isCallingProperty(QQmlJSScope::ConstPtr scope, const if (!methods.isEmpty()) { errorType = u"shadowed by a property."_s; switch (methods.first().methodType()) { - case QQmlJSMetaMethod::Signal: + case QQmlJSMetaMethodType::Signal: propertyType = u"Signal"_s; break; - case QQmlJSMetaMethod::Slot: + case QQmlJSMetaMethodType::Slot: propertyType = u"Slot"_s; break; - case QQmlJSMetaMethod::Method: + case QQmlJSMetaMethodType::Method: propertyType = u"Method"_s; break; + default: + Q_UNREACHABLE(); } } else if (m_typeResolver->equals(property.type(), m_typeResolver->varType())) { errorType = @@ -569,8 +575,11 @@ void QQmlJSTypePropagator::generate_LoadQmlContextPropertyLookup(int index) } if (m_passManager != nullptr) { - m_passManager->analyzeRead(m_function->qmlScope, name, m_function->qmlScope, - getCurrentSourceLocation()); + m_passManager->d_func()->analyzeRead( + QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name, + QQmlJSScope::createQQmlSAElement(m_function->qmlScope), + QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + getCurrentBindingSourceLocation())); } if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ScopeAttached) @@ -608,9 +617,12 @@ void QQmlJSTypePropagator::generate_StoreNameSloppy(int nameIndex) } if (m_passManager != nullptr) { - m_passManager->analyzeWrite(m_function->qmlScope, name, - m_typeResolver->containedType(in), - m_function->qmlScope, getCurrentSourceLocation()); + m_passManager->d_func()->analyzeWrite( + QQmlJSScope::createQQmlSAElement(m_function->qmlScope), name, + QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(in)), + QQmlJSScope::createQQmlSAElement(m_function->qmlScope), + QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + getCurrentBindingSourceLocation())); } m_state.setHasSideEffects(true); @@ -825,9 +837,14 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName) const bool isAttached = m_state.accumulatorIn().variant() == QQmlJSRegisterContent::ObjectAttached; - m_passManager->analyzeRead( - m_typeResolver->containedType(m_state.accumulatorIn()), propertyName, - isAttached ? m_attachedContext : m_function->qmlScope, getCurrentSourceLocation()); + m_passManager->d_func()->analyzeRead( + QQmlJSScope::createQQmlSAElement( + m_typeResolver->containedType(m_state.accumulatorIn())), + propertyName, + QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext + : m_function->qmlScope), + QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + getCurrentBindingSourceLocation())); } if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectAttached) @@ -902,10 +919,15 @@ void QQmlJSTypePropagator::generate_StoreProperty(int nameIndex, int base) if (m_passManager != nullptr) { const bool isAttached = callBase.variant() == QQmlJSRegisterContent::ObjectAttached; - m_passManager->analyzeWrite(m_typeResolver->containedType(callBase), propertyName, - m_typeResolver->containedType(m_state.accumulatorIn()), - isAttached ? m_attachedContext : m_function->qmlScope, - getCurrentSourceLocation()); + m_passManager->d_func()->analyzeWrite( + QQmlJSScope::createQQmlSAElement(m_typeResolver->containedType(callBase)), + propertyName, + QQmlJSScope::createQQmlSAElement( + m_typeResolver->containedType(m_state.accumulatorIn())), + QQmlJSScope::createQQmlSAElement(isAttached ? m_attachedContext + : m_function->qmlScope), + QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + getCurrentBindingSourceLocation())); } m_state.setHasSideEffects(true); @@ -1068,9 +1090,11 @@ 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( - baseType, - propertyName, m_function->qmlScope, getCurrentSourceLocation()); + m_passManager->d_func()->analyzeRead( + QQmlJSScope::createQQmlSAElement(baseType), propertyName, + QQmlJSScope::createQQmlSAElement(m_function->qmlScope), + QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + getCurrentBindingSourceLocation())); } addReadRegister(base, callBase); @@ -1977,7 +2001,7 @@ void QQmlJSTypePropagator::recordEqualsType(int lhs) const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) { auto contained = m_typeResolver->containedType(content); - if (contained->scopeType() == QQmlJSScope::EnumScope) + if (contained->scopeType() == QQmlSA::ScopeType::EnumScope) contained = contained->baseType(); return m_typeResolver->isIntegral(contained) && !m_typeResolver->equals(contained, m_typeResolver->uint32Type()); diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 49a3eabd37..e17dc5aebe 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -812,7 +812,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType( if (type->isListProperty()) return m_listPropertyType; - if (type->scopeType() == QQmlJSScope::EnumScope) + if (type->scopeType() == QQmlSA::ScopeType::EnumScope) return type->baseType(); if (isPrimitive(type)) @@ -887,7 +887,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr const QList<QQmlJSMetaPropertyBinding> defaultPropBindings = parent->propertyBindings(defaultPropertyName); for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) { - if (binding.bindingType() == QQmlJSMetaPropertyBinding::Object + if (binding.bindingType() == QQmlSA::BindingType::Object && equals(binding.objectType(), child)) { return true; } diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h index 956d946980..5e4f6004f8 100644 --- a/src/qmlcompiler/qqmljsutils_p.h +++ b/src/qmlcompiler/qqmljsutils_p.h @@ -141,7 +141,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSUtils const auto base = scope->baseType(); if (!base) return false; - return base->isComposite() && base->scopeType() == QQmlJSScope::QMLScope; + return base->isComposite() && base->scopeType() == QQmlSA::ScopeType::QMLScope; } enum PropertyAccessor { diff --git a/src/qmlcompiler/qqmlsa.cpp b/src/qmlcompiler/qqmlsa.cpp index c9296491f5..92091e14ec 100644 --- a/src/qmlcompiler/qqmlsa.cpp +++ b/src/qmlcompiler/qqmlsa.cpp @@ -1,15 +1,21 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "qqmlsa.h" #include "qqmlsa_p.h" +#include "qqmlsasourcelocation.h" #include "qqmljsscope_p.h" #include "qqmljslogger_p.h" #include "qqmljstyperesolver_p.h" #include "qqmljsimportvisitor_p.h" #include "qqmljsutils_p.h" +#include "qdeferredpointer_p.h" + +#include <QtQmlCompiler/private/qqmlsasourcelocation_p.h> #include <memory> +#include <new> QT_BEGIN_NAMESPACE @@ -17,63 +23,959 @@ using namespace Qt::StringLiterals; namespace QQmlSA { +static_assert(QQmlJSScope::sizeofQQmlSAElement() == sizeof(Element)); + +/*! + \namespace QQmlSA + \inmodule QtQmlCompiler + + \brief Provides tools for static analysis on QML programs. + */ + +/*! + \class QQmlSA::Binding::Bindings + \inmodule QtQmlCompiler + + \brief Holds multiple property name to property binding associations. + */ + +Binding::Bindings::Bindings() : d_ptr{ new BindingsPrivate{ this } } { } + +BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface) : q_ptr{ interface } { } + +Binding::Bindings::Bindings(const Bindings &other) + : d_ptr{ new BindingsPrivate{ this, *other.d_func() } } +{ +} + +Binding::Bindings::~Bindings() = default; + +BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, const BindingsPrivate &other) + : m_bindings{ other.m_bindings.begin(), other.m_bindings.end() }, q_ptr{ interface } +{ +} + +BindingsPrivate::BindingsPrivate(QQmlSA::Binding::Bindings *interface, BindingsPrivate &&other) + : m_bindings{ std::move(other.m_bindings) }, q_ptr{ interface } +{ +} + +/*! + Returns an iterator to the beginning of the bindings. + */ +QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constBegin() const +{ + Q_D(const Bindings); + return d->constBegin(); +} + +QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constBegin() const +{ + return m_bindings.constBegin(); +} + +/*! + Returns an iterator to the end of the bindings. + */ +QMultiHash<QString, Binding>::const_iterator Binding::Bindings::constEnd() const +{ + Q_D(const Bindings); + return d->constEnd(); +} + +QMultiHash<QString, Binding>::const_iterator BindingsPrivate::constEnd() const +{ + return m_bindings.constEnd(); +} + +/*! + \class QQmlSA::Binding + \inmodule QtQmlCompiler + + \brief Represents a single QML property binding for a specific type. + */ + +Binding::Binding() : d_ptr{ new BindingPrivate{ this } } { } + +BindingPrivate::BindingPrivate(Binding *interface) : q_ptr{ interface } { } + +Binding::Binding(const Binding &other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { } + +Binding::Binding(Binding &&other) : d_ptr{ new BindingPrivate{ this, *other.d_func() } } { } + +Binding &Binding::operator=(const Binding &other) +{ + if (*this == other) + return *this; + + d_func()->m_binding = other.d_func()->m_binding; + d_func()->q_ptr = this; + return *this; +} + +Binding &Binding::operator=(Binding &&other) +{ + if (*this == other) + return *this; + + d_func()->m_binding = std::move(other.d_func()->m_binding); + d_func()->q_ptr = this; + return *this; +} + +Binding::~Binding() = default; + +bool Binding::operatorEqualsImpl(const Binding &lhs, const Binding &rhs) +{ + return lhs.d_func()->m_binding == rhs.d_func()->m_binding; +} + +BindingPrivate::BindingPrivate(Binding *interface, const BindingPrivate &other) + : m_binding{ other.m_binding }, q_ptr{ interface } +{ +} + +QQmlSA::Binding BindingPrivate::createBinding(const QQmlJSMetaPropertyBinding &binding) +{ + QQmlSA::Binding saBinding; + saBinding.d_func()->m_binding = binding; + return saBinding; +} + +QQmlJSMetaPropertyBinding BindingPrivate::binding(QQmlSA::Binding &binding) +{ + return binding.d_func()->m_binding; +} + +const QQmlJSMetaPropertyBinding BindingPrivate::binding(const QQmlSA::Binding &binding) +{ + return binding.d_func()->m_binding; +} + +/*! + Returns the type of the property if this element is a group property, + otherwise returns an invalid Element. + */ +Element Binding::groupType() const +{ + return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).groupType()); +} + +QQmlSA::BindingType Binding::bindingType() const +{ + return BindingPrivate::binding(*this).bindingType(); +} + +/*! + Returns the associated string literal if the content type of this binding is + StringLiteral, otherwise returns an empty string. + */ +QString Binding::stringValue() const +{ + return BindingPrivate::binding(*this).stringValue(); +} + +/*! + Returns the name of the property using this binding. + */ +QString Binding::propertyName() const +{ + return BindingPrivate::binding(*this).propertyName(); +} + +/*! + Returns the attached type if the content type of this binding is + AttachedProperty, otherwise returns an invalid Element. + */ +Element Binding::attachingType() const +{ + return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).attachingType()); +} + +/*! + Returns the location in the QML code where this binding is defined. + */ +QQmlSA::SourceLocation Binding::sourceLocation() const +{ + return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + BindingPrivate::binding(*this).sourceLocation()); +} + +/*! + Returns the associated number if the content type of this binding is + NumberLiteral, otherwise returns 0. + */ +double Binding::numberValue() const +{ + return BindingPrivate::binding(*this).numberValue(); +} + +/*! + Returns the kind of associated associated script if the content type of + this binding is Script, otherwise returns Script_Invalid. + */ +QQmlSA::ScriptBindingKind Binding::scriptKind() const +{ + return BindingPrivate::binding(*this).scriptKind(); +} + +/*! + Returns \c true if this binding has an objects, otherwise returns \c false. + */ +bool Binding::hasObject() const +{ + return BindingPrivate::binding(*this).hasObject(); +} + +/*! + Returns the type of the associated object if the content type of this + binding is Object, otherwise returns an invlaid Element. + */ +QQmlSA::Element Binding::objectType() const +{ + return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).objectType()); +} + +Element QQmlSA::Binding::literalType(const QQmlJSTypeResolver *resolver) const +{ + return QQmlJSScope::createQQmlSAElement(BindingPrivate::binding(*this).literalType(resolver)); +} + +bool Binding::hasUndefinedScriptValue() const +{ + const auto &jsBinding = BindingPrivate::binding(*this); + return jsBinding.bindingType() == Script + && jsBinding.scriptValueType() == ScriptValue_Undefined; +} + +/*! + Returns \c true if \a bindingType is a literal type, and \c false + otherwise. Literal types include strings, booleans, numbers, regular + expressions among other. + */ +bool QQmlSA::Binding::isLiteralBinding(QQmlSA::BindingType bindingType) +{ + return QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType); +} + +QQmlSA::Method::Methods::Methods() : d_ptr{ new MethodsPrivate{ this } } { } + +QQmlSA::Method::Methods::Methods(const Methods &other) + : d_ptr{ new MethodsPrivate{ this, *other.d_func() } } +{ +} + +QQmlSA::Method::Methods::~Methods() = default; + +/*! + Returns an iterator to the beginning of the methods. + */ +QMultiHash<QString, Method>::const_iterator Method::Methods::constBegin() const +{ + Q_D(const Methods); + return d->constBegin(); +} + +QMultiHash<QString, Method>::const_iterator MethodsPrivate::constBegin() const +{ + return m_methods.constBegin(); +} + +/*! + Returns an iterator to the end of the methods. + */ +QMultiHash<QString, Method>::const_iterator Method::Methods::constEnd() const +{ + Q_D(const Methods); + return d->constEnd(); +} +QMultiHash<QString, Method>::const_iterator MethodsPrivate::constEnd() const +{ + return m_methods.constEnd(); +} + +MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface) : q_ptr{ interface } { } + +MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, const MethodsPrivate &other) + : m_methods{ other.m_methods }, q_ptr{ interface } +{ +} + +MethodsPrivate::MethodsPrivate(QQmlSA::Method::Methods *interface, MethodsPrivate &&other) + : m_methods{ std::move(other.m_methods) }, q_ptr{ interface } +{ +} + +MethodPrivate::MethodPrivate(Method *interface) : q_ptr{ interface } { } + +MethodPrivate::MethodPrivate(Method *interface, const MethodPrivate &other) + : m_method{ other.m_method }, q_ptr{ interface } +{ +} + +QString MethodPrivate::methodName() const +{ + return m_method.methodName(); +} + +MethodType MethodPrivate::methodType() const +{ + return m_method.methodType(); +} + +/*! + \class QQmlSA::Method + \inmodule QtQmlCompiler + + \brief Represents a QML method. + */ + +Method::Method() : d_ptr{ new MethodPrivate{ this } } { } + +Method::Method(const Method &other) : d_ptr{ new MethodPrivate{ this, *other.d_func() } } { } + +Method::Method(Method &&other) : d_ptr{ new MethodPrivate{ this, std::move(*other.d_func()) } } { } + +Method &Method::operator=(const Method &other) +{ + if (*this == other) + return *this; + + d_func()->m_method = other.d_func()->m_method; + d_func()->q_ptr = this; + return *this; +} + +Method &Method::operator=(Method &&other) +{ + if (*this == other) + return *this; + + d_func()->m_method = std::move(other.d_func()->m_method); + d_func()->q_ptr = this; + return *this; +} + +Method::~Method() = default; + +/*! + Returns the name of the this method. + */ +QString Method::methodName() const +{ + Q_D(const Method); + return d->methodName(); +} + +/*! + Returns the type of this method. For example, Signal, Slot, Method or + StaticMethod. + */ +MethodType Method::methodType() const +{ + Q_D(const Method); + return d->methodType(); +} + +bool Method::operatorEqualsImpl(const Method &lhs, const Method &rhs) +{ + return lhs.d_func()->m_method == rhs.d_func()->m_method; +} + +QQmlSA::Method MethodPrivate::createMethod(const QQmlJSMetaMethod &jsMethod) +{ + QQmlSA::Method saMethod; + auto &wrappedMethod = saMethod.d_func()->m_method; + wrappedMethod = jsMethod; + return saMethod; +} + +QQmlSA::Method::Methods +MethodsPrivate::createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &hash) +{ + QMultiHash<QString, QQmlSA::Method> saMethods; + for (const auto &[key, value] : hash.asKeyValueRange()) { + saMethods.insert(key, MethodPrivate::createMethod(value)); + } + + QQmlSA::Method::Methods methods; + methods.d_func()->m_methods = std::move(saMethods); + return methods; +} + +QQmlJSMetaMethod MethodPrivate::method(const QQmlSA::Method &method) +{ + return method.d_func()->m_method; +} + +PropertyPrivate::PropertyPrivate(Property *interface) : q_ptr{ interface } { } + +PropertyPrivate::PropertyPrivate(Property *interface, const PropertyPrivate &other) + : m_property{ other.m_property }, q_ptr{ interface } +{ +} + +PropertyPrivate::PropertyPrivate(Property *interface, PropertyPrivate &&other) + : m_property{ std::move(other.m_property) }, q_ptr{ interface } +{ +} + +QString PropertyPrivate::typeName() const +{ + return m_property.typeName(); +} + +bool PropertyPrivate::isValid() const +{ + return m_property.isValid(); +} + +QQmlJSMetaProperty PropertyPrivate::property(const QQmlSA::Property &property) +{ + return property.d_func()->m_property; +} + +QQmlSA::Property PropertyPrivate::createProperty(const QQmlJSMetaProperty &property) +{ + QQmlSA::Property saProperty; + auto &wrappedProperty = saProperty.d_func()->m_property; + wrappedProperty = property; + return saProperty; +} + +/*! + \class QQmlSA::Property + \inmodule QtQmlCompiler + + \brief Represents a QML property. + */ + +Property::Property() : d_ptr{ new PropertyPrivate{ this } } { } + +Property::Property(const Property &other) + : d_ptr{ new PropertyPrivate{ this, *other.d_func() } } { } + +Property::Property(Property &&other) + : d_ptr{ new PropertyPrivate{ this, std::move(*other.d_func()) } } +{ +} + +Property &Property::operator=(const Property &other) +{ + if (*this == other) + return *this; + + d_func()->m_property = other.d_func()->m_property; + d_func()->q_ptr = this; + return *this; +} + +Property &Property::operator=(Property &&other) +{ + if (*this == other) + return *this; + + d_func()->m_property = std::move(other.d_func()->m_property); + d_func()->q_ptr = this; + return *this; +} + +Property::~Property() = default; + +/*! + Returns the name of the type of this property. + */ +QString Property::typeName() const +{ + Q_D(const Property); + return d->typeName(); +} + +bool Property::isValid() const +{ + Q_D(const Property); + return d->isValid(); +} + +bool Property::operatorEqualsImpl(const Property &lhs, const Property &rhs) +{ + return lhs.d_func()->m_property == rhs.d_func()->m_property; +} + +/*! + \class QQmlSA::Element + \inmodule QtQmlCompiler + + \brief Represents a QML type. + */ + +Element::Element() +{ + new (m_data) QQmlJSScope::ConstPtr(); +} + +Element::Element(const QString &internalName) +{ + new (m_data) QQmlJSScope::ConstPtr(std::move(QQmlJSScope::create(internalName))); +} + +Element::Element(const Element &other) +{ + new (m_data) QQmlJSScope::ConstPtr(QQmlJSScope::scope(other)); +} + +Element &Element::operator=(const Element &other) +{ + if (this == &other) + return *this; + + *reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data) = QQmlJSScope::scope(other); + return *this; +} + +Element::~Element() +{ + (*reinterpret_cast<QQmlJSScope::ConstPtr *>(m_data)).QQmlJSScope::ConstPtr::~ConstPtr(); +} + +/*! + Returns the type of Element's scope. + */ +QQmlJSScope::ScopeType Element::scopeType() const +{ + return QQmlJSScope::scope(*this)->scopeType(); +} + +/*! + Returns the Element this Element derives from. + */ +Element Element::baseType() const +{ + return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->baseType()); +} + +/*! + Returns the name of the Element this Element derives from. + */ +QString Element::baseTypeName() const +{ + return QQmlJSScope::scope(*this)->baseTypeName(); +} + +/*! + Returns the Element that encloses this Element. + */ +Element Element::parentScope() const +{ + return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->parentScope()); +} + +/*! + Returns whether this Element inherits from \a element. + */ +bool Element::inherits(const Element &element) const +{ + return QQmlJSScope::scope(*this)->inherits(QQmlJSScope::scope(element)); +} + +bool Element::isNull() const +{ + return QQmlJSScope::scope(*this).isNull(); +} + +QString Element::internalName() const +{ + return QQmlJSScope::scope(*this)->internalName(); +} + +/*! + Returns the access semantics of this Element. For example, Reference, + Value or Sequence. + */ +AccessSemantics Element::accessSemantics() const +{ + return QQmlJSScope::scope(*this)->accessSemantics(); +} + +/*! + Returns true for objects defined from Qml, and false for objects declared from C++. + */ +bool QQmlSA::Element::isComposite() const +{ + return QQmlJSScope::scope(*this)->isComposite(); +} + +/*! + Returns this Element's attached Element. + */ +Element Element::attachedType() const +{ + return QQmlJSScope::createQQmlSAElement(QQmlJSScope::scope(*this)->attachedType()); +} + +/*! + Returns the name of this Element's attached Element. + */ +QString Element::attachedTypeName() const +{ + return QQmlJSScope::scope(*this)->attachedTypeName(); +} + +/*! + Returns whether this Element has a property with the name \a propertyName. + */ +bool Element::hasProperty(const QString &propertyName) const +{ + return QQmlJSScope::scope(*this)->hasProperty(propertyName); +} + +/*! + Returns the property with the name \a propertyName if it is found in this + Element or its base and extension objects, otherwise returns an invalid property. + */ +QQmlSA::Property Element::property(const QString &propertyName) const +{ + return PropertyPrivate::createProperty(QQmlJSScope::scope(*this)->property(propertyName)); +} + +/*! + Returns whether the property with the name \a propertyName resolved on this + Element is required. Returns false if the the property couldn't be found. + */ +bool Element::isPropertyRequired(const QString &propertyName) const +{ + return QQmlJSScope::scope(*this)->isPropertyRequired(propertyName); +} + +/*! + Returns the name of the default property of this Element. If it doesn't + have one, returns an empty string. + */ +QString Element::defaultPropertyName() const +{ + return QQmlJSScope::scope(*this)->defaultPropertyName(); +} + +/*! + Returns whether this Element has a method with the name \a methodName. + */ +bool Element::hasMethod(const QString &methodName) const +{ + return QQmlJSScope::scope(*this)->hasMethod(methodName); +} + +/*! + \class QQmlSA::Method::Methods + \inmodule QtQmlCompiler + + \brief Holds multiple method name to method associations. + */ + +/*! + Returns this Elements's method which are not defined on its base or + extension objects. + */ +Method::Methods Element::ownMethods() const +{ + return MethodsPrivate::createMethods(QQmlJSScope::scope(*this)->ownMethods()); +} + +/*! + Returns the location in the QML code where this method is defined. + */ +QQmlSA::SourceLocation Element::sourceLocation() const +{ + return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + QQmlJSScope::scope(*this)->sourceLocation()); +} + +/*! + Returns the file path of the QML code that defines this method. + */ +QString Element::filePath() const +{ + return QQmlJSScope::scope(*this)->filePath(); +} + +/*! + Returns whethe this Element has a property binding with the name \a name. + */ +bool Element::hasPropertyBindings(const QString &name) const +{ + return QQmlJSScope::scope(*this)->hasPropertyBindings(name); +} + +/*! + Returns whether this Element has property bindings which are not defined in + its base or extension objects and that have name \a propertyName. + */ +bool Element::hasOwnPropertyBindings(const QString &propertyName) const +{ + return QQmlJSScope::scope(*this)->hasOwnPropertyBindings(propertyName); +} + +/*! + Returns this Element's property bindings which are not defined on its base + or extension objects. + */ +Binding::Bindings Element::ownPropertyBindings() const +{ + return BindingsPrivate::createBindings(QQmlJSScope::scope(*this)->ownPropertyBindings()); +} + +/*! + Returns this Element's property bindings which are not defined on its base + or extension objects and that have the name \a propertyName. + */ +Binding::Bindings Element::ownPropertyBindings(const QString &propertyName) const +{ + return BindingsPrivate::createBindings( + QQmlJSScope::scope(*this)->ownPropertyBindings(propertyName)); +} + +/*! + Returns this Element's property bindings that have the name \a propertyName. + */ +QList<Binding> Element::propertyBindings(const QString &propertyName) const +{ + const auto &bindings = QQmlJSScope::scope(*this)->propertyBindings(propertyName); + + QList<Binding> saBindings; + for (const auto &jsBinding : bindings) { + saBindings.push_back(BindingPrivate::createBinding(jsBinding)); + } + return saBindings; +} + +QQmlSA::Binding::Bindings +BindingsPrivate::createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &hash) +{ + QMultiHash<QString, QQmlSA::Binding> saBindings; + for (const auto &[key, value] : hash.asKeyValueRange()) { + saBindings.insert(key, BindingPrivate::createBinding(value)); + } + + QQmlSA::Binding::Bindings bindings; + bindings.d_func()->m_bindings = std::move(saBindings); + return bindings; +} + +QQmlSA::Binding::Bindings BindingsPrivate::createBindings( + QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator, + QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator> iterators) +{ + QMultiHash<QString, QQmlSA::Binding> saBindings; + for (auto it = iterators.first; it != iterators.second; ++it) { + saBindings.insert(it.key(), BindingPrivate::createBinding(it.value())); + } + + QQmlSA::Binding::Bindings bindings; + bindings.d_func()->m_bindings = std::move(saBindings); + return bindings; +} + +/*! + Returns an iterator to the beginning of this Element's children. + */ +QQmlJS::ConstPtrWrapperIterator Element::childScopesBegin() const +{ + return QQmlJSScope::scope(*this)->childScopesBegin(); +} + +/*! + Returns an iterator to the end of this Element's children. + */ +QQmlJS::ConstPtrWrapperIterator Element::childScopesEnd() const +{ + return QQmlJSScope::scope(*this)->childScopesEnd(); +} + +Element::operator bool() const +{ + return bool(QQmlJSScope::scope(*this)); +} + +bool Element::operator!() const +{ + return !QQmlJSScope::scope(*this); +} + +QString Element::prettyName(QAnyStringView name) +{ + return QQmlJSScope::prettyName(name); +} + +bool Element::operatorEqualsImpl(const Element &lhs, const Element &rhs) +{ + return QQmlJSScope::scope(lhs) == QQmlJSScope::scope(rhs); +} + +qsizetype Element::qHashImpl(const Element &key, qsizetype seed) noexcept +{ + return qHash(QQmlJSScope::scope(key), seed); +} + +/*! + \class QQmlSA::GenericPass + \inmodule QtQmlCompiler + + \brief Represents a generic static analysis pass. This class should be extended to + implement custom behavior. + */ + class GenericPassPrivate { + Q_DECLARE_PUBLIC(GenericPass); + public: - const PassManager *manager; + GenericPassPrivate(GenericPass *interface, PassManager *manager) + : m_manager{ manager }, q_ptr{ interface } + { + Q_ASSERT(manager); + } + +private: + PassManager *m_manager; + + GenericPass *q_ptr; }; +/*! + Creates a generic pass. + */ GenericPass::~GenericPass() = default; GenericPass::GenericPass(PassManager *manager) + : d_ptr{ new GenericPassPrivate{ this, manager } } { } + +/*! + Emits a warning message \a diagnostic about an issue of tyep \a id. + */ +void GenericPass::emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id) { - Q_ASSERT(manager); - d = std::make_unique<GenericPassPrivate>(); - d->manager = manager; + emitWarning(diagnostic, id, QQmlSA::SourceLocation{}); } -void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id, - QQmlJS::SourceLocation srcLocation) +/*! + Emits a warning message \a diagnostic about an issue of tyep \a id located at + \a srcLocation. + */ +void GenericPass::emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id, + QQmlSA::SourceLocation srcLocation) { - d->manager->m_visitor->logger()->log(diagnostic.toString(), id, srcLocation); + Q_D(const GenericPass); + PassManagerPrivate::visitor(*d->m_manager) + ->logger() + ->log(diagnostic.toString(), id, + QQmlSA::SourceLocationPrivate::sourceLocation(srcLocation)); } - -void GenericPass::emitWarning(QAnyStringView diagnostic, LoggerWarningId id, - QQmlJS::SourceLocation srcLocation, const FixSuggestion &fix) +/*! + Emits a warning message \a diagnostic about an issue of tyep \a id located at + \a srcLocation and with suggested fix \a fix. + */ +void GenericPass::emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id, + QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix) { - d->manager->m_visitor->logger()->log(diagnostic.toString(), id, srcLocation, true, true, fix); + Q_D(const GenericPass); + PassManagerPrivate::visitor(*d->m_manager) + ->logger() + ->log(diagnostic.toString(), id, + QQmlSA::SourceLocationPrivate::sourceLocation(srcLocation), true, true, + FixSuggestionPrivate::fixSuggestion(fix)); } +/*! + Returns the type of \a typeName. + */ Element GenericPass::resolveTypeInFileScope(QAnyStringView typeName) { - return d->manager->m_visitor->imports().type(typeName.toString()).scope; + Q_D(const GenericPass); + const auto scope = + PassManagerPrivate::visitor(*d->m_manager)->imports().type(typeName.toString()).scope; + return QQmlJSScope::createQQmlSAElement(scope); } +/*! + Returns the type of \a typeName defined in module \a moduleName. + */ Element GenericPass::resolveType(QAnyStringView moduleName, QAnyStringView typeName) { - auto typeImporter = d->manager->m_visitor->importer(); - auto module = typeImporter->importModule(moduleName.toString()); - return module.type(typeName.toString()).scope; + Q_D(const GenericPass); + QQmlJSImporter *typeImporter = PassManagerPrivate::visitor(*d->m_manager)->importer(); + const auto module = typeImporter->importModule(moduleName.toString()); + const auto scope = module.type(typeName.toString()).scope; + return QQmlJSScope::createQQmlSAElement(scope); } -Element GenericPass::resolveLiteralType(const QQmlJSMetaPropertyBinding &binding) +/*! + Returns the element representing the type of literal in \a binding. If the + binding does not contain a literal value, a null Element is returned. + */ +Element GenericPass::resolveLiteralType(const QQmlSA::Binding &binding) { - return binding.literalType(d->manager->m_typeResolver); + Q_D(const GenericPass); + return binding.literalType(PassManagerPrivate::resolver(*d->m_manager)); } +/*! + Returns the element in \a context that has id \a id. + */ Element GenericPass::resolveIdToElement(QAnyStringView id, const Element &context) { - return d->manager->m_visitor->addressableScopes().scope(id.toString(), context); + Q_D(const GenericPass); + const auto scope = PassManagerPrivate::visitor(*d->m_manager) + ->addressableScopes() + .scope(id.toString(), QQmlJSScope::scope(context)); + return QQmlJSScope::createQQmlSAElement(scope); } +/*! + Returns the id of \a element in a given \a context. + */ QString GenericPass::resolveElementToId(const Element &element, const Element &context) { - return d->manager->m_visitor->addressableScopes().id(element, context); + Q_D(const GenericPass); + return PassManagerPrivate::visitor(*d->m_manager) + ->addressableScopes() + .id(QQmlJSScope::scope(element), QQmlJSScope::scope(context)); } -QString GenericPass::sourceCode(QQmlJS::SourceLocation location) +/*! + Returns the source code located within \a location. + */ +QString GenericPass::sourceCode(QQmlSA::SourceLocation location) { - return d->manager->m_visitor->logger()->code().mid(location.offset, location.length); + Q_D(const GenericPass); + return PassManagerPrivate::visitor(*d->m_manager) + ->logger() + ->code() + .mid(location.offset(), location.length()); +} + +/*! + \class QQmlSA::PassManager + \inmodule QtQmlCompiler + + \brief Can analyze an element and its children with static analysis passes. + */ + +/*! + Constructs a pass manager given an import visitor and a type resolver. + */ +QQmlSA::PassManager::PassManager(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver) + : d_ptr{ new PassManagerPrivate{ this, visitor, resolver } } +{ +} + +PassManager::~PassManager() = default; // explicitly defaulted out-of-line for PIMPL + +/*! + Registers a static analysis \a pass to be run on all elements. + */ +void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass) +{ + Q_D(PassManager); + d->registerElementPass(std::move(pass)); } /*! @@ -81,7 +983,7 @@ QString GenericPass::sourceCode(QQmlJS::SourceLocation location) with the pass manager. \param pass The registered pass. Ownership is transferred to the pass manager. */ -void PassManager::registerElementPass(std::unique_ptr<ElementPass> pass) +void PassManagerPrivate::registerElementPass(std::unique_ptr<ElementPass> pass) { m_elementPasses.push_back(std::move(pass)); } @@ -90,76 +992,113 @@ enum LookupMode { Register, Lookup }; static QString lookupName(const QQmlSA::Element &element, LookupMode mode = Lookup) { QString name; - if (element.isNull() || element->internalName().isEmpty()) { + if (element.isNull() || QQmlJSScope::scope(element)->internalName().isEmpty()) { // Bail out with an invalid name, this type is so screwed up we can't do anything reasonable // with it We should have warned about it in another plac - if (element.isNull() || element->baseType().isNull()) + if (element.isNull() || element.baseType().isNull()) return u"$INVALID$"_s; - name = element->baseType()->internalName(); + name = QQmlJSScope::scope(element.baseType())->internalName(); } else { - name = element->internalName(); + name = QQmlJSScope::scope(element)->internalName(); } const QString filePath = - (mode == Register || !element->baseType() ? element : element->baseType())->filePath(); + (mode == Register || !element.baseType() ? element : element.baseType()).filePath(); - if (element->isComposite() && !filePath.endsWith(u".h")) + if (QQmlJSScope::scope(element)->isComposite() && !filePath.endsWith(u".h")) name += u'@' + filePath; return name; } +/*! + Registers a static analysis pass for properties. The \a pass will be run on + every property matching the \a moduleName, \a typeName and \a propertyName. + + Omitting the \a propertyName will register this pass for all properties + matching the \a typeName and \a moduleName. + + Setting \a allowInheritance to \c true means that the filtering on the type + also accepts types deriving from \a typeName. + + \note Running analysis passes on too many items can be expensive. This is + why it is generally good to filter down the set of properties of a pass + using the \a moduleName, \a typeName and \a propertyName. + + Returns \c true if the pass was successfully added, \c false otherwise. +*/ bool PassManager::registerPropertyPass(std::shared_ptr<PropertyPass> pass, QAnyStringView moduleName, QAnyStringView typeName, QAnyStringView propertyName, bool allowInheritance) { + Q_D(PassManager); + return d->registerPropertyPass(pass, moduleName, typeName, propertyName, allowInheritance); +} + +bool PassManagerPrivate::registerPropertyPass(std::shared_ptr<PropertyPass> pass, + QAnyStringView moduleName, QAnyStringView typeName, + QAnyStringView propertyName, bool allowInheritance) +{ + if (moduleName.isEmpty() || typeName.isEmpty()) + qWarning() << "Both the moduleName and the typeName must be specified " + "for the pass to be registered."; + QString name; if (!moduleName.isEmpty() && !typeName.isEmpty()) { auto typeImporter = m_visitor->importer(); auto module = typeImporter->importModule(moduleName.toString()); - auto element = module.type(typeName.toString()).scope; + auto element = QQmlJSScope::createQQmlSAElement(module.type(typeName.toString()).scope); if (element.isNull()) return false; name = lookupName(element, Register); } - const PassManager::PropertyPassInfo passInfo { - propertyName.isEmpty() ? QStringList {} : QStringList { propertyName.toString() }, - std::move(pass), allowInheritance - }; + const QQmlSA::PropertyPassInfo passInfo{ propertyName.isEmpty() + ? QStringList{} + : QStringList{ propertyName.toString() }, + std::move(pass), allowInheritance }; m_propertyPasses.insert({ name, passInfo }); return true; } -void PassManager::addBindingSourceLocations(const Element &element, const Element &scope, - const QString prefix, bool isAttached) +void PassManagerPrivate::addBindingSourceLocations(const Element &element, const Element &scope, + const QString prefix, bool isAttached) { const Element ¤tScope = scope.isNull() ? element : scope; - const auto ownBindings = currentScope->ownPropertyBindings(); - for (const auto &binding : ownBindings.values()) { + const auto ownBindings = currentScope.ownPropertyBindings(); + for (const auto &binding : ownBindings) { switch (binding.bindingType()) { - case QQmlJSMetaPropertyBinding::GroupProperty: - addBindingSourceLocations(element, binding.groupType(), + case QQmlSA::BindingType::GroupProperty: + addBindingSourceLocations(element, Element{ binding.groupType() }, prefix + binding.propertyName() + u'.'); break; - case QQmlJSMetaPropertyBinding::AttachedProperty: - addBindingSourceLocations(element, binding.attachingType(), + case QQmlSA::BindingType::AttachedProperty: + addBindingSourceLocations(element, Element{ binding.attachingType() }, prefix + binding.propertyName() + u'.', true); break; default: - m_bindingsByLocation.insert({ binding.sourceLocation().offset, - BindingInfo { prefix + binding.propertyName(), binding, - currentScope, isAttached } }); + m_bindingsByLocation.insert({ binding.sourceLocation().offset(), + BindingInfo{ prefix + binding.propertyName(), binding, + currentScope, isAttached } }); - if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script) + if (binding.bindingType() != QQmlSA::BindingType::Script) analyzeBinding(element, QQmlSA::Element(), binding.sourceLocation()); } } } +/*! + Runs the element passes over \a root and all its children. + */ void PassManager::analyze(const Element &root) { + Q_D(PassManager); + d->analyze(root); +} + +void PassManagerPrivate::analyze(const Element &root) +{ QList<Element> runStack; runStack.push_back(root); while (!runStack.isEmpty()) { @@ -169,31 +1108,32 @@ void PassManager::analyze(const Element &root) if (elementPass->shouldRun(element)) elementPass->run(element); - for (auto it = element->childScopesBegin(); it != element->childScopesEnd(); ++it) { - if ((*it)->scopeType() == QQmlJSScope::QMLScope) - runStack.push_back(*it); + for (auto it = element.childScopesBegin(); it != element.childScopesEnd(); ++it) { + if ((*it)->scopeType() == QQmlSA::ScopeType::QMLScope) + runStack.push_back(QQmlJSScope::createQQmlSAElement(*it)); } } } -void PassManager::analyzeWrite(const Element &element, QString propertyName, const Element &value, - const Element &writeScope, QQmlJS::SourceLocation location) +void PassManagerPrivate::analyzeWrite(const Element &element, QString propertyName, + const Element &value, const Element &writeScope, + QQmlSA::SourceLocation location) { for (PropertyPass *pass : findPropertyUsePasses(element, propertyName)) pass->onWrite(element, propertyName, value, writeScope, location); } -void PassManager::analyzeRead(const Element &element, QString propertyName, - const Element &readScope, QQmlJS::SourceLocation location) +void PassManagerPrivate::analyzeRead(const Element &element, QString propertyName, + const Element &readScope, QQmlSA::SourceLocation location) { for (PropertyPass *pass : findPropertyUsePasses(element, propertyName)) pass->onRead(element, propertyName, readScope, location); } -void PassManager::analyzeBinding(const Element &element, const QQmlSA::Element &value, - QQmlJS::SourceLocation location) +void PassManagerPrivate::analyzeBinding(const Element &element, const QQmlSA::Element &value, + QQmlSA::SourceLocation location) { - const auto info = m_bindingsByLocation.find(location.offset); + const auto info = m_bindingsByLocation.find(location.offset()); // If there's no matching binding that means we're in a nested Ret somewhere inside an // expression @@ -201,43 +1141,64 @@ void PassManager::analyzeBinding(const Element &element, const QQmlSA::Element & return; const QQmlSA::Element &bindingScope = info->second.bindingScope; - const QQmlJSMetaPropertyBinding &binding = info->second.binding; + const QQmlSA::Binding &binding = info->second.binding; const QString &propertyName = info->second.fullPropertyName; for (PropertyPass *pass : findPropertyUsePasses(element, propertyName)) pass->onBinding(element, propertyName, binding, bindingScope, value); - if (!info->second.isAttached || bindingScope->baseType().isNull()) + if (!info->second.isAttached || bindingScope.baseType().isNull()) return; - for (PropertyPass *pass : findPropertyUsePasses(bindingScope->baseType(), propertyName)) + for (PropertyPass *pass : findPropertyUsePasses(bindingScope.baseType(), propertyName)) pass->onBinding(element, propertyName, binding, bindingScope, value); } +/*! + Returns \c true if the module named \a module has been imported by the + import visitor, \c false otherwise. + */ bool PassManager::hasImportedModule(QAnyStringView module) const { - return m_visitor->imports().hasType(u"$module$." + module.toString()); + return PassManagerPrivate::visitor(*this)->imports().hasType(u"$module$." + module.toString()); +} + +/*! + Returns \c true if warnings of \a category are enabled, \c false otherwise. + */ +bool PassManager::isCategoryEnabled(QQmlJS::LoggerWarningId category) const +{ + return !PassManagerPrivate::visitor(*this)->logger()->isCategoryIgnored(category); } -bool PassManager::isCategoryEnabled(LoggerWarningId category) const +/*! + Sets whether the given \a category of warnings should be enabled. + */ +void PassManager::setCategoryEnabled(QQmlJS::LoggerWarningId category, bool enabled) +{ + PassManagerPrivate::visitor(*this)->logger()->setCategoryIgnored(category, !enabled); +} + +QQmlJSImportVisitor *QQmlSA::PassManagerPrivate::visitor(const QQmlSA::PassManager &manager) { - return !m_visitor->logger()->isCategoryIgnored(category); + return manager.d_func()->m_visitor; } -void PassManager::setCategoryEnabled(LoggerWarningId category, bool enabled) +QQmlJSTypeResolver *QQmlSA::PassManagerPrivate::resolver(const QQmlSA::PassManager &manager) { - m_visitor->logger()->setCategoryIgnored(category, !enabled); + return manager.d_func()->m_typeResolver; } -QSet<PropertyPass *> PassManager::findPropertyUsePasses(const QQmlSA::Element &element, - const QString &propertyName) +QSet<PropertyPass *> PassManagerPrivate::findPropertyUsePasses(const QQmlSA::Element &element, + const QString &propertyName) { QStringList typeNames { lookupName(element) }; QQmlJSUtils::searchBaseAndExtensionTypes( - element, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) { + QQmlJSScope::scope(element), + [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) { Q_UNUSED(mode); - typeNames.append(lookupName(scope)); + typeNames.append(lookupName(QQmlJSScope::createQQmlSAElement(scope))); return false; }); @@ -263,26 +1224,71 @@ QSet<PropertyPass *> PassManager::findPropertyUsePasses(const QQmlSA::Element &e } void DebugElementPass::run(const Element &element) { - emitWarning(u"Type: " + element->baseTypeName(), qmlPlugin); - if (auto bindings = element->propertyBindings(u"objectName"_s); !bindings.isEmpty()) { + emitWarning(u"Type: " + element.baseTypeName(), qmlPlugin); + if (auto bindings = element.propertyBindings(u"objectName"_s); !bindings.isEmpty()) { emitWarning(u"is named: " + bindings.first().stringValue(), qmlPlugin); } - if (auto defPropName = element->defaultPropertyName(); !defPropName.isEmpty()) { - emitWarning(u"binding " + QString::number(element->propertyBindings(defPropName).size()) + if (auto defPropName = element.defaultPropertyName(); !defPropName.isEmpty()) { + emitWarning(u"binding " + QString::number(element.propertyBindings(defPropName).size()) + u" elements to property "_s + defPropName, qmlPlugin); } } -bool ElementPass::shouldRun(const Element &) +/*! + \class QQmlSA::LintPlugin + \inmocule QtQmlCompiler + + \brief Base class for all static analysis plugins. + */ + +/*! + \fn void QQmlSA::LintPlugin::registerPasses(PassManager *manager, const Element &rootElement) + + Adds a pass manager that will be executed on \a rootElement. + */ + +/*! + \class QQmlSA::ElementPass + \inmodule QtQmlCompiler + + \brief Base class for all static analysis passes on elements. + */ + +/*! + \fn void QQmlSA::ElementPass::run(const Element &element) + + Exeecutes if \c shouldRun() returns \c true. Performs the real computation + of the pass on \a element. + */ + +/*! + Determines if the \c run() function should be executed on the given \a element + */ +bool ElementPass::shouldRun(const Element &element) { + (void)element; return true; } +/*! + \class QQmlSA::PropertyPass + \inmodule QtQmlCompiler + + \brief Base class for all static analysis passes on properties. + */ + + PropertyPass::PropertyPass(PassManager *manager) : GenericPass(manager) { } +/*! + Executes whenever a property gets bound to a value. + + The property \a propertyName of \a element is bound to the \a value within + \a bindingScope with \a binding. + */ void PropertyPass::onBinding(const Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const Element &bindingScope, + const QQmlSA::Binding &binding, const Element &bindingScope, const Element &value) { Q_UNUSED(element); @@ -292,8 +1298,14 @@ void PropertyPass::onBinding(const Element &element, const QString &propertyName Q_UNUSED(value); } +/*! + Executes whenever a property is read. + + The property \a propertyName of \a element is read by an instruction within + \a readScope defined at \a location. + */ void PropertyPass::onRead(const Element &element, const QString &propertyName, - const Element &readScope, QQmlJS::SourceLocation location) + const Element &readScope, QQmlSA::SourceLocation location) { Q_UNUSED(element); Q_UNUSED(propertyName); @@ -301,9 +1313,16 @@ void PropertyPass::onRead(const Element &element, const QString &propertyName, Q_UNUSED(location); } +/*! + Executes whenever a property is written to. + + The property \a propertyName of \a element is written to by an instruction + within \a writeScope defined at \a location. The property is written the + value \a value. + */ void PropertyPass::onWrite(const Element &element, const QString &propertyName, const Element &value, const Element &writeScope, - QQmlJS::SourceLocation location) + QQmlSA::SourceLocation location) { Q_UNUSED(element); Q_UNUSED(propertyName); @@ -317,48 +1336,280 @@ DebugPropertyPass::DebugPropertyPass(QQmlSA::PassManager *manager) : QQmlSA::Pro } void DebugPropertyPass::onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) { emitWarning(u"onRead "_s - + (element->internalName().isEmpty() ? element->baseTypeName() - : element->internalName()) - + u' ' + propertyName + u' ' + readScope->internalName() + u' ' - + QString::number(location.startLine) + u':' - + QString::number(location.startColumn), + + (QQmlJSScope::scope(element)->internalName().isEmpty() + ? element.baseTypeName() + : QQmlJSScope::scope(element)->internalName()) + + u' ' + propertyName + u' ' + QQmlJSScope::scope(readScope)->internalName() + + u' ' + QString::number(location.startLine()) + u':' + + QString::number(location.startColumn()), qmlPlugin, location); } void DebugPropertyPass::onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) { - const auto location = binding.sourceLocation(); + const auto location = QQmlSA::SourceLocation{ binding.sourceLocation() }; emitWarning(u"onBinding element: '"_s - + (element->internalName().isEmpty() ? element->baseTypeName() - : element->internalName()) + + (QQmlJSScope::scope(element)->internalName().isEmpty() + ? element.baseTypeName() + : QQmlJSScope::scope(element)->internalName()) + u"' property: '"_s + propertyName + u"' value: '"_s - + (value.isNull() - ? u"NULL"_s - : (value->internalName().isNull() ? value->baseTypeName() - : value->internalName())) + + (value.isNull() ? u"NULL"_s + : (QQmlJSScope::scope(value)->internalName().isNull() + ? value.baseTypeName() + : QQmlJSScope::scope(value)->internalName())) + u"' binding_scope: '"_s - + (bindingScope->internalName().isEmpty() ? bindingScope->baseTypeName() - : bindingScope->internalName()) - + u"' "_s + QString::number(location.startLine) + u':' - + QString::number(location.startColumn), + + (QQmlJSScope::scope(bindingScope)->internalName().isEmpty() + ? bindingScope.baseTypeName() + : QQmlJSScope::scope(bindingScope)->internalName()) + + u"' "_s + QString::number(location.startLine()) + u':' + + QString::number(location.startColumn()), qmlPlugin, location); } void DebugPropertyPass::onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) + QQmlSA::SourceLocation location) { - emitWarning(u"onWrite "_s + element->baseTypeName() + u' ' + propertyName + u' ' - + value->internalName() + u' ' + writeScope->internalName() + u' ' - + QString::number(location.startLine) + u':' - + QString::number(location.startColumn), + emitWarning(u"onWrite "_s + element.baseTypeName() + u' ' + propertyName + u' ' + + QQmlJSScope::scope(value)->internalName() + u' ' + + QQmlJSScope::scope(writeScope)->internalName() + u' ' + + QString::number(location.startLine()) + u':' + + QString::number(location.startColumn()), qmlPlugin, location); } + +/*! + Returns the list of element passes. + */ +std::vector<std::shared_ptr<ElementPass>> PassManager::elementPasses() const +{ + Q_D(const PassManager); + return d->m_elementPasses; +} + +/*! + Returns the list of property passes. + */ +std::multimap<QString, PropertyPassInfo> PassManager::propertyPasses() const +{ + Q_D(const PassManager); + return d->m_propertyPasses; +} + +/*! + Returns bindings by their source location. + */ +std::unordered_map<quint32, BindingInfo> PassManager::bindingsByLocation() const +{ + Q_D(const PassManager); + return d->m_bindingsByLocation; +} + +FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface) : q_ptr{ interface } { } + +FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, const QString &fixDescription, + const QQmlSA::SourceLocation &location, + const QString &replacement) + : m_fixSuggestion{ fixDescription, QQmlSA::SourceLocationPrivate::sourceLocation(location), + replacement }, + q_ptr{ interface } +{ +} + +FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, + const FixSuggestionPrivate &other) + : m_fixSuggestion{ other.m_fixSuggestion }, q_ptr{ interface } +{ +} + +FixSuggestionPrivate::FixSuggestionPrivate(FixSuggestion *interface, FixSuggestionPrivate &&other) + : m_fixSuggestion{ std::move(other.m_fixSuggestion) }, q_ptr{ interface } +{ +} + +QString FixSuggestionPrivate::fixDescription() const +{ + return m_fixSuggestion.fixDescription(); +} + +QQmlSA::SourceLocation FixSuggestionPrivate::location() const +{ + return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation(m_fixSuggestion.location()); +} + +QString FixSuggestionPrivate::replacement() const +{ + return m_fixSuggestion.replacement(); +} + +void FixSuggestionPrivate::setFileName(const QString &fileName) +{ + m_fixSuggestion.setFilename(fileName); +} + +QString FixSuggestionPrivate::fileName() const +{ + return m_fixSuggestion.filename(); +} + +void FixSuggestionPrivate::setHint(const QString &hint) +{ + m_fixSuggestion.setHint(hint); +} + +QString FixSuggestionPrivate::hint() const +{ + return m_fixSuggestion.hint(); +} + +void FixSuggestionPrivate::setAutoApplicable(bool autoApplicable) +{ + m_fixSuggestion.setAutoApplicable(autoApplicable); +} + +bool FixSuggestionPrivate::isAutoApplicable() const +{ + return m_fixSuggestion.isAutoApplicable(); +} + +QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(FixSuggestion &saFixSuggestion) +{ + return saFixSuggestion.d_func()->m_fixSuggestion; +} + +const QQmlJSFixSuggestion &FixSuggestionPrivate::fixSuggestion(const FixSuggestion &saFixSuggestion) +{ + return saFixSuggestion.d_func()->m_fixSuggestion; +} + +/*! + \class QQmlSA::FixSuggestion + \inmodule QtQmlCompiler + + \brief Represents a suggested fix for an issue in the source code. + */ + +FixSuggestion::FixSuggestion() : d_ptr{ new FixSuggestionPrivate{ this } } { } + +FixSuggestion::FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location, + const QString &replacement) + : d_ptr{ new FixSuggestionPrivate{ this, fixDescription, location, replacement } } +{ +} + +FixSuggestion::FixSuggestion(const FixSuggestion &other) + : d_ptr{ new FixSuggestionPrivate{ this, *other.d_func() } } +{ +} + +FixSuggestion::FixSuggestion(FixSuggestion &&other) + : d_ptr{ new FixSuggestionPrivate{ this, std::move(*other.d_func()) } } +{ } +FixSuggestion &FixSuggestion::operator=(const FixSuggestion &other) +{ + if (*this == other) + return *this; + + d_func()->m_fixSuggestion = other.d_func()->m_fixSuggestion; + return *this; +} + +FixSuggestion &FixSuggestion::operator=(FixSuggestion &&other) +{ + if (*this == other) + return *this; + + d_func()->m_fixSuggestion = std::move(other.d_func()->m_fixSuggestion); + return *this; +} + +FixSuggestion::~FixSuggestion() = default; + +/*! + Returns the description of the fix. + */ +QString QQmlSA::FixSuggestion::fixDescription() const +{ + return FixSuggestionPrivate::fixSuggestion(*this).fixDescription(); +} + +/*! + Returns the location where the fix would be applied. + */ +QQmlSA::SourceLocation FixSuggestion::location() const +{ + return QQmlSA::SourceLocationPrivate::createQQmlSASourceLocation( + FixSuggestionPrivate::fixSuggestion(*this).location()); +} + +/*! + Returns the fix that will replace the problematic source code. + */ +QString FixSuggestion::replacement() const +{ + return FixSuggestionPrivate::fixSuggestion(*this).replacement(); +} + +/*! + Sets \a fileName as the name of the file where this fix suggestion applies. + */ +void FixSuggestion::setFileName(const QString &fileName) +{ + FixSuggestionPrivate::fixSuggestion(*this).setFilename(fileName); +} + +/*! + Returns the name of the file where this fix suggestion applies. + */ +QString FixSuggestion::fileName() const +{ + return FixSuggestionPrivate::fixSuggestion(*this).filename(); +} + +/*! + Sets \a hint as the hint for this fix suggestion. + */ +void FixSuggestion::setHint(const QString &hint) +{ + FixSuggestionPrivate::fixSuggestion(*this).setHint(hint); +} + +/*! + Returns the hint for this fix suggestion. + */ +QString FixSuggestion::hint() const +{ + return FixSuggestionPrivate::fixSuggestion(*this).hint(); +} + +/*! + Sets uses \a autoApplicable to set whtether this suggested fix can be applied automatically. + */ +void FixSuggestion::setAutoApplicable(bool autoApplicable) +{ + return FixSuggestionPrivate::fixSuggestion(*this).setAutoApplicable(autoApplicable); +} + +/*! + Returns whether this suggested fix can be applied automatically. + */ +bool QQmlSA::FixSuggestion::isAutoApplicable() const +{ + return FixSuggestionPrivate::fixSuggestion(*this).isAutoApplicable(); +} + +bool FixSuggestion::operatorEqualsImpl(const FixSuggestion &lhs, const FixSuggestion &rhs) +{ + return lhs.d_func()->m_fixSuggestion == rhs.d_func()->m_fixSuggestion; +} + +} // namespace QQmlSA + QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmlsa.h b/src/qmlcompiler/qqmlsa.h new file mode 100644 index 0000000000..c6163f9697 --- /dev/null +++ b/src/qmlcompiler/qqmlsa.h @@ -0,0 +1,431 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef QQMLSA_H +#define QQMLSA_H + +#include "qqmlsaconstants.h" +#include "qqmljsloggingutils.h" + +#include <qtqmlcompilerexports.h> + +#include <QtCore/QMultiHash> +#include <QtQmlCompiler/qqmlsasourcelocation.h> + +#include <unordered_map> + +QT_BEGIN_NAMESPACE + +class QQmlJSImportVisitor; // needed for PassManager +class QQmlJSTypeResolver; // needed for PassManager +struct QQmlJSTypePropagator; // needed for PassManager + +namespace QQmlJS { +class ConstPtrWrapperIterator; // needed for Element's child scope iterators +} // namespace QQmlJS + +namespace QQmlSA { + +class BindingPrivate; +class BindingsPrivate; +class Element; +class ElementPass; +class FixSuggestion; +class FixSuggestionPrivate; +class GenericPassPrivate; +class MethodPrivate; +class MethodsPrivate; +class PassManager; +class PassManagerPrivate; +class PropertyPass; +class PropertyPrivate; +enum class AccessSemantics; +struct BindingInfo; +struct PropertyPassInfo; + +enum class MethodType { Signal, Slot, Method, StaticMethod }; + +class Q_QMLCOMPILER_EXPORT Binding +{ + Q_DECLARE_PRIVATE(Binding) + +public: + class Q_QMLCOMPILER_EXPORT Bindings + { + Q_DECLARE_PRIVATE(Bindings) + + public: + Bindings(); + Bindings(const Bindings &); + ~Bindings(); + + QMultiHash<QString, Binding>::const_iterator begin() const { return constBegin(); } + QMultiHash<QString, Binding>::const_iterator end() const { return constEnd(); } + QMultiHash<QString, Binding>::const_iterator constBegin() const; + QMultiHash<QString, Binding>::const_iterator constEnd() const; + + private: + std::unique_ptr<BindingsPrivate> d_ptr; + }; + + Binding(); + Binding(const Binding &); + Binding(Binding &&); + Binding &operator=(const Binding &); + Binding &operator=(Binding &&); + ~Binding(); + + Element groupType() const; + BindingType bindingType() const; + QString stringValue() const; + QString propertyName() const; + Element attachingType() const; + QQmlSA::SourceLocation sourceLocation() const; + double numberValue() const; + ScriptBindingKind scriptKind() const; + bool hasObject() const; + Element objectType() const; + Element literalType(const QQmlJSTypeResolver *) const; + bool hasUndefinedScriptValue() const; + + friend bool operator==(const Binding &lhs, const Binding &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const Binding &lhs, const Binding &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + + static bool isLiteralBinding(BindingType); + +private: + static bool operatorEqualsImpl(const Binding &, const Binding &); + + std::unique_ptr<BindingPrivate> d_ptr; +}; + +class Q_QMLCOMPILER_EXPORT Method +{ + Q_DECLARE_PRIVATE(Method) + +public: + class Q_QMLCOMPILER_EXPORT Methods + { + Q_DECLARE_PRIVATE(Methods) + + public: + Methods(); + Methods(const Methods &); + ~Methods(); + + QMultiHash<QString, Method>::const_iterator begin() const { return constBegin(); } + QMultiHash<QString, Method>::const_iterator end() const { return constEnd(); } + QMultiHash<QString, Method>::const_iterator constBegin() const; + QMultiHash<QString, Method>::const_iterator constEnd() const; + + private: + std::unique_ptr<MethodsPrivate> d_ptr; + }; + + Method(); + Method(const Method &); + Method(Method &&); + Method &operator=(const Method &); + Method &operator=(Method &&); + ~Method(); + + QString methodName() const; + MethodType methodType() const; + + friend bool operator==(const Method &lhs, const Method &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const Method &lhs, const Method &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +private: + static bool operatorEqualsImpl(const Method &, const Method &); + + std::unique_ptr<MethodPrivate> d_ptr; +}; + +class Q_QMLCOMPILER_EXPORT Property +{ + Q_DECLARE_PRIVATE(Property) + +public: + Property(); + Property(const Property &); + Property(Property &&); + Property &operator=(const Property &); + Property &operator=(Property &&); + ~Property(); + + QString typeName() const; + bool isValid() const; + + friend bool operator==(const Property &lhs, const Property &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + + friend bool operator!=(const Property &lhs, const Property &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +private: + static bool operatorEqualsImpl(const Property &, const Property &); + + std::unique_ptr<PropertyPrivate> d_ptr; +}; + +class Q_QMLCOMPILER_EXPORT Element +{ + friend class QT_PREPEND_NAMESPACE(QQmlJSScope); + +public: + Element(); + explicit Element(const QString &); + Element(const Element &); + Element(Element &&other) + { + memcpy(m_data, other.m_data, sizeofElement); + memset(other.m_data, 0, sizeofElement); + } + Element &operator=(const Element &); + Element &operator=(Element &&other) + { + memcpy(m_data, other.m_data, sizeofElement); + memset(other.m_data, 0, sizeofElement); + return *this; + } + ~Element(); + + ScopeType scopeType() const; + Element baseType() const; + QString baseTypeName() const; + Element parentScope() const; + bool inherits(const Element &) const; + + bool isNull() const; + QString internalName() const; + AccessSemantics accessSemantics() const; + bool isComposite() const; + + Element attachedType() const; + QString attachedTypeName() const; + + bool hasProperty(const QString &propertyName) const; + Property property(const QString &propertyName) const; + bool isPropertyRequired(const QString &propertyName) const; + QString defaultPropertyName() const; + + bool hasMethod(const QString &methodName) const; + Method::Methods ownMethods() const; + + QQmlSA::SourceLocation sourceLocation() const; + QString filePath() const; + + bool hasPropertyBindings(const QString &name) const; + bool hasOwnPropertyBindings(const QString &propertyName) const; + + Binding::Bindings ownPropertyBindings() const; + Binding::Bindings ownPropertyBindings(const QString &propertyName) const; + QList<Binding> propertyBindings(const QString &propertyName) const; + + QQmlJS::ConstPtrWrapperIterator childScopesBegin() const; + QQmlJS::ConstPtrWrapperIterator childScopesEnd() const; + + explicit operator bool() const; + bool operator!() const; + + static QString prettyName(QAnyStringView); + + friend inline bool operator==(const QQmlSA::Element &lhs, const QQmlSA::Element &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend inline bool operator!=(const Element &lhs, const Element &rhs) { return !(lhs == rhs); } + + friend inline qsizetype qHash(const Element &key, qsizetype seed = 0) noexcept + { + return qHashImpl(key, seed); + } + +private: + static bool operatorEqualsImpl(const Element &, const Element &); + static qsizetype qHashImpl(const Element &key, qsizetype seed) noexcept; + + static constexpr qsizetype sizeofElement = 2 * sizeof(QSharedPointer<int>); + char m_data[sizeofElement]; +}; + +class Q_QMLCOMPILER_EXPORT GenericPass +{ + Q_DECLARE_PRIVATE(GenericPass) + Q_DISABLE_COPY_MOVE(GenericPass) + +public: + GenericPass(PassManager *manager); + virtual ~GenericPass(); + + void emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id); + void emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id, + QQmlSA::SourceLocation srcLocation); + void emitWarning(QAnyStringView diagnostic, QQmlJS::LoggerWarningId id, + QQmlSA::SourceLocation srcLocation, const QQmlSA::FixSuggestion &fix); + + Element resolveTypeInFileScope(QAnyStringView typeName); + Element resolveType(QAnyStringView moduleName, QAnyStringView typeName); // #### TODO: revisions + Element resolveLiteralType(const Binding &binding); + + Element resolveIdToElement(QAnyStringView id, const Element &context); + QString resolveElementToId(const Element &element, const Element &context); + + QString sourceCode(QQmlSA::SourceLocation location); + +private: + std::unique_ptr<GenericPassPrivate> d_ptr; +}; + +class Q_QMLCOMPILER_EXPORT PassManager +{ + friend struct ::QQmlJSTypePropagator; + Q_DECLARE_PRIVATE(PassManager) + +public: + PassManager(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver); + ~PassManager(); + + void registerElementPass(std::unique_ptr<ElementPass> pass); + bool registerPropertyPass(std::shared_ptr<PropertyPass> pass, QAnyStringView moduleName, + QAnyStringView typeName, + QAnyStringView propertyName = QAnyStringView(), + bool allowInheritance = true); + void analyze(const Element &root); + + bool hasImportedModule(QAnyStringView name) const; + + bool isCategoryEnabled(QQmlJS::LoggerWarningId category) const; + void setCategoryEnabled(QQmlJS::LoggerWarningId category, bool enabled = true); + + std::vector<std::shared_ptr<ElementPass>> elementPasses() const; + std::multimap<QString, PropertyPassInfo> propertyPasses() const; + std::unordered_map<quint32, BindingInfo> bindingsByLocation() const; + +private: + std::unique_ptr<PassManagerPrivate> d_ptr; +}; + +class Q_QMLCOMPILER_EXPORT LintPlugin +{ +public: + LintPlugin() = default; + virtual ~LintPlugin() = default; + + Q_DISABLE_COPY_MOVE(LintPlugin) + + virtual void registerPasses(PassManager *manager, const Element &rootElement) = 0; +}; + +class Q_QMLCOMPILER_EXPORT PropertyPass : public GenericPass +{ +public: + PropertyPass(PassManager *manager); + + virtual void onBinding(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Element &value); + virtual void onRead(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location); + virtual void onWrite(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Element &value, const QQmlSA::Element &writeScope, + QQmlSA::SourceLocation location); +}; + +class Q_QMLCOMPILER_EXPORT ElementPass : public GenericPass +{ +public: + ElementPass(PassManager *manager) : GenericPass(manager) { } + + virtual bool shouldRun(const Element &element); + virtual void run(const Element &element) = 0; +}; + +class Q_QMLCOMPILER_EXPORT DebugElementPass : public ElementPass +{ + void run(const Element &element) override; +}; + +class Q_QMLCOMPILER_EXPORT DebugPropertyPass : public QQmlSA::PropertyPass +{ +public: + DebugPropertyPass(QQmlSA::PassManager *manager); + + void onRead(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override; + void onBinding(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Element &value) override; + void onWrite(const QQmlSA::Element &element, const QString &propertyName, + const QQmlSA::Element &value, const QQmlSA::Element &writeScope, + QQmlSA::SourceLocation location) override; +}; + +class Q_QMLCOMPILER_EXPORT FixSuggestion +{ + Q_DECLARE_PRIVATE(FixSuggestion) + +public: + FixSuggestion(); + FixSuggestion(const QString &fixDescription, const QQmlSA::SourceLocation &location, + const QString &replacement = QString()); + FixSuggestion(const FixSuggestion &); + FixSuggestion(FixSuggestion &&); + FixSuggestion &operator=(const FixSuggestion &); + FixSuggestion &operator=(FixSuggestion &&); + ~FixSuggestion(); + + QString fixDescription() const; + QQmlSA::SourceLocation location() const; + QString replacement() const; + + void setFileName(const QString &); + QString fileName() const; + + void setHint(const QString &); + QString hint() const; + + void setAutoApplicable(bool autoApplicable = true); + bool isAutoApplicable() const; + + friend bool operator==(const FixSuggestion &lhs, const FixSuggestion &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + + friend bool operator!=(const FixSuggestion &lhs, const FixSuggestion &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +private: + static bool operatorEqualsImpl(const FixSuggestion &, const FixSuggestion &); + + std::unique_ptr<FixSuggestionPrivate> d_ptr; +}; + +} // namespace QQmlSA + +#define QmlLintPluginInterface_iid "org.qt-project.Qt.Qml.SA.LintPlugin/1.0" + +Q_DECLARE_INTERFACE(QQmlSA::LintPlugin, QmlLintPluginInterface_iid) + +QT_END_NAMESPACE + +#endif // QQMLSA_H diff --git a/src/qmlcompiler/qqmlsa_p.h b/src/qmlcompiler/qqmlsa_p.h index 6bbbe9441b..c15295b6b5 100644 --- a/src/qmlcompiler/qqmlsa_p.h +++ b/src/qmlcompiler/qqmlsa_p.h @@ -16,9 +16,9 @@ #include <qtqmlcompilerexports.h> -#include <private/qqmljsscope_p.h> #include <private/qqmljslogger_p.h> #include <QtCore/qset.h> +#include "qqmljsmetatypes_p.h" #include <map> #include <unordered_map> @@ -33,82 +33,160 @@ class QQmlJSImportVisitor; namespace QQmlSA { -// ### FIXME: Replace with a proper PIMPL'd type -using Element = QQmlJSScope::ConstPtr; -using FixSuggestion = QQmlJSFixSuggestion; - +class Bindings; class GenericPassPrivate; class PassManager; -class Q_QMLCOMPILER_EXPORT GenericPass +enum class AccessSemantics { Reference, Value, None, Sequence }; + +enum class Flag { + Creatable = 0x1, + Composite = 0x2, + Singleton = 0x4, + Script = 0x8, + CustomParser = 0x10, + Array = 0x20, + InlineComponent = 0x40, + WrappedInImplicitComponent = 0x80, + HasBaseTypeError = 0x100, + HasExtensionNamespace = 0x200, + IsListProperty = 0x400, +}; + +struct BindingInfo +{ + QString fullPropertyName; + QQmlSA::Binding binding; + QQmlSA::Element bindingScope; + bool isAttached; +}; + +struct PropertyPassInfo { + QStringList properties; + std::shared_ptr<QQmlSA::PropertyPass> pass; + bool allowInheritance = true; +}; + +class BindingsPrivate +{ + friend class QT_PREPEND_NAMESPACE(QQmlJSMetaPropertyBinding); + Q_DECLARE_PUBLIC(QQmlSA::Binding::Bindings) + public: - Q_DISABLE_COPY_MOVE(GenericPass) - GenericPass(PassManager *manager); - virtual ~GenericPass(); + explicit BindingsPrivate(QQmlSA::Binding::Bindings *); + BindingsPrivate(QQmlSA::Binding::Bindings *, const BindingsPrivate &); + BindingsPrivate(QQmlSA::Binding::Bindings *, BindingsPrivate &&); + ~BindingsPrivate() = default; - void emitWarning(QAnyStringView diagnostic, LoggerWarningId id, - QQmlJS::SourceLocation srcLocation = QQmlJS::SourceLocation()); - void emitWarning(QAnyStringView diagnostic, LoggerWarningId id, - QQmlJS::SourceLocation srcLocation, const FixSuggestion &fix); + QMultiHash<QString, Binding>::const_iterator constBegin() const; + QMultiHash<QString, Binding>::const_iterator constEnd() const; - Element resolveTypeInFileScope(QAnyStringView typeName); - Element resolveType(QAnyStringView moduleName, QAnyStringView typeName); // #### TODO: revisions - Element resolveLiteralType(const QQmlJSMetaPropertyBinding &binding); + static QQmlSA::Binding::Bindings + createBindings(const QMultiHash<QString, QQmlJSMetaPropertyBinding> &); + static QQmlSA::Binding::Bindings + createBindings(QPair<QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator, + QMultiHash<QString, QQmlJSMetaPropertyBinding>::const_iterator>); - Element resolveIdToElement(QAnyStringView id, const Element &context); - QString resolveElementToId(const Element &element, const Element &context); +private: + QMultiHash<QString, Binding> m_bindings; + QQmlSA::Binding::Bindings *q_ptr; +}; - QString sourceCode(QQmlJS::SourceLocation location); +class BindingPrivate +{ + friend class QT_PREPEND_NAMESPACE(QQmlJSMetaPropertyBinding); + Q_DECLARE_PUBLIC(Binding) + +public: + explicit BindingPrivate(Binding *); + BindingPrivate(Binding *, const BindingPrivate &); + + static QQmlSA::Binding createBinding(const QQmlJSMetaPropertyBinding &); + static QQmlJSMetaPropertyBinding binding(QQmlSA::Binding &binding); + static const QQmlJSMetaPropertyBinding binding(const QQmlSA::Binding &binding); private: - std::unique_ptr<GenericPassPrivate> d; // PIMPL might be overkill + QQmlJSMetaPropertyBinding m_binding; + Binding *q_ptr; }; -class Q_QMLCOMPILER_EXPORT ElementPass : public GenericPass +class MethodPrivate { + friend class QT_PREPEND_NAMESPACE(QQmlJSMetaMethod); + Q_DECLARE_PUBLIC(Method) + public: - ElementPass(PassManager *manager) : GenericPass(manager) { } + explicit MethodPrivate(Method *); + MethodPrivate(Method *, const MethodPrivate &); + + QString methodName() const; + MethodType methodType() const; - virtual bool shouldRun(const Element &element); - virtual void run(const Element &element) = 0; + static QQmlSA::Method createMethod(const QQmlJSMetaMethod &); + static QQmlJSMetaMethod method(const QQmlSA::Method &); + +private: + QQmlJSMetaMethod m_method; + Method *q_ptr; }; -class Q_QMLCOMPILER_EXPORT PropertyPass : public GenericPass +class MethodsPrivate { + friend class QT_PREPEND_NAMESPACE(QQmlJSMetaMethod); + Q_DECLARE_PUBLIC(QQmlSA::Method::Methods) + public: - PropertyPass(PassManager *manager); - - virtual void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, - const QQmlSA::Element &bindingScope, const QQmlSA::Element &value); - virtual void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location); - virtual void onWrite(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location); + explicit MethodsPrivate(QQmlSA::Method::Methods *); + MethodsPrivate(QQmlSA::Method::Methods *, const MethodsPrivate &); + MethodsPrivate(QQmlSA::Method::Methods *, MethodsPrivate &&); + ~MethodsPrivate() = default; + + QMultiHash<QString, Method>::const_iterator constBegin() const; + QMultiHash<QString, Method>::const_iterator constEnd() const; + + static QQmlSA::Method::Methods createMethods(const QMultiHash<QString, QQmlJSMetaMethod> &); + +private: + QMultiHash<QString, Method> m_methods; + QQmlSA::Method::Methods *q_ptr; }; -class Q_QMLCOMPILER_EXPORT LintPlugin +class PropertyPrivate { + friend class QT_PREPEND_NAMESPACE(QQmlJSMetaProperty); + Q_DECLARE_PUBLIC(QQmlSA::Property) + public: - LintPlugin() = default; - virtual ~LintPlugin() = default; + explicit PropertyPrivate(Property *); + PropertyPrivate(Property *, const PropertyPrivate &); + PropertyPrivate(Property *, PropertyPrivate &&); + ~PropertyPrivate() = default; - Q_DISABLE_COPY_MOVE(LintPlugin) + QString typeName() const; + bool isValid() const; - virtual void registerPasses(PassManager *manager, const Element &rootElement) = 0; + static QQmlJSMetaProperty property(const QQmlSA::Property &property); + static QQmlSA::Property createProperty(const QQmlJSMetaProperty &); + +private: + QQmlJSMetaProperty m_property; + QQmlSA::Property *q_ptr; }; -// ### FIXME: Make this (at least partially) private again as soon as possible -class Q_QMLCOMPILER_EXPORT PassManager +class Q_QMLCOMPILER_EXPORT PassManagerPrivate { + friend class QT_PREPEND_NAMESPACE(QQmlJSScope); + + Q_DECLARE_PUBLIC(PassManager) + public: - Q_DISABLE_COPY_MOVE(PassManager) + Q_DISABLE_COPY_MOVE(PassManagerPrivate) friend class GenericPass; - PassManager(QQmlJSImportVisitor *visitor, QQmlJSTypeResolver *resolver) - : m_visitor(visitor), m_typeResolver(resolver) + PassManagerPrivate(PassManager *manager, QQmlJSImportVisitor *visitor, + QQmlJSTypeResolver *resolver) + : m_visitor(visitor), m_typeResolver(resolver), q_ptr{ manager } { Q_UNUSED(m_typeResolver); } @@ -121,8 +199,11 @@ public: bool hasImportedModule(QAnyStringView name) const; - bool isCategoryEnabled(LoggerWarningId category) const; - void setCategoryEnabled(LoggerWarningId category, bool enabled = true); + bool isCategoryEnabled(QQmlJS::LoggerWarningId category) const; + void setCategoryEnabled(QQmlJS::LoggerWarningId category, bool enabled = true); + + static QQmlJSImportVisitor *visitor(const QQmlSA::PassManager &); + static QQmlJSTypeResolver *resolver(const QQmlSA::PassManager &); private: friend struct ::QQmlJSTypePropagator; @@ -132,62 +213,60 @@ private: void analyzeWrite(const QQmlSA::Element &element, QString propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location); + QQmlSA::SourceLocation location); void analyzeRead(const QQmlSA::Element &element, QString propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location); + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location); void analyzeBinding(const QQmlSA::Element &element, const QQmlSA::Element &value, - QQmlJS::SourceLocation location); - - struct BindingInfo - { - QString fullPropertyName; - QQmlJSMetaPropertyBinding binding; - QQmlSA::Element bindingScope; - bool isAttached; - }; - - struct PropertyPassInfo - { - QStringList properties; - std::shared_ptr<PropertyPass> pass; - bool allowInheritance = true; - }; + QQmlSA::SourceLocation location); void addBindingSourceLocations(const QQmlSA::Element &element, const QQmlSA::Element &scope = QQmlSA::Element(), const QString prefix = QString(), bool isAttached = false); - std::vector<std::unique_ptr<ElementPass>> m_elementPasses; + std::vector<std::shared_ptr<ElementPass>> m_elementPasses; std::multimap<QString, PropertyPassInfo> m_propertyPasses; std::unordered_map<quint32, BindingInfo> m_bindingsByLocation; QQmlJSImportVisitor *m_visitor; QQmlJSTypeResolver *m_typeResolver; -}; -class Q_QMLCOMPILER_EXPORT DebugElementPass : public ElementPass -{ - void run(const Element &element) override; + PassManager *q_ptr; }; -class Q_QMLCOMPILER_EXPORT DebugPropertyPass : public QQmlSA::PropertyPass +class FixSuggestionPrivate { + Q_DECLARE_PUBLIC(FixSuggestion) + friend class QT_PREPEND_NAMESPACE(QQmlJSFixSuggestion); + public: - DebugPropertyPass(QQmlSA::PassManager *manager); - - void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override; - void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope, - const QQmlSA::Element &value) override; - void onWrite(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) override; -}; -} + explicit FixSuggestionPrivate(FixSuggestion *); + FixSuggestionPrivate(FixSuggestion *, const QString &fixDescription, + const QQmlSA::SourceLocation &location, const QString &replacement); + FixSuggestionPrivate(FixSuggestion *, const FixSuggestionPrivate &); + FixSuggestionPrivate(FixSuggestion *, FixSuggestionPrivate &&); + ~FixSuggestionPrivate() = default; + + QString fixDescription() const; + QQmlSA::SourceLocation location() const; + QString replacement() const; + + void setFileName(const QString &); + QString fileName() const; -#define QmlLintPluginInterface_iid "org.qt-project.Qt.Qml.SA.LintPlugin/1.0" + void setHint(const QString &); + QString hint() const; + + void setAutoApplicable(bool autoApplicable = true); + bool isAutoApplicable() const; + + static QQmlJSFixSuggestion &fixSuggestion(QQmlSA::FixSuggestion &); + static const QQmlJSFixSuggestion &fixSuggestion(const QQmlSA::FixSuggestion &); + +private: + QQmlJSFixSuggestion m_fixSuggestion; + QQmlSA::FixSuggestion *q_ptr; +}; -Q_DECLARE_INTERFACE(QQmlSA::LintPlugin, QmlLintPluginInterface_iid) +} // namespace QQmlSA QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmlsaconstants.h b/src/qmlcompiler/qqmlsaconstants.h new file mode 100644 index 0000000000..f3fe1d0257 --- /dev/null +++ b/src/qmlcompiler/qqmlsaconstants.h @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLSACONSTANTS_H +#define QQMLSACONSTANTS_H + +#include <QtCore/qfileinfo.h> + +QT_BEGIN_NAMESPACE + +class QQmlJSScope; + +namespace QQmlSA { + +enum BindingType : unsigned int { + Invalid, + BoolLiteral, + NumberLiteral, + StringLiteral, + RegExpLiteral, + Null, + Translation, + TranslationById, + Script, + Object, + Interceptor, + ValueSource, + AttachedProperty, + GroupProperty, +}; + +enum ScriptBindingKind : unsigned int { + Script_Invalid, + Script_PropertyBinding, // property int p: 1 + 1 + Script_SignalHandler, // onSignal: { ... } + Script_ChangeHandler, // onXChanged: { ... } +}; + +enum ExtensionKind { + NotExtension, + ExtensionType, + ExtensionNamespace, +}; + +enum class BindingTargetSpecifier { + SimplePropertyTarget, // e.g. `property int p: 42` + ListPropertyTarget, // e.g. `property list<Item> pList: [ Text {} ]` + UnnamedPropertyTarget // default property bindings, where property name is unspecified +}; + +enum class ScopeType { + JSFunctionScope, + JSLexicalScope, + QMLScope, + GroupedPropertyScope, + AttachedPropertyScope, + EnumScope +}; + +enum class QQmlJSMetaMethodAccess { Private, Protected, Public }; +} // namespace QQmlSA + +QT_END_NAMESPACE + +#endif // QQMLSACONSTANTS_H diff --git a/src/qmlcompiler/qqmlsasourcelocation.cpp b/src/qmlcompiler/qqmlsasourcelocation.cpp new file mode 100644 index 0000000000..63b61cce5b --- /dev/null +++ b/src/qmlcompiler/qqmlsasourcelocation.cpp @@ -0,0 +1,125 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qqmlsasourcelocation.h" +#include "qqmlsasourcelocation_p.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +namespace QQmlSA { + +static_assert(SourceLocationPrivate::sizeOfSourceLocation() == sizeof(SourceLocation)); + +/*! + \class QQmlSA::SourceLocation + \inmodule QtQmlCompiler + + \brief Represents a location or region in the source code. + */ +QQmlSA::SourceLocation::SourceLocation(quint32 offset, quint32 length, quint32 line, quint32 column) +{ + new (m_data) QQmlJS::SourceLocation{ offset, length, line, column }; +} + +// explicitly defaulted out-of-line for PIMPL +QQmlSA::SourceLocation::SourceLocation(const SourceLocation &other) = default; +QQmlSA::SourceLocation & QQmlSA::SourceLocation::operator=(const QQmlSA::SourceLocation &other) = default; +SourceLocation::~SourceLocation() = default; + +bool QQmlSA::SourceLocation::isValid() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).isValid(); +} + +/*! + Returns the offset of the beginning of this source location. + */ +quint32 QQmlSA::SourceLocation::begin() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).begin(); +} + +/*! + Returns the offset of the end of this source location. + */ +quint32 QQmlSA::SourceLocation::end() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).end(); +} + +/*! + Returns the offset of the beginning of this source location. + */ +quint32 QQmlSA::SourceLocation::offset() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).offset; +} + +/*! + Returns the length of this source location. + */ +quint32 QQmlSA::SourceLocation::length() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).length; +} + +/*! + Returns the line number containing the beginning of this source location. + */ +quint32 QQmlSA::SourceLocation::startLine() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).startLine; +} + +/*! + Returns the column number containing the beginning of this source location. + */ +quint32 QQmlSA::SourceLocation::startColumn() const +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(*this).startColumn; +} + +/*! + Returns a source location of lenth zero pointing to the beginning of this + source location. + */ +QQmlSA::SourceLocation QQmlSA::SourceLocation::startZeroLengthLocation() const +{ + QQmlSA::SourceLocation saLocation; + auto &wrappedLocation = reinterpret_cast<QQmlJS::SourceLocation &>(saLocation.m_data); + wrappedLocation = + QQmlSA::SourceLocationPrivate::sourceLocation(*this).startZeroLengthLocation(); + + return saLocation; +} + +/*! + Returns a source location of lenth zero pointing to the end of this source + location pointing to \a text. + */ +QQmlSA::SourceLocation QQmlSA::SourceLocation::endZeroLengthLocation(QStringView text) const +{ + QQmlSA::SourceLocation saLocation; + auto &wrappedLocation = reinterpret_cast<QQmlJS::SourceLocation &>(saLocation.m_data); + wrappedLocation = wrappedLocation.endZeroLengthLocation(text); + + return saLocation; +} + +qsizetype QQmlSA::SourceLocation::qHashImpl(const SourceLocation &location, qsizetype seed) +{ + return qHash(QQmlSA::SourceLocationPrivate::sourceLocation(location), seed); +} + +bool QQmlSA::SourceLocation::operatorEqualsImpl(const SourceLocation &lhs, + const SourceLocation &rhs) +{ + return QQmlSA::SourceLocationPrivate::sourceLocation(lhs) + == QQmlSA::SourceLocationPrivate::sourceLocation(rhs); +} + +} // namespace QQmlSA + +QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmlsasourcelocation.h b/src/qmlcompiler/qqmlsasourcelocation.h new file mode 100644 index 0000000000..38f21a2167 --- /dev/null +++ b/src/qmlcompiler/qqmlsasourcelocation.h @@ -0,0 +1,83 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef QQMLSASOURCELOCATION_H +#define QQMLSASOURCELOCATION_H + +#include <QtQmlCompiler/qtqmlcompilerexports.h> + +#include <QtCore/qstringview.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +class SourceLocation; +} // namespace QQmlJS + +namespace QQmlSA { + +class SourceLocationPrivate; + +class Q_QMLCOMPILER_EXPORT SourceLocation +{ + friend class QT_PREPEND_NAMESPACE(QQmlSA::SourceLocationPrivate); + +public: + explicit SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, + quint32 column = 0); + SourceLocation(const SourceLocation &); + SourceLocation(SourceLocation &&other) + { + memcpy(m_data, other.m_data, sizeofSourceLocation); + memset(other.m_data, 0, sizeofSourceLocation); + } + SourceLocation &operator=(const SourceLocation &); + SourceLocation &operator=(SourceLocation &&other) + { + memcpy(m_data, other.m_data, sizeofSourceLocation); + memset(other.m_data, 0, sizeofSourceLocation); + return *this; + } + ~SourceLocation(); + + bool isValid() const; + + quint32 begin() const; + quint32 end() const; + + quint32 offset() const; + quint32 length() const; + quint32 startLine() const; + quint32 startColumn() const; + + SourceLocation startZeroLengthLocation() const; + SourceLocation endZeroLengthLocation(QStringView text) const; + + friend qsizetype qHash(const SourceLocation &location, qsizetype seed = 0) + { + return qHashImpl(location, seed); + } + + friend bool operator==(const SourceLocation &lhs, const SourceLocation &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + + friend bool operator!=(const SourceLocation &lhs, const SourceLocation &rhs) + { + return !(lhs == rhs); + } + +private: + static qsizetype qHashImpl(const SourceLocation &location, qsizetype seed); + static bool operatorEqualsImpl(const SourceLocation &, const SourceLocation &); + + static constexpr qsizetype sizeofSourceLocation = 4 * sizeof(quint32); + char m_data[sizeofSourceLocation] = {}; +}; + +} // namespace QQmlSA + +QT_END_NAMESPACE + +#endif // QQMLSASOURCELOCATION_H diff --git a/src/qmlcompiler/qqmlsasourcelocation_p.h b/src/qmlcompiler/qqmlsasourcelocation_p.h new file mode 100644 index 0000000000..fa78512c46 --- /dev/null +++ b/src/qmlcompiler/qqmlsasourcelocation_p.h @@ -0,0 +1,53 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef QQMLSASOURCELOCATION_P_H +#define QQMLSASOURCELOCATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include "qqmlsasourcelocation.h" + +#include <QtQml/private/qqmljssourcelocation_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlSA { + +class SourceLocationPrivate +{ +public: + static const QQmlJS::SourceLocation & + sourceLocation(const QQmlSA::SourceLocation &sourceLocation) + { + return reinterpret_cast<const QQmlJS::SourceLocation &>(sourceLocation.m_data); + } + + static QQmlSA::SourceLocation + createQQmlSASourceLocation(const QQmlJS::SourceLocation &jsLocation) + { + QQmlSA::SourceLocation saLocation; + auto &internal = reinterpret_cast<QQmlJS::SourceLocation &>(saLocation.m_data); + internal = jsLocation; + return saLocation; + } + + static constexpr qsizetype sizeOfSourceLocation() + { + return SourceLocation::sizeofSourceLocation; + } +}; + +} // namespace QQmlSA + +QT_END_NAMESPACE + +#endif // QQMLSASOURCELOCATION_P_H diff --git a/src/qmldom/qqmldomtypesreader.cpp b/src/qmldom/qqmldomtypesreader.cpp index 12cba9ae0e..ce0f3b8c05 100644 --- a/src/qmldom/qqmldomtypesreader.cpp +++ b/src/qmldom/qqmldomtypesreader.cpp @@ -59,11 +59,11 @@ void QmltypesReader::insertSignalOrMethod(const QQmlJSMetaMethod &metaMethod, MethodInfo methodInfo; // ### confusion between Method and Slot. Method should be removed. switch (metaMethod.methodType()) { - case QQmlJSMetaMethod::Method: - case QQmlJSMetaMethod::Slot: + case QQmlJSMetaMethodType::Method: + case QQmlJSMetaMethodType::Slot: methodInfo.methodType = MethodInfo::MethodType::Method; break; - case QQmlJSMetaMethod::Signal: + case QQmlJSMetaMethodType::Signal: methodInfo.methodType = MethodInfo::MethodType::Signal; break; default: diff --git a/src/qmlls/qqmllintsuggestions.cpp b/src/qmlls/qqmllintsuggestions.cpp index 4413ca40e8..474372ce52 100644 --- a/src/qmlls/qqmllintsuggestions.cpp +++ b/src/qmlls/qqmllintsuggestions.cpp @@ -154,7 +154,7 @@ void QmlLintSuggestions::diagnose(const QByteArray &url) fileContents = doc.field(Fields::code).value().toString(); QStringList qmltypesFiles; QStringList resourceFiles; - QList<QQmlJSLogger::Category> categories; + QList<QQmlJS::LoggerCategory> categories; QQmlJSLinter linter(imports); diff --git a/tests/auto/qml/qmllint/lintplugin.cpp b/tests/auto/qml/qmllint/lintplugin.cpp index 47279adcaa..86c0d450f0 100644 --- a/tests/auto/qml/qmllint/lintplugin.cpp +++ b/tests/auto/qml/qmllint/lintplugin.cpp @@ -5,7 +5,7 @@ using namespace Qt::StringLiterals; -static constexpr LoggerWarningId plugin { "testPlugin.test" }; +static constexpr QQmlJS::LoggerWarningId plugin{ "testPlugin.test" }; class ElementTest : public QQmlSA::ElementPass { @@ -17,25 +17,25 @@ public: bool shouldRun(const QQmlSA::Element &element) override { - return element->baseType() == m_rectangle; + return element.baseType() == m_rectangle; } void run(const QQmlSA::Element &element) override { - auto property = element->property(u"radius"_s); - if (!property.isValid() || element->property(u"radius"_s).typeName() != u"double") { - emitWarning(u"Failed to verify radius property", plugin, element->sourceLocation()); + auto property = element.property(u"radius"_s); + if (!property.isValid() || element.property(u"radius"_s).typeName() != u"double") { + emitWarning(u"Failed to verify radius property", plugin, element.sourceLocation()); return; } - auto bindings = element->propertyBindings(u"radius"_s); + auto bindings = element.propertyBindings(u"radius"_s); if (bindings.isEmpty() || bindings.constFirst().numberValue() != 5) { emitWarning(u"Failed to verify radius property binding", plugin, - element->sourceLocation()); + element.sourceLocation()); return; } - emitWarning(u"ElementTest OK", plugin, element->sourceLocation()); + emitWarning(u"ElementTest OK", plugin, element.sourceLocation()); } private: @@ -48,37 +48,37 @@ public: PropertyTest(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager) { } void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override { emitWarning(u"Saw binding on %1 property %2 with value %3 (and type %4) in scope %5"_s - .arg(element->baseTypeName(), propertyName, + .arg(element.baseTypeName(), propertyName, value.isNull() ? u"NULL"_s - : (value->internalName().isNull() ? value->baseTypeName() - : value->baseTypeName())) + : (value.internalName().isNull() ? value.baseTypeName() + : value.baseTypeName())) .arg(binding.bindingType()) - .arg(bindingScope->baseTypeName()), - plugin, bindingScope->sourceLocation()); + .arg(bindingScope.baseTypeName()), + plugin, bindingScope.sourceLocation()); } void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override { emitWarning(u"Saw read on %1 property %2 in scope %3"_s.arg( - element->baseTypeName(), propertyName, readScope->baseTypeName()), + element.baseTypeName(), propertyName, readScope.baseTypeName()), plugin, location); } void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) override + QQmlSA::SourceLocation location) override { emitWarning(u"Saw write on %1 property %2 with value %3 in scope %4"_s.arg( - element->baseTypeName(), propertyName, - (value->internalName().isNull() ? value->baseTypeName() - : value->internalName()), - writeScope->baseTypeName()), + element.baseTypeName(), propertyName, + (value.internalName().isNull() ? value.baseTypeName() + : value.internalName()), + writeScope.baseTypeName()), plugin, location); } }; @@ -109,7 +109,7 @@ private: void LintPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) { - if (!rootElement->filePath().endsWith(u"_pluginTest.qml")) + if (!rootElement.filePath().endsWith(u"_pluginTest.qml")) return; manager->registerElementPass(std::make_unique<ElementTest>(manager)); diff --git a/tests/auto/qml/qmllint/lintplugin.h b/tests/auto/qml/qmllint/lintplugin.h index 76733ca7a7..28d30b225d 100644 --- a/tests/auto/qml/qmllint/lintplugin.h +++ b/tests/auto/qml/qmllint/lintplugin.h @@ -6,7 +6,7 @@ #include <QtPlugin> #include <QtCore/qobject.h> -#include <QtQmlCompiler/private/qqmlsa_p.h> +#include <QtQmlCompiler/qqmlsa.h> class LintPlugin : public QObject, public QQmlSA::LintPlugin { diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 8c35553745..cfd93eaf39 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -117,7 +117,7 @@ private: QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, - QList<QQmlJSLogger::Category> *categories = nullptr, bool autoFixable = false, + QList<QQmlJS::LoggerCategory> *categories = nullptr, bool autoFixable = false, LintType type = LintFile); void searchWarnings(const QJsonArray &warnings, const QString &string, @@ -139,7 +139,7 @@ private: void runTest(const QString &testFile, const Result &result, QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, - QList<QQmlJSLogger::Category> *categories = nullptr); + QList<QQmlJS::LoggerCategory> *categories = nullptr); QString m_qmllintPath; QString m_qmljsrootgenPath; @@ -357,12 +357,12 @@ void TestQmllint::verifyJsRoot() QString currentJsRootContent, generatedJsRootContent; QFile currentJsRoot(currentJsRootPath); - QVERIFY(currentJsRoot.open(QFile::ReadOnly)); + QVERIFY(currentJsRoot.open(QFile::ReadOnly | QIODevice::Text)); currentJsRootContent = QString::fromUtf8(currentJsRoot.readAll()); currentJsRoot.close(); QFile generatedJsRoot(dir.path() + QDir::separator() + "jsroot.qmltypes"); - QVERIFY(generatedJsRoot.open(QFile::ReadOnly)); + QVERIFY(generatedJsRoot.open(QFile::ReadOnly | QIODevice::Text)); generatedJsRootContent = QString::fromUtf8(generatedJsRoot.readAll()); generatedJsRoot.close(); @@ -1305,8 +1305,10 @@ void TestQmllint::compilerWarnings() auto category = std::find(categories.begin(), categories.end(), qmlCompiler); Q_ASSERT(category != categories.end()); - if (enableCompilerWarnings) - category->setLevel(u"warning"_s); + if (enableCompilerWarnings) { + category->setLevel(QtWarningMsg); + category->setIgnored(false); + } runTest(filename, result, {}, {}, {}, UseDefaultImports, &categories); } @@ -1401,7 +1403,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings, QStringList importPaths, QStringList qmldirFiles, QStringList resources, DefaultImportOption defaultImports, - QList<QQmlJSLogger::Category> *categories, bool autoFixable, + QList<QQmlJS::LoggerCategory> *categories, bool autoFixable, LintType type) { QJsonArray jsonOutput; @@ -1415,15 +1417,14 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs ? m_defaultImportPaths + importPaths : importPaths; if (type == LintFile) { - const QList<QQmlJSLogger::Category> resolvedCategories = categories != nullptr - ? *categories - : QQmlJSLogger::defaultCategories(); + const QList<QQmlJS::LoggerCategory> resolvedCategories = + categories != nullptr ? *categories : QQmlJSLogger::defaultCategories(); lintResult = m_linter.lintFile( lintedFile, nullptr, true, &jsonOutput, resolvedImportPaths, qmldirFiles, resources, resolvedCategories); } else { - lintResult = m_linter.lintModule( - fileToLint, true, &jsonOutput, resolvedImportPaths, resources); + lintResult = + m_linter.lintModule(fileToLint, true, &jsonOutput, resolvedImportPaths, resources); } bool success = lintResult == QQmlJSLinter::LintSuccess; @@ -1480,7 +1481,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs void TestQmllint::runTest(const QString &testFile, const Result &result, QStringList importDirs, QStringList qmltypesFiles, QStringList resources, DefaultImportOption defaultImports, - QList<QQmlJSLogger::Category> *categories) + QList<QQmlJS::LoggerCategory> *categories) { QJsonArray warnings; callQmllint(testFile, result.flags.testFlag(Result::Flag::ExitsNormally), &warnings, importDirs, @@ -1670,12 +1671,12 @@ void TestQmllint::qrcUrlImport() void TestQmllint::attachedPropertyReuse() { - auto categories = QQmlJSLogger::defaultCategories(); auto category = std::find(categories.begin(), categories.end(), qmlAttachedPropertyReuse); Q_ASSERT(category != categories.end()); - category->setLevel(u"warning"_s); + category->setLevel(QtWarningMsg); + category->setIgnored(false); runTest("attachedPropNotReused.qml", Result { { Message { QStringLiteral("Using attached type QQuickKeyNavigationAttached " "already initialized in a parent " diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp index 09c6601858..aeda087dfc 100644 --- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp +++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp @@ -160,14 +160,14 @@ void tst_qqmljsscope::orderedBindings() QCOMPARE(std::distance(pBindingsBegin, pBindingsEnd), 2); // check that the bindings are properly ordered - QCOMPARE(pBindingsBegin->bindingType(), QQmlJSMetaPropertyBinding::Object); - QCOMPARE(std::next(pBindingsBegin)->bindingType(), QQmlJSMetaPropertyBinding::Interceptor); + QCOMPARE(pBindingsBegin->bindingType(), QQmlSA::BindingType::Object); + QCOMPARE(std::next(pBindingsBegin)->bindingType(), QQmlSA::BindingType::Interceptor); auto [itemsBindingsBegin, itemsBindingsEnd] = root->ownPropertyBindings(u"items"_s); QCOMPARE(std::distance(itemsBindingsBegin, itemsBindingsEnd), 2); - QCOMPARE(itemsBindingsBegin->bindingType(), QQmlJSMetaPropertyBinding::Object); - QCOMPARE(std::next(itemsBindingsBegin)->bindingType(), QQmlJSMetaPropertyBinding::Object); + QCOMPARE(itemsBindingsBegin->bindingType(), QQmlSA::BindingType::Object); + QCOMPARE(std::next(itemsBindingsBegin)->bindingType(), QQmlSA::BindingType::Object); QCOMPARE(itemsBindingsBegin->objectType()->baseTypeName(), u"Item"_s); QCOMPARE(std::next(itemsBindingsBegin)->objectType()->baseTypeName(), u"Text"_s); @@ -184,8 +184,8 @@ void tst_qqmljsscope::signalCreationDifferences() const auto conflicting = root->ownMethods(u"conflictingPropertyChanged"_s); QCOMPARE(conflicting.size(), 2); - QCOMPARE(conflicting[0].methodType(), QQmlJSMetaMethod::Signal); - QCOMPARE(conflicting[1].methodType(), QQmlJSMetaMethod::Signal); + QCOMPARE(conflicting[0].methodType(), QQmlJSMetaMethodType::Signal); + QCOMPARE(conflicting[1].methodType(), QQmlJSMetaMethodType::Signal); const QQmlJSMetaMethod *explicitMethod = nullptr; if (conflicting[0].isImplicitQmlPropertyChangeSignal()) @@ -302,7 +302,7 @@ void tst_qqmljsscope::groupedProperties() const auto getBindingsWithinGroup = [&](QMultiHash<QString, QQmlJSMetaPropertyBinding> *bindings, qsizetype index) -> void { const auto &binding = anchorBindings[index]; - QCOMPARE(binding.bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); + QCOMPARE(binding.bindingType(), QQmlSA::BindingType::GroupProperty); auto anchorScope = binding.groupType(); QVERIFY(anchorScope); *bindings = anchorScope->ownPropertyBindings(); @@ -316,14 +316,14 @@ void tst_qqmljsscope::groupedProperties() QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfType; getBindingsWithinGroup(&bindingsOfType, 0); QCOMPARE(bindingsOfType.size(), 2); - QCOMPARE(value(bindingsOfType, u"left"_s).bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfType, u"left"_s).bindingType(), QQmlSA::BindingType::Script); QCOMPARE(value(bindingsOfType, u"leftMargin"_s).bindingType(), - QQmlJSMetaPropertyBinding::NumberLiteral); + QQmlSA::BindingType::NumberLiteral); QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfBaseType; getBindingsWithinGroup(&bindingsOfBaseType, 1); QCOMPARE(bindingsOfBaseType.size(), 1); - QCOMPARE(value(bindingsOfBaseType, u"top"_s).bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfBaseType, u"top"_s).bindingType(), QQmlSA::BindingType::Script); } void tst_qqmljsscope::descriptiveNameOfNull() @@ -363,8 +363,8 @@ void tst_qqmljsscope::groupedPropertiesConsistency() // The binding order in QQmlJSScope case is "reversed": first come // bindings on the leaf type, followed by the bindings on the base type - QCOMPARE(fontBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); - QCOMPARE(fontBindings[1].bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(fontBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty); + QCOMPARE(fontBindings[1].bindingType(), QQmlSA::BindingType::Script); } } @@ -378,7 +378,7 @@ void tst_qqmljsscope::groupedPropertySyntax() // The binding order in QQmlJSScope case is "reversed": first come // bindings on the leaf type, followed by the bindings on the base type - QCOMPARE(fontBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); + QCOMPARE(fontBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty); auto fontScope = fontBindings[0].groupType(); QVERIFY(fontScope); QCOMPARE(fontScope->accessSemantics(), QQmlJSScope::AccessSemantics::Value); @@ -390,9 +390,8 @@ void tst_qqmljsscope::groupedPropertySyntax() return bindings.value(key, QQmlJSMetaPropertyBinding(QQmlJS::SourceLocation {})); }; - QCOMPARE(value(subbindings, u"pixelSize"_s).bindingType(), - QQmlJSMetaPropertyBinding::NumberLiteral); - QCOMPARE(value(subbindings, u"bold"_s).bindingType(), QQmlJSMetaPropertyBinding::BoolLiteral); + QCOMPARE(value(subbindings, u"pixelSize"_s).bindingType(), QQmlSA::BindingType::NumberLiteral); + QCOMPARE(value(subbindings, u"bold"_s).bindingType(), QQmlSA::BindingType::BoolLiteral); } void tst_qqmljsscope::attachedProperties() @@ -407,7 +406,7 @@ void tst_qqmljsscope::attachedProperties() const auto getBindingsWithinAttached = [&](QMultiHash<QString, QQmlJSMetaPropertyBinding> *bindings, qsizetype index) -> void { const auto &binding = keysBindings[index]; - QCOMPARE(binding.bindingType(), QQmlJSMetaPropertyBinding::AttachedProperty); + QCOMPARE(binding.bindingType(), QQmlSA::BindingType::AttachedProperty); auto keysScope = binding.attachingType(); QVERIFY(keysScope); QCOMPARE(keysScope->accessSemantics(), QQmlJSScope::AccessSemantics::Reference); @@ -422,23 +421,21 @@ void tst_qqmljsscope::attachedProperties() QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfType; getBindingsWithinAttached(&bindingsOfType, 0); QCOMPARE(bindingsOfType.size(), 2); - QCOMPARE(value(bindingsOfType, u"enabled"_s).bindingType(), - QQmlJSMetaPropertyBinding::BoolLiteral); - QCOMPARE(value(bindingsOfType, u"forwardTo"_s).bindingType(), - QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfType, u"enabled"_s).bindingType(), QQmlSA::BindingType::BoolLiteral); + QCOMPARE(value(bindingsOfType, u"forwardTo"_s).bindingType(), QQmlSA::BindingType::Script); QMultiHash<QString, QQmlJSMetaPropertyBinding> bindingsOfBaseType; getBindingsWithinAttached(&bindingsOfBaseType, 1); QCOMPARE(bindingsOfBaseType.size(), 1); - QCOMPARE(value(bindingsOfBaseType, u"priority"_s).bindingType(), - QQmlJSMetaPropertyBinding::Script); + QCOMPARE(value(bindingsOfBaseType, u"priority"_s).bindingType(), QQmlSA::BindingType::Script); } inline QString getScopeName(const QQmlJSScope::ConstPtr &scope) { Q_ASSERT(scope); QQmlJSScope::ScopeType type = scope->scopeType(); - if (type == QQmlJSScope::GroupedPropertyScope || type == QQmlJSScope::AttachedPropertyScope) + if (type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope) return scope->internalName(); return scope->baseTypeName(); } @@ -537,8 +534,9 @@ void tst_qqmljsscope::scriptIndices() const auto suitableScope = [](const QQmlJSScope::ConstPtr &scope) { const auto type = scope->scopeType(); - return type == QQmlJSScope::QMLScope || type == QQmlJSScope::GroupedPropertyScope - || type == QQmlJSScope::AttachedPropertyScope; + return type == QQmlSA::ScopeType::QMLScope + || type == QQmlSA::ScopeType::GroupedPropertyScope + || type == QQmlSA::ScopeType::AttachedPropertyScope; }; QList<QQmlJSScope::ConstPtr> queue; @@ -550,7 +548,7 @@ void tst_qqmljsscope::scriptIndices() if (suitableScope(current)) { const auto methods = current->ownMethods(); for (const auto &method : methods) { - if (method.methodType() == QQmlJSMetaMethod::Signal) + if (method.methodType() == QQmlJSMetaMethodType::Signal) continue; QString name = method.methodName(); auto relativeIndex = method.jsFunctionIndex(); @@ -563,7 +561,7 @@ void tst_qqmljsscope::scriptIndices() const auto bindings = current->ownPropertyBindings(); for (const auto &binding : bindings) { - if (binding.bindingType() != QQmlJSMetaPropertyBinding::Script) + if (binding.bindingType() != QQmlSA::BindingType::Script) continue; QString name = binding.propertyName(); auto relativeIndex = binding.scriptIndex(); @@ -711,32 +709,32 @@ void tst_qqmljsscope::resolvedNonUniqueScopes() { auto topLevelBindings = root->propertyBindings(u"Component"_s); QCOMPARE(topLevelBindings.size(), 1); - QCOMPARE(topLevelBindings[0].bindingType(), QQmlJSMetaPropertyBinding::AttachedProperty); + QCOMPARE(topLevelBindings[0].bindingType(), QQmlSA::BindingType::AttachedProperty); auto componentScope = topLevelBindings[0].attachingType(); auto componentBindings = componentScope->ownPropertyBindings(); QCOMPARE(componentBindings.size(), 2); auto onCompletedBinding = value(componentBindings, u"onCompleted"_s); QVERIFY(onCompletedBinding.isValid()); - QCOMPARE(onCompletedBinding.bindingType(), QQmlJSMetaPropertyBinding::Script); - QCOMPARE(onCompletedBinding.scriptKind(), QQmlJSMetaPropertyBinding::Script_SignalHandler); + QCOMPARE(onCompletedBinding.bindingType(), QQmlSA::BindingType::Script); + QCOMPARE(onCompletedBinding.scriptKind(), QQmlSA::ScriptBindingKind::Script_SignalHandler); auto onDestructionBinding = value(componentBindings, u"onDestruction"_s); QVERIFY(onDestructionBinding.isValid()); - QCOMPARE(onDestructionBinding.bindingType(), QQmlJSMetaPropertyBinding::Script); + QCOMPARE(onDestructionBinding.bindingType(), QQmlSA::BindingType::Script); QCOMPARE(onDestructionBinding.scriptKind(), - QQmlJSMetaPropertyBinding::Script_SignalHandler); + QQmlSA::ScriptBindingKind::Script_SignalHandler); } { auto topLevelBindings = root->propertyBindings(u"p"_s); QCOMPARE(topLevelBindings.size(), 1); - QCOMPARE(topLevelBindings[0].bindingType(), QQmlJSMetaPropertyBinding::GroupProperty); + QCOMPARE(topLevelBindings[0].bindingType(), QQmlSA::BindingType::GroupProperty); auto pScope = topLevelBindings[0].groupType(); auto pBindings = pScope->ownPropertyBindings(); QCOMPARE(pBindings.size(), 1); auto onXChangedBinding = value(pBindings, u"onXChanged"_s); QVERIFY(onXChangedBinding.isValid()); - QCOMPARE(onXChangedBinding.bindingType(), QQmlJSMetaPropertyBinding::Script); - QCOMPARE(onXChangedBinding.scriptKind(), QQmlJSMetaPropertyBinding::Script_SignalHandler); + QCOMPARE(onXChangedBinding.bindingType(), QQmlSA::BindingType::Script); + QCOMPARE(onXChangedBinding.scriptKind(), QQmlSA::ScriptBindingKind::Script_SignalHandler); } } diff --git a/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp b/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp index 7d8a216c78..801fc3ca2a 100644 --- a/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp +++ b/tests/auto/quickcontrols/sanity/quickcontrolssanity.cpp @@ -10,7 +10,7 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -static constexpr LoggerWarningId qmlControlsSanity{ "QuickControlsSanity.controls-sanity" }; +static constexpr QQmlJS::LoggerWarningId qmlControlsSanity{ "QuickControlsSanity.controls-sanity" }; AnchorsElementPass::AnchorsElementPass(QQmlSA::PassManager *manager) : QQmlSA::ElementPass(manager), m_item(resolveType("QtQuick"_L1, "Item"_L1)) @@ -19,14 +19,14 @@ AnchorsElementPass::AnchorsElementPass(QQmlSA::PassManager *manager) bool AnchorsElementPass::shouldRun(const QQmlSA::Element &element) { - return !m_item.isNull() && element->inherits(m_item) - && element->hasOwnPropertyBindings("anchors"_L1); + return !m_item.isNull() && element.inherits(m_item) + && element.hasOwnPropertyBindings("anchors"_L1); } void AnchorsElementPass::run(const QQmlSA::Element &element) { - const auto anchorBindings = element->propertyBindings("anchors"_L1); - for (auto &&anchors : anchorBindings) { + const auto anchorBindings = element.propertyBindings("anchors"_L1); + for (const auto &anchors : anchorBindings) { emitWarning(u"Using anchors here"_s, qmlControlsSanity, anchors.sourceLocation()); } } @@ -38,17 +38,19 @@ SignalHandlerPass::SignalHandlerPass(QQmlSA::PassManager *manager) bool SignalHandlerPass::shouldRun(const QQmlSA::Element &element) { - return !m_qtobject.isNull() && element->inherits(m_qtobject); + return !m_qtobject.isNull() && element.inherits(m_qtobject); } void SignalHandlerPass::run(const QQmlSA::Element &element) { - for (auto &&[propertyName, propertyBinding] : - element->ownPropertyBindings().asKeyValueRange()) { + const auto &ownBindings = element.ownPropertyBindings(); + for (auto it = ownBindings.constBegin(); it != ownBindings.constEnd(); ++it) { + const auto &propertyName = it.key(); + const auto &propertyBinding = it.value(); // Already script binding, check if the script kind is signal handler - if (propertyBinding.bindingType() == QQmlJSMetaPropertyBinding::Script) { - if (propertyBinding.scriptKind() == QQmlJSMetaPropertyBinding::Script_SignalHandler) { + if (propertyBinding.bindingType() == QQmlSA::BindingType::Script) { + if (propertyBinding.scriptKind() == QQmlSA::ScriptBindingKind::Script_SignalHandler) { emitWarning(u"Declared signal handler \"%1\""_s.arg(propertyName), qmlControlsSanity, propertyBinding.sourceLocation()); } @@ -56,11 +58,11 @@ void SignalHandlerPass::run(const QQmlSA::Element &element) } // Current property is attached property, recursively go through attaching type - if (propertyBinding.bindingType() == QQmlJSMetaPropertyBinding::AttachedProperty) { - const auto scope = propertyBinding.attachingType(); + if (propertyBinding.bindingType() == QQmlSA::BindingType::AttachedProperty) { + const auto scope = QQmlSA::Element{ propertyBinding.attachingType() }; run(scope); } - }; + } } FunctionDeclarationPass::FunctionDeclarationPass(QQmlSA::PassManager *manager) @@ -76,12 +78,12 @@ bool FunctionDeclarationPass::shouldRun(const QQmlSA::Element &element) void FunctionDeclarationPass::run(const QQmlSA::Element &element) { - for (auto &&method : element->ownMethods()) { - if (method.methodType() != QQmlJSMetaMethod::Method) + for (const auto &method : element.ownMethods()) { + if (method.methodType() != QQmlSA::MethodType::Method) continue; emitWarning(u"Declared function \"%1\""_s.arg(method.methodName()), qmlControlsSanity, - element->sourceLocation()); + element.sourceLocation()); } } diff --git a/tests/auto/quickcontrols/sanity/tst_sanity.cpp b/tests/auto/quickcontrols/sanity/tst_sanity.cpp index af3f119745..cde062d49e 100644 --- a/tests/auto/quickcontrols/sanity/tst_sanity.cpp +++ b/tests/auto/quickcontrols/sanity/tst_sanity.cpp @@ -48,7 +48,7 @@ private: QStringList m_importPaths; QQmlJSLinter m_linter; - QList<QQmlJSLogger::Category> m_categories; + QList<QQmlJS::LoggerCategory> m_categories; }; tst_Sanity::tst_Sanity() @@ -65,9 +65,11 @@ tst_Sanity::tst_Sanity() for (auto &category : m_categories) { if (category == qmlDeferredPropertyId || category == qmlAttachedPropertyReuse) { - category.setLevel(u"warning"_s); + category.setLevel(QtWarningMsg); + category.setIgnored(false); } else { - category.setLevel(u"disable"_s); + category.setLevel(QtCriticalMsg); + category.setIgnored(true); } } } diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index 947a13da08..b244868a17 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -6,6 +6,7 @@ #include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h> #include <QtQmlCompiler/private/qqmljscompiler_p.h> #include <QtQmlCompiler/private/qqmljslinter_p.h> +#include <QtQmlCompiler/private/qqmljsloggingutils_p.h> #include <QtCore/qdebug.h> #include <QtCore/qfile.h> @@ -32,7 +33,7 @@ constexpr int JSON_LOGGING_FORMAT_REVISION = 3; int main(int argv, char *argc[]) { QHashSeed::setDeterministicGlobalSeed(); - QList<QQmlJSLogger::Category> categories; + QList<QQmlJS::LoggerCategory> categories; QCoreApplication app(argv, argc); QCoreApplication::setApplicationName("qmllint"); @@ -150,20 +151,36 @@ All warnings can be set to three levels: QLatin1String("directory")); parser.addOption(pluginPathsOption); - auto addCategory = [&](const QQmlJSLogger::Category &category) { + auto levelToString = [](const QQmlJS::LoggerCategory &category) -> QString { + Q_ASSERT(category.isIgnored() || category.level() != QtCriticalMsg); + if (category.isIgnored()) + return QStringLiteral("disable"); + + switch (category.level()) { + case QtInfoMsg: + return QStringLiteral("info"); + case QtWarningMsg: + return QStringLiteral("warning"); + default: + Q_UNREACHABLE(); + break; + } + }; + + auto addCategory = [&](const QQmlJS::LoggerCategory &category) { categories.push_back(category); - if (category.isDefault) + if (category.isDefault()) return; QCommandLineOption option( category.id().name().toString(), - category.description - + QStringLiteral(" (default: %1)").arg(category.levelToString()), - QStringLiteral("level"), category.levelToString()); - if (category.ignored) + category.description() + + QStringLiteral(" (default: %1)").arg(levelToString(category)), + QStringLiteral("level"), levelToString(category)); + if (category.isIgnored()) option.setFlags(QCommandLineOption::HiddenFromHelp); parser.addOption(option); - settings.addOption(QStringLiteral("Warnings/") + category.settingsName, - category.levelToString()); + settings.addOption(QStringLiteral("Warnings/") + category.settingsName(), + levelToString(category)); }; for (const auto &category : QQmlJSLogger::defaultCategories()) { @@ -192,21 +209,30 @@ All warnings can be set to three levels: auto updateLogLevels = [&]() { for (auto &category : categories) { - if (category.isDefault) + if (category.isDefault()) continue; const QString &key = category.id().name().toString(); - const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName; + const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName(); if (parser.isSet(key) || settings.isSet(settingsName)) { const QString value = parser.isSet(key) ? parser.value(key) : settings.value(settingsName).toString(); // Do not try to set the levels if it's due to a default config option. // This way we can tell which options have actually been overwritten by the user. - if (category.levelToString() == value && !parser.isSet(key)) + if (levelToString(category) == value && !parser.isSet(key)) continue; - if (!category.setLevel(value)) { + if (value == "disable"_L1) { + category.setLevel(QtCriticalMsg); + category.setIgnored(true); + } else if (value == "info"_L1) { + category.setLevel(QtInfoMsg); + category.setIgnored(false); + } else if (value == "warning"_L1) { + category.setLevel(QtWarningMsg); + category.setIgnored(false); + } else { qWarning() << "Invalid logging level" << value << "provided for" << category.id().name().toString() << "(allowed are: disable, info, warning)"; @@ -267,7 +293,7 @@ All warnings can be set to three levels: QQmlJSLinter linter(qmlImportPaths, pluginPaths, useAbsolutePath); for (const QQmlJSLinter::Plugin &plugin : linter.plugins()) { - for (const QQmlJSLogger::Category &category : plugin.categories()) + for (const QQmlJS::LoggerCategory &category : plugin.categories()) addCategory(category); } diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp index 65560c70e1..5d4bfcc4b7 100644 --- a/tools/qmltc/main.cpp +++ b/tools/qmltc/main.cpp @@ -31,7 +31,7 @@ using namespace Qt::StringLiterals; void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler { - for (const QQmlJSLogger::Category &category : logger.categories()) { + for (const QQmlJS::LoggerCategory &category : logger.categories()) { if (category == qmlUnusedImports) continue; logger.setCategoryLevel(category.id(), QtCriticalMsg); diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp index 6996277473..67ffcd6b0b 100644 --- a/tools/qmltc/qmltccodewriter.cpp +++ b/tools/qmltc/qmltccodewriter.cpp @@ -42,14 +42,14 @@ static QString getFunctionCategory(const QmltcMethod &method) { QString category = getFunctionCategory(static_cast<const QmltcMethodBase &>(method)); switch (method.type) { - case QQmlJSMetaMethod::Signal: + case QQmlJSMetaMethodType::Signal: category = u"Q_SIGNALS"_s; break; - case QQmlJSMetaMethod::Slot: + case QQmlJSMetaMethodType::Slot: category += u" Q_SLOTS"_s; break; - case QQmlJSMetaMethod::Method: - case QQmlJSMetaMethod::StaticMethod: + case QQmlJSMetaMethodType::Method: + case QQmlJSMetaMethodType::StaticMethod: break; } return category; @@ -396,14 +396,14 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method) { const auto [hSignature, cppSignature] = functionSignatures(method); // Note: augment return type with preambles in declaration - code.rawAppendToHeader((method.type == QQmlJSMetaMethod::StaticMethod + code.rawAppendToHeader((method.type == QQmlJSMetaMethodType::StaticMethod ? u"static " + functionReturnType(method) : functionReturnType(method)) + u" " + hSignature + u";"); // do not generate method implementation if it is a signal const auto methodType = method.type; - if (methodType != QQmlJSMetaMethod::Signal) { + if (methodType != QQmlJSMetaMethodType::Signal) { code.rawAppendToCpp(u""_s); // blank line if (method.comments.size() > 0) { code.rawAppendToCpp(u"/*! \\internal"_s); diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index 6d8fbd99b5..61205cef55 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -127,7 +127,7 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info) }; for (const auto &type : pureTypes) { - Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope); compiledTypes.emplaceBack(); // create empty type compileType(compiledTypes.back(), type, compile); } @@ -324,7 +324,7 @@ void QmltcCompiler::compileType( staticCreate.comments << u"Used by the engine for singleton creation."_s << u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s; - staticCreate.type = QQmlJSMetaMethod::StaticMethod; + staticCreate.type = QQmlJSMetaMethodType::StaticMethod; staticCreate.access = QQmlJSMetaMethod::Public; staticCreate.name = u"create"_s; staticCreate.returnType = u"%1 *"_s.arg(current.cppType); @@ -453,7 +453,7 @@ compileMethodParameters(const QList<QQmlJSMetaParameter> ¶meterInfos, bool a static QString figureReturnType(const QQmlJSMetaMethod &m) { const bool isVoidMethod = - m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethod::Signal; + m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethodType::Signal; Q_ASSERT(isVoidMethod || m.returnType()); QString type; if (isVoidMethod) { @@ -470,10 +470,10 @@ void QmltcCompiler::compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m, const auto returnType = figureReturnType(m); const QList<QmltcVariable> compiledParams = compileMethodParameters(m.parameters()); - const auto methodType = QQmlJSMetaMethod::Type(m.methodType()); + const auto methodType = m.methodType(); QStringList code; - if (methodType != QQmlJSMetaMethod::Signal) { + if (methodType != QQmlJSMetaMethodType::Signal) { QmltcCodeGenerator urlGenerator { m_url, m_visitor }; QmltcCodeGenerator::generate_callExecuteRuntimeFunction( &code, urlGenerator.urlMethodName() + u"()", @@ -488,7 +488,7 @@ void QmltcCompiler::compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m, compiled.body = std::move(code); compiled.type = methodType; compiled.access = m.access(); - if (methodType != QQmlJSMetaMethod::Signal) { + if (methodType != QQmlJSMetaMethodType::Signal) { compiled.declarationPrefixes << u"Q_INVOKABLE"_s; compiled.userVisible = m.access() == QQmlJSMetaMethod::Public; } else { @@ -1060,7 +1060,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor) { - Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Object); + Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object); const QString &propertyName = binding.propertyName(); const QQmlJSMetaProperty property = type->property(propertyName); @@ -1157,8 +1157,8 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor) { - Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::ValueSource - || binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor); + Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource + || binding.bindingType() == QQmlSA::BindingType::Interceptor); const QString &propertyName = binding.propertyName(); const QQmlJSMetaProperty property = type->property(propertyName); @@ -1166,7 +1166,7 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType ¤t, // NB: object is compiled with compileType(), here just need to use it QSharedPointer<const QQmlJSScope> object; - if (binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor) + if (binding.bindingType() == QQmlSA::BindingType::Interceptor) object = binding.interceptorType(); else object = binding.valueSourceType(); @@ -1215,7 +1215,7 @@ void QmltcCompiler::compileAttachedPropertyBinding(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor) { - Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::AttachedProperty); + Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty); const QString &propertyName = binding.propertyName(); const QQmlJSMetaProperty property = type->property(propertyName); @@ -1279,7 +1279,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor) { - Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty); + Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty); const QString &propertyName = binding.propertyName(); const QQmlJSMetaProperty property = type->property(propertyName); @@ -1336,7 +1336,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType ¤t, auto it = subbindings.begin(); Q_ASSERT(std::all_of(it, firstScript, [](const auto &x) { - return x.bindingType() != QQmlJSMetaPropertyBinding::Script; + return x.bindingType() != QQmlSA::BindingType::Script; })); compile(it, firstScript); it = firstScript; @@ -1357,7 +1357,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType ¤t, // once the value is written back, process the script bindings Q_ASSERT(std::all_of(it, subbindings.end(), [](const auto &x) { - return x.bindingType() == QQmlJSMetaPropertyBinding::Script; + return x.bindingType() == QQmlSA::BindingType::Script; })); compile(it, subbindings.end()); } @@ -1371,8 +1371,8 @@ void QmltcCompiler::compileTranslationBinding(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor) { - Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Translation - || binding.bindingType() == QQmlJSMetaPropertyBinding::TranslationById); + Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation + || binding.bindingType() == QQmlSA::BindingType::TranslationById); const QString &propertyName = binding.propertyName(); @@ -1449,7 +1449,7 @@ void QmltcCompiler::compileBinding(QmltcType ¤t, const auto location = binding.sourceLocation(); // make sure group property is not generalized by checking if type really has a property // called propertyName. If not, it is probably an id. - if (binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty + if (binding.bindingType() == QQmlSA::BindingType::GroupProperty && type->hasProperty(propertyName)) { qCWarning(lcQmltcCompiler) << QStringLiteral("Binding at line %1 column %2 is not deferred as it is a " @@ -1496,26 +1496,26 @@ void QmltcCompiler::compileBindingByType(QmltcType ¤t, accessor.name, constructFromQObject); }; switch (binding.bindingType()) { - case QQmlJSMetaPropertyBinding::BoolLiteral: { + case QQmlSA::BindingType::BoolLiteral: { const bool value = binding.boolValue(); assignToProperty(metaProperty, value ? u"true"_s : u"false"_s); break; } - case QQmlJSMetaPropertyBinding::NumberLiteral: { + case QQmlSA::BindingType::NumberLiteral: { assignToProperty(metaProperty, QString::number(binding.numberValue())); break; } - case QQmlJSMetaPropertyBinding::StringLiteral: { + case QQmlSA::BindingType::StringLiteral: { assignToProperty(metaProperty, QQmlJSUtils::toLiteral(binding.stringValue())); break; } - case QQmlJSMetaPropertyBinding::RegExpLiteral: { + case QQmlSA::BindingType::RegExpLiteral: { const QString value = u"QRegularExpression(%1)"_s.arg(QQmlJSUtils::toLiteral(binding.regExpValue())); assignToProperty(metaProperty, value); break; } - case QQmlJSMetaPropertyBinding::Null: { + case QQmlSA::BindingType::Null: { // poor check: null bindings are only supported for var and objects Q_ASSERT(propertyType->isSameType(m_typeResolver->varType()) || propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference); @@ -1525,38 +1525,38 @@ void QmltcCompiler::compileBindingByType(QmltcType ¤t, assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s); break; } - case QQmlJSMetaPropertyBinding::Script: { + case QQmlSA::BindingType::Script: { QString bindingSymbolName = type->internalName() + u'_' + propertyName + u"_binding"; bindingSymbolName.replace(u'.', u'_'); // can happen with group properties compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType, accessor); break; } - case QQmlJSMetaPropertyBinding::Object: { + case QQmlSA::BindingType::Object: { compileObjectBinding(current, binding, type, accessor); break; } - case QQmlJSMetaPropertyBinding::Interceptor: + case QQmlSA::BindingType::Interceptor: Q_FALLTHROUGH(); - case QQmlJSMetaPropertyBinding::ValueSource: { + case QQmlSA::BindingType::ValueSource: { compileValueSourceOrInterceptorBinding(current, binding, type, accessor); break; } - case QQmlJSMetaPropertyBinding::AttachedProperty: { + case QQmlSA::BindingType::AttachedProperty: { compileAttachedPropertyBinding(current, binding, type, accessor); break; } - case QQmlJSMetaPropertyBinding::GroupProperty: { + case QQmlSA::BindingType::GroupProperty: { compileGroupPropertyBinding(current, binding, type, accessor); break; } - case QQmlJSMetaPropertyBinding::TranslationById: - case QQmlJSMetaPropertyBinding::Translation: { + case QQmlSA::BindingType::TranslationById: + case QQmlSA::BindingType::Translation: { compileTranslationBinding(current, binding, type, accessor); break; } - case QQmlJSMetaPropertyBinding::Invalid: { + case QQmlSA::BindingType::Invalid: { recordError(binding.sourceLocation(), u"This binding is invalid"_s); break; } @@ -1652,10 +1652,10 @@ void QmltcCompiler::compileScriptBinding(QmltcType ¤t, Q_ASSERT(!objectClassName_signal.isEmpty()); Q_ASSERT(!objectClassName_slot.isEmpty()); - const auto signalMethods = objectType->methods(name, QQmlJSMetaMethod::Signal); + const auto signalMethods = objectType->methods(name, QQmlJSMetaMethodType::Signal); Q_ASSERT(!signalMethods.isEmpty()); // an error somewhere else QQmlJSMetaMethod signal = signalMethods.at(0); - Q_ASSERT(signal.methodType() == QQmlJSMetaMethod::Signal); + Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal); const QString signalName = signal.methodName(); const QString slotName = newSymbol(signalName + u"_slot"); @@ -1675,7 +1675,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType ¤t, objectType->ownRuntimeFunctionIndex(binding.scriptIndex()), u"this"_s, // Note: because script bindings always use current QML object scope signalReturnType, slotParameters); - slotMethod.type = QQmlJSMetaMethod::Slot; + slotMethod.type = QQmlJSMetaMethodType::Slot; current.functions << std::move(slotMethod); current.setComplexBindings.body << u"QObject::connect(" + This_signal + u", " + u"&" @@ -1684,7 +1684,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType ¤t, }; switch (binding.scriptKind()) { - case QQmlJSMetaPropertyBinding::Script_PropertyBinding: { + case QQmlSA::ScriptBindingKind::Script_PropertyBinding: { if (!propertyType) { recordError(binding.sourceLocation(), u"Binding on property '" + propertyName + u"' of unknown type"); @@ -1726,13 +1726,13 @@ void QmltcCompiler::compileScriptBinding(QmltcType ¤t, property, valueTypeIndex, accessor.name); break; } - case QQmlJSMetaPropertyBinding::Script_SignalHandler: { + case QQmlSA::ScriptBindingKind::Script_SignalHandler: { const auto name = QQmlJSUtils::signalName(propertyName); Q_ASSERT(name.has_value()); compileScriptSignal(*name); break; } - case QQmlJSMetaPropertyBinding::Script_ChangeHandler: { + case QQmlSA ::ScriptBindingKind::Script_ChangeHandler: { const QString objectClassName = objectType->internalName(); const QString bindingFunctorName = newSymbol(bindingSymbolName + u"Functor"); diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h index baa5b23256..3deab6d44e 100644 --- a/tools/qmltc/qmltccompiler.h +++ b/tools/qmltc/qmltccompiler.h @@ -50,7 +50,7 @@ public: static bool isComplexBinding(const QQmlJSMetaPropertyBinding &binding) { // TODO: translation bindings (once supported) are also complex? - return binding.bindingType() == QQmlJSMetaPropertyBinding::Script; + return binding.bindingType() == QQmlSA::BindingType::Script; } private: @@ -187,14 +187,14 @@ private: bool hasErrors() const { return m_logger->hasErrors(); } void recordError(const QQmlJS::SourceLocation &location, const QString &message, - LoggerWarningId id = qmlCompiler) + QQmlJS::LoggerWarningId id = qmlCompiler) { // pretty much any compiler error is a critical error (we cannot // generate code - compilation fails) m_logger->log(message, id, location); } void recordError(const QV4::CompiledData::Location &location, const QString &message, - LoggerWarningId id = qmlCompiler) + QQmlJS::LoggerWarningId id = qmlCompiler) { recordError(QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message, id); diff --git a/tools/qmltc/qmltccompilerpieces.cpp b/tools/qmltc/qmltccompilerpieces.cpp index 601cf1bbed..62b457337a 100644 --- a/tools/qmltc/qmltccompilerpieces.cpp +++ b/tools/qmltc/qmltccompilerpieces.cpp @@ -15,8 +15,8 @@ static QString scopeName(const QQmlJSScope::ConstPtr &scope) { Q_ASSERT(scope->isFullyResolved()); const auto scopeType = scope->scopeType(); - if (scopeType == QQmlJSScope::GroupedPropertyScope - || scopeType == QQmlJSScope::AttachedPropertyScope) { + if (scopeType == QQmlSA::ScopeType::GroupedPropertyScope + || scopeType == QQmlSA::ScopeType::AttachedPropertyScope) { return scope->baseType()->internalName(); } return scope->internalName(); diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h index cd094ee7b1..bed5a2037f 100644 --- a/tools/qmltc/qmltcoutputir.h +++ b/tools/qmltc/qmltcoutputir.h @@ -75,7 +75,7 @@ struct QmltcMethodBase struct QmltcMethod : QmltcMethodBase { QString returnType; // C++ return type - QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type + QQmlJSMetaMethodType type = QQmlJSMetaMethodType::Method; // Qt function type // TODO: should be a better way to handle this bool userVisible = false; // tells if a function is prioritized during the output generation diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index 236ad76467..3acee13bbb 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -133,8 +133,9 @@ void QmltcVisitor::findCppIncludes() Q_ASSERT(type); const auto scopeType = type->scopeType(); - if (scopeType != QQmlJSScope::QMLScope && scopeType != QQmlJSScope::GroupedPropertyScope - && scopeType != QQmlJSScope::AttachedPropertyScope) { + if (scopeType != QQmlSA::ScopeType::QMLScope + && scopeType != QQmlSA::ScopeType::GroupedPropertyScope + && scopeType != QQmlSA::ScopeType::AttachedPropertyScope) { continue; } @@ -174,7 +175,7 @@ void QmltcVisitor::findCppIncludes() static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope) { - Q_ASSERT(scope->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope); Q_ASSERT(!scope->isArrayScope()); Q_ASSERT(!scope->baseTypeName().isEmpty()); // the scope is guaranteed to be a new QML type, so any prefixes (separated @@ -197,7 +198,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object) } // we're not interested in non-QML scopes - if (m_currentScope->scopeType() != QQmlJSScope::QMLScope) + if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) return true; if (m_currentScope->isInlineComponent()) { @@ -216,7 +217,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object) void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *object) { - if (m_currentScope->scopeType() == QQmlJSScope::QMLScope) + if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope) m_qmlTypeNames.removeLast(); QQmlJSImportVisitor::endVisit(object); } @@ -280,7 +281,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember) u"internal error: %1 found for property '%2'"_s.arg(errorString, name), qmlCompiler, publicMember->identifierToken); return false; - } else if (methods[0].methodType() != QQmlJSMetaMethod::Signal) { + } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) { m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg( notifyName, name), qmlCompiler, publicMember->identifierToken); @@ -342,11 +343,11 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program) QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding) { switch (binding.bindingType()) { - case QQmlJSMetaPropertyBinding::Object: + case QQmlSA::BindingType::Object: return binding.objectType(); - case QQmlJSMetaPropertyBinding::Interceptor: + case QQmlSA::BindingType::Interceptor: return binding.interceptorType(); - case QQmlJSMetaPropertyBinding::ValueSource: + case QQmlSA::BindingType::ValueSource: return binding.valueSourceType(); // TODO: AttachedProperty and GroupProperty are not supported yet, // but have to also be acknowledged here @@ -435,7 +436,7 @@ void QmltcVisitor::postVisitResolve( // match scopes to indices of QmlIR::Object from QmlIR::Document qsizetype count = 0; const auto setIndex = [&](const QQmlJSScope::Ptr ¤t) { - if (current->scopeType() != QQmlJSScope::QMLScope || current->isArrayScope()) + if (current->scopeType() != QQmlSA::ScopeType::QMLScope || current->isArrayScope()) return; Q_ASSERT(!m_qmlIrObjectIndices.contains(current)); m_qmlIrObjectIndices[current] = count; @@ -547,9 +548,9 @@ void QmltcVisitor::postVisitResolve( if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope return; switch (scope->scopeType()) { - case QQmlJSScope::QMLScope: - case QQmlJSScope::GroupedPropertyScope: - case QQmlJSScope::AttachedPropertyScope: { + case QQmlSA::ScopeType::QMLScope: + case QQmlSA::ScopeType::GroupedPropertyScope: + case QQmlSA::ScopeType::AttachedPropertyScope: { ++qmlScopeCount[scope->enclosingInlineComponentName()]; break; } diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h index 0ec9349527..c7d2990848 100644 --- a/tools/qmltc/qmltcvisitor.h +++ b/tools/qmltc/qmltcvisitor.h @@ -57,7 +57,7 @@ public: qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const { - Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope); return m_creationIndices.value(type, -1); } @@ -68,13 +68,13 @@ public: qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const { - Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope); return m_syntheticTypeIndices.value(type, -1); } qsizetype qmlIrObjectIndex(const QQmlJSScope::ConstPtr &type) const { - Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope); Q_ASSERT(m_qmlIrObjectIndices.contains(type)); return m_qmlIrObjectIndices.value(type, -1); } |
