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.inidescription
, 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
.