119 const QQmlSA::Element &,
const QQmlSA::Element &,
120 QQmlSA::SourceLocation location)
122 static constexpr std::array forbiddenAssignments = {
"baseline"_L1,
129 "horizontalCenter"_L1,
130 "horizontalCenterOffset"_L1,
142 "verticalCenterOffset"_L1,
147 Q_ASSERT(std::is_sorted(forbiddenAssignments.cbegin(), forbiddenAssignments.cend()));
148 if (std::find(forbiddenAssignments.cbegin(), forbiddenAssignments.cend(), propertyName)
149 != forbiddenAssignments.cend()) {
150 emitWarning(
"Imperative JavaScript assignments can break the visual tooling in Qt Design "
152 WarnImperativeCodeNotEditableInVisualDesigner, location);
157 const QString &propertyName,
158 const QQmlSA::Element &readScope,
159 QQmlSA::SourceLocation location)
163 const QQmlSA::Element globalJSObject = resolveBuiltinType(u"GlobalObject");
164 if (element != globalJSObject)
167 constexpr std::array translationFunctions = {
169 "QT_TRANSLATE_NOOP"_L1,
174 constexpr std::array idTranslationFunctions = {
179 const bool isTranslation =
180 std::find(translationFunctions.cbegin(), translationFunctions.cend(), propertyName)
181 != translationFunctions.cend();
182 const bool isIdTranslation =
183 std::find(idTranslationFunctions.cbegin(), idTranslationFunctions.cend(), propertyName)
184 != idTranslationFunctions.cend();
186 if (!isTranslation && !isIdTranslation)
189 const TranslationType current = isTranslation ? Normal : IdBased;
191 if (m_lastTranslationFunction == None) {
192 m_lastTranslationFunction = current;
196 if (m_lastTranslationFunction != current) {
197 emitWarning(
"Do not mix translation functions", qmlTranslationFunctionMismatch, location);
201void QmlLintQdsPlugin::registerPasses(PassManager *manager,
const Element &rootElement)
203 if (!rootElement.filePath().endsWith(u".ui.qml"))
206 manager->registerPropertyPass(std::make_shared<FunctionCallValidator>(manager),
207 QAnyStringView(), QAnyStringView());
208 manager->registerPropertyPass(std::make_shared<QdsBindingValidator>(manager, rootElement),
209 QAnyStringView(), QAnyStringView());
210 manager->registerPropertyPass(std::make_unique<QQmlJSTranslationFunctionMismatchCheck>(manager),
211 QString(), QString(), QString());
212 manager->registerElementPass(std::make_unique<QdsElementValidator>(manager));
216 const Element &readScope, SourceLocation location)
218 auto currentQmlScope = QQmlJSScope::findCurrentQMLScope(QQmlJSScope::scope(readScope));
221 if (currentQmlScope && currentQmlScope->inherits(QQmlJSScope::scope(m_connectionsType)))
225 const Element globalJSObject = resolveBuiltinType(u"GlobalObject");
226 const Element mathObjectType = globalJSObject.property(u"Math"_s).type();
227 if (element.inherits(mathObjectType))
230 const Element qjsValue = resolveBuiltinType(u"QJSValue");
231 if (element.inherits(qjsValue)) {
236 const std::array<QStringView, 4> dateMethodmethods{ u"now", u"parse", u"prototype",
238 if (
auto it = std::find(dateMethodmethods.cbegin(), dateMethodmethods.cend(), propertyName);
239 it != dateMethodmethods.cend())
243 static const std::vector<std::pair<Element, std::unordered_set<QString>>>
244 whiteListedFunctions = {
254 u"isNaN"_s, u"isFinite"_s,
255 u"qsTr"_s, u"qsTrId"_s, u"qsTranslate"_s,
256 u"QT_TRANSLATE_NOOP"_s, u"QT_TRID_NOOP"_s, u"QT_TR_NOOP"_s,
259 { resolveBuiltinType(u"ArrayPrototype"_s), { u"indexOf"_s, u"lastIndexOf"_s } },
260 { resolveBuiltinType(u"NumberPrototype"_s),
269 { resolveBuiltinType(u"StringPrototype"_s),
273 u"toLocaleLowerCase"_s,
275 u"toLocaleUpperCase"_s,
285 { resolveType(u"QtQml"_s, u"Qt"_s),
286 { u"lighter"_s, u"darker"_s, u"rgba"_s, u"tint"_s, u"hsla"_s, u"hsva"_s,
287 u"point"_s, u"rect"_s, u"size"_s, u"vector2d"_s, u"vector3d"_s, u"vector4d"_s,
288 u"quaternion"_s, u"matrix4x4"_s, u"formatDate"_s, u"formatDateTime"_s,
289 u"formatTime"_s, u"resolvedUrl"_s } },
292 for (
const auto &[currentElement, methods] : whiteListedFunctions) {
293 if ((!currentElement || element.inherits(currentElement)) && methods.count(propertyName)) {
299 emitWarning(u"Arbitrary functions and function calls outside of a Connections object are not "
300 u"supported in a UI file (.ui.qml)",
301 ErrFunctionsNotSupportedInQmlUi, location);
306 auto loadTypes = [&manager,
this](QSpan<
const UnsupportedName> names, QSpan<Element> output) {
307 for (qsizetype i = 0; i < qsizetype(names.size()); ++i) {
308 if (!manager->hasImportedModule(names[i].first))
310 output[i] = resolveType(names[i].first, names[i].second);
313 loadTypes(s_unsupportedElementNames, m_unsupportedElements);
314 loadTypes(s_unsupportedRootNames, m_unsupportedRootElements);
315 m_qtObject = resolveType(
"QtQml"_L1,
"QtObject"_L1);
316 m_supportFunctions = { resolveType(
"QtQml"_L1,
"Connections"_L1),
317 resolveType(
"QtQuick"_L1,
"ScriptAction"_L1) };
344 enum WarningType { ForElements, ForRootElements };
345 auto warnIfElementIsUnsupported = [
this, &element](WarningType warningType) {
346 QSpan<
const Element> unsupportedComponents = warningType == ForElements
347 ? QSpan<
const Element>(m_unsupportedElements)
348 : QSpan<
const Element>(m_unsupportedRootElements);
349 const QStringView message = warningType == ForElements
350 ? u"This type (%1) is not supported in a UI file (.ui.qml)."_sv
351 : u"This type (%1) is not supported as a root element of a UI file (.ui.qml)."_sv;
352 const LoggerWarningId &id = warningType == ForElements ? ErrUnsupportedTypeInQmlUi
353 : ErrUnsupportedRootTypeInQmlUi;
355 for (
const auto &unsupportedElement : unsupportedComponents) {
356 if (!element.inherits(unsupportedElement))
359 emitWarning(message.arg(element.baseTypeName()), id, element.sourceLocation());
365 if (warningType == ForRootElements && element.baseType() == m_qtObject)
366 emitWarning(message.arg(element.baseTypeName()), id, element.sourceLocation());
369 if (element.isFileRootComponent())
370 warnIfElementIsUnsupported(ForRootElements);
371 warnIfElementIsUnsupported(ForElements);
373 if (QString id = resolveElementToId(element, element); !id.isEmpty()) {
374 static constexpr std::array unsupportedNames = {
375 "action"_L1,
"alias"_L1,
"anchors"_L1,
"as"_L1,
"baseState"_L1,
376 "bool"_L1,
"border"_L1,
"bottom"_L1,
"break"_L1,
"case"_L1,
377 "catch"_L1,
"clip"_L1,
"color"_L1,
"continue"_L1,
"data"_L1,
378 "date"_L1,
"debugger"_L1,
"default"_L1,
"delete"_L1,
"do"_L1,
379 "double"_L1,
"else"_L1,
"enabled"_L1,
"enumeration"_L1,
"finally"_L1,
380 "flow"_L1,
"focus"_L1,
"font"_L1,
"for"_L1,
"function"_L1,
381 "height"_L1,
"id"_L1,
"if"_L1,
"import"_L1,
"in"_L1,
382 "instanceof"_L1,
"int"_L1,
"item"_L1,
"layer"_L1,
"left"_L1,
383 "list"_L1,
"margin"_L1,
"matrix4x4"_L1,
"new"_L1,
"opacity"_L1,
384 "padding"_L1,
"parent"_L1,
"point"_L1,
"print"_L1,
"quaternion"_L1,
385 "real"_L1,
"rect"_L1,
"return"_L1,
"right"_L1,
"scale"_L1,
386 "shaderInfo"_L1,
"size"_L1,
"source"_L1,
"sprite"_L1,
"spriteSequence"_L1,
387 "state"_L1,
"string"_L1,
"switch"_L1,
"text"_L1,
"texture"_L1,
388 "this"_L1,
"throw"_L1,
"time"_L1,
"top"_L1,
"try"_L1,
389 "typeof"_L1,
"url"_L1,
"var"_L1,
"variant"_L1,
"vector"_L1,
390 "vector2d"_L1,
"vector3d"_L1,
"vector4d"_L1,
"visible"_L1,
"void"_L1,
391 "while"_L1,
"width"_L1,
"with"_L1,
"x"_L1,
"y"_L1,
395 Q_ASSERT(std::is_sorted(unsupportedNames.begin(), unsupportedNames.end()));
396 if (std::binary_search(unsupportedNames.cbegin(), unsupportedNames.cend(), id)) {
398 u"This id (%1) might be ambiguous and is not supported in a UI file (.ui.qml)."_s
400 ErrInvalidIdeInVisualDesigner, element.idSourceLocation());
404 if (std::none_of(m_supportFunctions.cbegin(), m_supportFunctions.cend(),
405 [&element](
const Element &base) {
return element.inherits(base); })) {
406 complainAboutFunctions(element);
void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, QQmlSA::SourceLocation location) override
Executes whenever a property is written to.