QML Statische Analyse 2 - Benutzerdefinierter Durchlauf
In diesem Kapitel wird gezeigt, wie benutzerdefinierte Analysedurchläufe zu qmllint hinzugefügt werden können, indem das Plugin, das wir im letzten Kapitel erstellt haben, erweitert wird. Zu Demonstrationszwecken erstellen wir ein Plugin, das prüft, ob die Texteigenschaft von Text -Elementen mit "Hello world!" belegt ist.
Zu diesem Zweck erstellen wir eine neue Klasse, die von ElementPass abgeleitet ist.
Hinweis: Es gibt zwei Arten von Übergaben, die Plugins registrieren können, ElementPasses und PropertyPasses. In diesem Tutorial werden wir nur die einfachere ElementPass
betrachten.
class HelloWorldElementPass : public QQmlSA::ElementPass { public: HelloWorldElementPass(QQmlSA::PassManager *manager); bool shouldRun(const QQmlSA::Element &element) override; void run(const QQmlSA::Element &element) override; private: QQmlSA::Element m_textType; };
Da unser HelloWorldElementPass
die Elemente von Text
analysieren soll, benötigen wir einen Verweis auf den Typ Text
. Wir können die Funktion resolveType verwenden, um sie zu erhalten. Da wir den Typ nicht ständig neu auflösen wollen, tun wir dies einmal im Konstruktor und speichern den Typ in einer Mitgliedsvariablen.
HelloWorldElementPass::HelloWorldElementPass(QQmlSA::PassManager *manager) : QQmlSA::ElementPass(manager) { m_textType = resolveType("QtQuick", "Text"); }
Die eigentliche Logik unserer Übergabe findet in zwei Funktionen statt: shouldRun und run. Sie werden für alle Elemente in der Datei ausgeführt, die von qmllint analysiert wird.
In unserer Methode shouldRun
prüfen wir, ob das aktuelle Element von Text abgeleitet ist und ob es eine Bindung an die Eigenschaft text hat.
bool HelloWorldElementPass::shouldRun(const QQmlSA::Element &element) { if (!element.inherits(m_textType)) return false; if (!element.hasOwnPropertyBindings(u"text"_s)) return false; return true; }
Nur Elemente, die diese Prüfungen bestehen, werden dann von unserem Pass über seine run
-Methode analysiert. Es wäre möglich, alle Überprüfungen innerhalb von run
selbst durchzuführen, aber im Allgemeinen ist eine Trennung der Bereiche vorzuziehen - sowohl für die Leistung als auch für die bessere Lesbarkeit des Codes.
In unserer Funktion run
rufen wir die Bindungen an die Eigenschaft text ab. Wenn der gebundene Wert ein String-Literal ist, prüfen wir, ob es sich um die erwartete Ansage handelt.
void HelloWorldElementPass::run(const QQmlSA::Element &element) { auto textBindings = element.ownPropertyBindings(u"text"_s); for (const auto &textBinding: textBindings) { if (textBinding.bindingType() != QQmlSA::BindingType::StringLiteral) continue; if (textBinding.stringValue() != u"Hello world!"_s) emitWarning("Incorrect greeting", helloWorld, textBinding.sourceLocation()); } }
Hinweis: In den meisten Fällen wird einer Eigenschaft nur eine Bindung zugewiesen sein. Es kann jedoch vorkommen, dass der gleichen Eigenschaft eine Literalbindung und eine Behavior zugewiesen sind.
Schließlich müssen wir eine Instanz unseres Passes erstellen und sie bei PassManager registrieren. Dies geschieht durch Hinzufügen von
manager->registerElementPass(std::make_unique<HelloWorldElementPass>(manager));
zu den registerPasses
Funktionen unseres Plugins hinzufügen.
Wir können unser Plugin testen, indem wir qmllint für eine Beispieldatei aufrufen über
qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml
Wenn test.qml
aussieht wie
import QtQuick Item { id: root property string greeting: "Hello" component MyText : Text {} component NotText : Item { property string text } Text { text: "Hello world!" } Text { text: root.greeting } Text { text: "Goodbye world!" } NotText { text: "Does not trigger" MyText { text: "Goodbye world!" } } }
aussieht, erhalten wir
Info: test.qml:22:26: Incorrect greeting [Plugin.HelloWorld.hello-world] MyText { text: "Goodbye world!" } ^^^^^^^^^^^^^^^^ Info: test.qml:19:19: Incorrect greeting [Plugin.HelloWorld.hello-world] Text { text: "Goodbye world!" }
als Ausgabe. Wir können hier ein paar Beobachtungen machen:
- Die erste
Text
enthält die erwartete Ansage, also gibt es keine Warnung - Die zweite
Text
würde zur Laufzeit die falsche Warnung ausgeben ("Hello"
statt"Hello world"
. Dies kann jedoch von qmllint (im Allgemeinen) nicht erkannt werden, da es sich nicht um eine literale Bindung, sondern um eine Bindung an eine andere Eigenschaft handelt. Da wir nur wörtliche Bindungen prüfen, überspringen wir diese Bindung einfach. - Bei der literalen Bindung im dritten
Text
Element warnen wir korrekt vor der falschen Ansage. - Da
NotText
nicht vonText
abgeleitet ist, wird es bei der Analyse übersprungen, da die Prüfung voninherits
es verwirft. - Das benutzerdefinierte Element
MyText
erbt vonText
, und folglich wird die erwartete Warnung angezeigt.
Zusammenfassend lässt sich sagen, dass wir die Schritte gesehen haben, die notwendig sind, um qmllint
mit benutzerdefinierten Übergängen zu erweitern, und dass wir uns auch der Grenzen statischer Prüfungen bewusst geworden sind.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.