QML 静的解析 2 - カスタムパス
この章では、前章で作成したプラグインを拡張して、qmllint にカスタム解析パスを追加する方法を示します。デモのために、Text 要素の text プロパティに "Hello world!" が代入されているかどうかをチェックするプラグインを作成します。
そのために、ElementPass から派生した新しいクラスを作成します。
注: プラグインが登録できるパスには、ElementPasses とPropertyPasses の2種類があります。このチュートリアルでは、より単純なElementPass
のみを考えます。
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; };
HelloWorldElementPass
はText
要素を分析するため、Text
型への参照が必要です。resolveType 関数を使えば、それを得ることができます。常に型を再解析したくないので、コンストラクタで一度だけこの処理を行い、メンバ変数に型を格納します。
HelloWorldElementPass::HelloWorldElementPass(QQmlSA::PassManager *manager) : QQmlSA::ElementPass(manager) { m_textType = resolveType("QtQuick", "Text"); }
パスの実際のロジックは、shouldRun とrun の2つの関数で行われます。これらの関数はqmllintによって解析されるファイル内のすべての要素に対して実行されます。
shouldRun
メソッドでは、現在の Element が Text から派生しているかどうかをチェックし、text プロパティにバインディングがあるかどうかをチェックします。
bool HelloWorldElementPass::shouldRun(const QQmlSA::Element &element) { if (!element.inherits(m_textType)) return false; if (!element.hasOwnPropertyBindings(u"text"_s)) return false; return true; }
このチェックに合格したエレメントだけがrun
メソッドで解析されます。すべてのチェックをrun
自体の内部で行うことも可能ですが、一般的には、パフォーマンスとコードの読みやすさの両方の観点から、関係性を分離することが望ましいとされています。
run
関数では、 text プロパティへのバインディングを取得します。バインドされた値が文字列リテラルであれば、それが期待する挨拶であるかどうかをチェックします。
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()); } }
注意: ほとんどの場合、プロパティには1つのバインディングしか割り当てられていません。しかし、同じプロパティにリテラルバインディングとBehavior 。
最後に、パスのインスタンスを作成し、PassManager に登録する必要があります。
manager->registerElementPass(std::make_unique<HelloWorldElementPass>(manager));
をプラグインのregisterPasses
関数に追加することで行います。
プラグインをテストするには、qmllintをサンプル・ファイルに対して次のように実行します。
qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml
もしtest.qml
が
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!" } } }
のようであれば
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!" }
が出力されます。ここでいくつか観察することができる:
- 最初の
Text
は期待された挨拶を含んでいるので、警告はありません。 - 番目の
Text
は実行時に間違った警告("Hello world"
ではなく"Hello"
)を出力します。 しかし、これは(一般的に)qmllintでは検出できません。なぜなら、リテラルバインディングではなく、別のプロパティへのバインディングだからです。私たちはリテラルバインディングだけをチェックするので、このバインディングは単純にスキップします。 - 3番目の
Text
要素のリテラルバインディングについては、間違った挨拶について正しく警告します。 NotText
はText
から派生していないので、inherits
のチェックで破棄されるように、解析はこれをスキップします。- カスタムの
MyText
要素はText
を継承しているため、期待通りの警告が表示されます。
まとめると、qmllint
をカスタムパスで拡張するために必要なステップを確認し、静的チェックの限界についても認識しました。
© 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.