QML Static Analysis 1 - Basic Setup

This chapter introduces the basic structure of a qmllint extension plugin, and how it can be used with qmllint.

To create our plugin, we first need to make the QmlCompiler module available:

find_package(Qt6 REQUIRED COMPONENTS QmlCompiler)

We then create a plugin, and link it against the QmlCompiler module.

qt_add_plugin(HelloWorldPlugin)

target_sources(HelloWorldPlugin
    PRIVATE
        helloplugin.h
        helloplugin.cpp
)

target_link_libraries(HelloWorldPlugin PRIVATE Qt::QmlCompiler)

The implementation follows the pattern for extending Qt with a plugin: We subclass the QQmlSA::LintPlugin,

class HelloWorldPlugin : public QObject, public QQmlSA::LintPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QmlLintPluginInterface_iid FILE "plugin.json")
    Q_INTERFACES(QQmlSA::LintPlugin)

public:
    void registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) override;
};

The plugin references a plugin.json file, which contains some important information:

{
    "name": "HelloWorld",
    "author": "Qt Example",
    "description": "Demonstrates how to write a qmllint plugin",
    "version": "1.0",
    "loggingCategories": [
        {
            "name": "hello-world",
            "settingsName": "HelloWorld",
            "description": "Used to create test messages"
        }
    ]
}

name, author, version and description are meta-data to describe the plugin.

Each plugin can have one or more logging categories, which are used to thematically group warnings. loggingCategories takes an array of categories, each consisting of

  • name, which is used to identify the category, and as the flag name in qmllint,
  • settingsName, which is used to configure the category in qmllint.ini
  • description, which should describe what kind of warning messages are tagged with the category

In our example, we have only one logging category, hello-world. For each category, we have to create a LoggerWarningId object in our plugin.

static constexpr QQmlSA::LoggerWarningId helloWorld { "Plugin.HelloWorld.hello-world" };

We construct it with a string literal which should have the format Plugin.<plugin name>.<category name>.

Lastly, for our plugin to actually do anything useful, we have to implement the registerPasses function.

void HelloWorldPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement)
{
    const bool pluginIsEnabled = manager->isCategoryEnabled(helloWorld);
    qDebug() << "Hello World plugin is" << (pluginIsEnabled ? "enabled" : "disabled");
    if (!pluginIsEnabled)
        return; // skip registration if the plugin is disabled anyway
    // here we will later register our passes
}

We check whether our category is enabled, and print a diagnostic indicating its status via qDebug. Note that the plugin currently does not do anything useful, as we do not register any passes. This will done in the next part of this tutorial.

We can however already verify that our plugin is correctly detected. We use the following command to check it:

qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml

The -P options tells qmllint to look for plugins in the specified folder. To avoid conflicts with Qt internal categories, plugin categories are always prefixed with "Plugin", then followed by a dot, the plugin name, another dot and finally the category. Running the command should print Hello World Plugin is enabled.

© 2024 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.