Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
tutorial.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2023 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
\page qqmlsa-tutorial.html
6
\title QML Static Analysis Tutorial
7
\brief An introduction on writing your own qmllint checks
8
\nextpage QML Static Analysis 1 - Basic Setup
9
10
This tutorial gives an introduction to QQmlSA, the API to statically analyze QML code.
11
12
Through the different steps of this tutorial we will learn about how to set up a basic qmllint
13
plugin, we will create our own custom analysis pass, and we will add support for fixit-hints.
14
15
Chapter one starts with a minimal plugin and the following chapters introduce new concepts.
16
17
The tutorial's source code is located in the \c{examples/qmlcompiler/tutorials/helloworld} directory.
18
19
Tutorial chapters:
20
21
\list 1
22
\li \l {QML Static Analysis 1 - Basic Setup}{Basic Setup}
23
\li \l {QML Static Analysis 2 - Custom Pass}{Custom Pass}
24
\li \l {QML Static Analysis 3 - Fixit Hints}{Fixit Hints}
25
\endlist
26
27
*/
28
29
/*!
30
\page qqmlsa-tutorial1.html
31
\title QML Static Analysis 1 - Basic Setup
32
\previouspage QML Static Analysis Tutorial
33
\nextpage QML Static Analysis 2 - Custom Pass
34
35
This chapter introduces the basic structure of a qmllint extension plugin,
36
and how it can be used with qmllint.
37
38
To create our plugin, we first need to make the QmlCompiler module available:
39
\quotefromfile tutorials/helloworld/chapter1/CMakeLists.txt
40
\skipto find_package
41
\printline find_package
42
43
We then create a plugin, and link it against the QmlCompiler module.
44
45
\skipto qt_add_plugin(HelloWorldPlugin)
46
\printuntil
47
48
The implementation follows the pattern for \l{The High-Level API: Writing Qt Extensions}{extending
49
Qt with a plugin}: We subclass the \l{QQmlSA::LintPlugin},
50
\quotefromfile tutorials/helloworld/chapter1/helloplugin.h
51
\skipto class HelloWorldPlugin
52
\printuntil };
53
54
The plugin references a \c{plugin.json} file, which contains some important information:
55
\quotefile tutorials/helloworld/chapter1/plugin.json
56
\c{name}, \c{author}, \c{version} and \c{description} are meta-data to describe the plugin.
57
58
Each plugin can have one or more logging categories, which are used to thematically group
59
warnings.
60
\c{loggingCategories} takes an array of categories, each consisting of
61
\list
62
\li \c{name}, which is used to identify the category, and as the flag name in qmllint,
63
\li \c{settingsName}, which is used to configure the category in qmllint.ini
64
\li \c{description}, which should describe what kind of warning messages are tagged with the category
65
\endlist
66
67
In our example, we have only one logging category, \c{hello-world}. For each category, we have to
68
create a \l{QQmlSA::}{LoggerWarningId} object in our plugin.
69
70
\quotefromfile tutorials/helloworld/chapter1/helloplugin.cpp
71
\skipto static constexpr QQmlSA::LoggerWarningId
72
\printline static constexpr QQmlSA::LoggerWarningId
73
74
We construct it with a string literal which should have the format
75
\c{Plugin.<plugin name>.<category name>}.
76
77
Lastly, for our plugin to actually do anything useful, we have to implement the
78
\c{registerPasses} function.
79
80
\skipto void HelloWorldPlugin::registerPasses(
81
\printuntil }
82
83
We check whether our category is enabled, and print a diagnostic indicating its status
84
via \l{qDebug}.
85
Note that the plugin currently does not do anything useful, as we do not register any
86
passes. This will done in the next part of this tutorial.
87
88
We can however already verify that our plugin is correctly detected. We use the following command to
89
check it:
90
91
\badcode
92
qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml
93
\endcode
94
95
The \c{-P} options tells qmllint to look for plugins in the specified folder. To avoid conflicts
96
with Qt internal categories, plugin categories are always prefixed with "Plugin", then followed by
97
a dot, the plugin name, another dot and finally the category.
98
Running the command should print \c{Hello World Plugin is enabled}.
99
100
*/
101
102
103
/*!
104
\page qqmlsa-tutorial2.html
105
\title QML Static Analysis 2 - Custom Pass
106
\previouspage QML Static Analysis 1 - Basic Setup
107
\nextpage QML Static Analysis 3 - Fixit Hints
108
109
This chapter shows how custom analysis passes can be added to \l{qmllint},
110
by extending the plugin we've created in the last chapter.
111
For demonstration purposes, we will create a plugin which checks whether
112
\l{Text} elements have "Hello world!" assigned to their text property.
113
114
To do this, we create a new class derived from
115
\l{QQmlSA::ElementPass}{ElementPass}.
116
117
\note There are two types of passes that
118
plugins can register, \l{QQmlSA::ElementPass}{ElementPasses}, and \l{QQmlSA::PropertyPass}{PropertyPasses}.
119
In this tutorial, we will only consider the simpler \c{ElementPass}.
120
121
\quotefromfile tutorials/helloworld/chapter2/helloplugin.cpp
122
\skipto class HelloWorldElementPass : public QQmlSA::ElementPass
123
\printuntil };
124
125
As our \c{HelloWorldElementPass} should analyze \c{Text} elements,
126
we need a reference to the \c{Text} type. We can use the
127
\l{QQmlSA::GenericPass::resolveType}{resolveType} function to obtain it.
128
As we don't want to constantly re-resolve the type, we do this
129
once in the constructor, and store the type in a member variable.
130
131
\skipto HelloWorldElementPass::HelloWorldElementPass(
132
\printuntil }
133
134
The actual logic of our pass happens in two functions:
135
\l{QQmlSA::ElementPass::shouldRun}{shouldRun} and
136
\l{QQmlSA::ElementPass::run}{run}. They will run on
137
all Elements in the file that gets analyzed by qmllint.
138
139
In our \c{shouldRun} method, we check whether the current
140
Element is derived from Text, and check whether it has
141
a binding on the text property.
142
\skipto bool HelloWorldElementPass::shouldRun
143
\printuntil }
144
145
Only elements passing the checks there will then be analyzed by our
146
pass via its \c{run} method. It would be possible to do all checking
147
inside of \c{run} itself, but it is generally preferable to have
148
a separation of concerns – both for performance and to enhance
149
code readability.
150
151
In our \c{run} function, we retrieve the bindings to the text
152
property. If the bound value is a string literal, we check
153
if it's the greeting we expect.
154
155
\skipto void HelloWorldElementPass::run
156
\printuntil /^}/
157
158
\note Most of the time, a property will only have one
159
binding assigned to it. However, there might be for
160
instance a literal binding and a \l{Behavior} assigned
161
to the same property.
162
163
Lastly, we need to create an instance of our pass, and
164
register it with the \l{QQmlSA::PassManager}{PassManager}. This is done by
165
adding
166
\skipto manager->registerElementPass
167
\printline manager->registerElementPass
168
to the \c{registerPasses} functions of our plugin.
169
170
We can test our plugin by invoking \l{qmllint} on an example
171
file via
172
\badcode
173
qmllint -P /path/to/the/directory/containing/the/plugin --Plugin.HelloWorld.hello-world info test.qml
174
\endcode
175
176
If \c{test.qml} looks like
177
\quotefromfile{tutorials/helloworld/chapter2/test.qml}
178
\skipto import
179
\printuntil
180
181
we will get
182
\badcode
183
Info: test.qml:22:26: Incorrect greeting [Plugin.HelloWorld.hello-world]
184
MyText { text: "Goodbye world!" }
185
^^^^^^^^^^^^^^^^
186
Info: test.qml:19:19: Incorrect greeting [Plugin.HelloWorld.hello-world]
187
Text { text: "Goodbye world!" }
188
\endcode
189
190
as the output. We can make a few observations here:
191
\list
192
\li The first \c{Text} does contain the expected greeting, so there's no warning
193
\li The second \c{Text} would at runtime have the wrong warning (\c{"Hello"}
194
instead of \c{"Hello world"}. However, this cannot be detected by qmllint
195
(in general), as there's no literal binding, but a binding to another property.
196
As we only check literal bindings, we simply skip over this binding.
197
\li For the literal binding in the third \c{Text} element, we correctly warn about
198
the wrong greeting.
199
\li As \c{NotText} does not derive from \c{Text}, the analysis will skip it, as
200
the \c{inherits} check will discard it.
201
\li The custom \c{MyText} element inherits from \c{Text}, and consequently we
202
see the expected warning.
203
\endlist
204
205
In summary, we've seen the steps necessary to extend \c{qmllint} with custom passes,
206
and have also become aware of the limitations of static checks.
207
208
*/
209
210
/*!
211
\page qqmlsa-tutorial3.html
212
\title QML Static Analysis 3 - Fixit Hints
213
\previouspage QML Static Analysis 2 - Custom Pass
214
215
In this chapter we learn how to improve our custom warnings by amending them
216
with fixit hints.
217
218
So far, we only created warning messages. However, sometimes we also want to
219
add a tip for the user how to fix the code. For that, we can pass an instance
220
of \c FixSuggestion to \l{QQmlSA::GenericPass::emitWarning}{emitWarning}.
221
A fix suggestion always consists of a description of what should be fixed, and
222
the location where it should apply. It can also feature a replacement text.
223
By default, the replacement text is only shown in the diagnostic message.
224
By calling \c{setAutoApplicable(true)} on the \c{FixSuggestion}, the user
225
can however apply the fix automatically via qmllint or the QML language
226
server.
227
It is important to only mark the suggestion as auto-applicable if the
228
resulting code is valid.
229
*/
qtdeclarative
src
qmlcompiler
doc
src
tutorial.qdoc
Generated on
for Qt by
1.14.0