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
qmlcodeparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6#include "node.h"
7#include "qmlvisitor.h"
8#include "utilities.h"
9
10#include <private/qqmljsast_p.h>
11
12#include <QtCore/qstringview.h>
13#include <qdebug.h>
14
15#include <optional>
16
17using namespace Qt::Literals::StringLiterals;
18
20
21/*!
22 Returns "QML".
23 */
24QString QmlCodeParser::language()
25{
26 return "QML";
27}
28
29/*!
30 Returns a string list containing "*.qml". This is the only
31 file type parsed by the QMLN parser.
32 */
34{
35 return QStringList() << "*.qml";
36}
37
38/*!
39 Parses the source file at \a filePath and inserts the contents
40 into the database. The \a location is used for error reporting.
41
42 If it can't open the file at \a filePath, it reports an error
43 and returns without doing anything.
44 */
45void QmlCodeParser::parseSourceFile(const Location &location, const QString &filePath, CppCodeParser&)
46{
47 static const QSet<QString> topic_commands{
52 };
53
54 QFile in(filePath);
55 if (!in.open(QIODevice::ReadOnly)) {
56 location.error(QStringLiteral("Cannot open QML file '%1'").arg(filePath));
57 return;
58 }
59
60 QString document = in.readAll();
61 in.close();
62
63 QString newCode = std::move(document);
64 const QStringList pragmas = extractPragmas(newCode);
65
66 QQmlJS::Engine engine{};
67 QQmlJS::Lexer lexer{&engine};
68 lexer.setCode(newCode, 1);
69
70 QQmlJS::Parser parser{&engine};
71
72 if (parser.parse()) {
73 QQmlJS::AST::UiProgram *ast = parser.ast();
74 QmlDocVisitor visitor(filePath, newCode, &engine, topic_commands + CodeParser::common_meta_commands,
75 topic_commands);
76 visitor.setSingletonPragmaFound(pragmas.contains("singleton"_L1));
77 QQmlJS::AST::Node::accept(ast, &visitor);
78 if (visitor.hasError())
79 Location(filePath).warning("Could not analyze QML file, output is incomplete.");
80 }
81 const auto &messages = parser.diagnosticMessages();
82 for (const auto &msg : messages) {
83 qCDebug(lcQdoc, "%s: %d: %d: QML syntax error: %s", qUtf8Printable(filePath),
84 msg.loc.startLine, msg.loc.startColumn, qUtf8Printable(msg.message));
85 }
86}
87
88/*!
89 Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
90 This function blanks out the section of the \a str beginning at \a idx
91 and running for \a n characters.
92*/
93void replaceWithSpace(QString &str, int idx, int n) // Also used in qmlcodemarker.cpp.
94{
95 QChar *data = str.data() + idx;
96 const QChar space(QLatin1Char(' '));
97 for (int ii = 0; ii < n; ++ii)
98 *data++ = space;
99}
100
101/*!
102 \struct PragmaInfo
103
104 \brief Container for the result of processing a pragma declaration.
105
106 \list
107 \li \c value contains the pragma value (e.g., "library", "singleton").
108 \li \c startOffset is the start position in script to replace.
109 \li \c length is the length of text to replace.
110 \li \c nextToken holds the next token after the pragma.
111 \list
112*/
119
120/*!
121 Processes a ".pragma <value>" declaration (JavaScript syntax).
122 Returns pragma information if successfully processed, std::nullopt otherwise.
123 Advances the lexer position but does not modify the script content.
124*/
125static std::optional<PragmaInfo> processDotPragma(QQmlJS::Lexer &lexer, const QStringView script)
126{
127 static constexpr QLatin1StringView pragma{"pragma"_L1};
128
129 qsizetype startOffset = lexer.tokenOffset();
130 qsizetype startLine = lexer.tokenStartLine();
131
132 int token = lexer.lex();
133
134 if (token != QQmlJSGrammar::T_IDENTIFIER || lexer.tokenStartLine() != startLine
135 || script.mid(lexer.tokenOffset(), lexer.tokenLength()) != pragma)
136 return std::nullopt;
137
138 token = lexer.lex();
139
140 if (token != QQmlJSGrammar::T_IDENTIFIER || lexer.tokenStartLine() != startLine)
141 return std::nullopt;
142
143 const QString pragmaValue{script.mid(lexer.tokenOffset(), lexer.tokenLength()).toString()};
144 const qsizetype endOffset = lexer.tokenLength() + lexer.tokenOffset();
145
146 token = lexer.lex();
147 if (lexer.tokenStartLine() == startLine)
148 return std::nullopt;
149
150 if (pragmaValue == "library"_L1) {
151 return PragmaInfo{
152 pragmaValue,
153 startOffset,
154 endOffset - startOffset,
155 token
156 };
157 }
158
159 return std::nullopt;
160}
161
162/*!
163 Processes a "pragma <value>" declaration (QML syntax).
164 Returns pragma information if successfully processed, std::nullopt otherwise.
165 Advances the lexer position but does not modify the script content.
166*/
167static std::optional<PragmaInfo> processPragma(QQmlJS::Lexer &lexer, const QStringView script)
168{
169 qsizetype startOffset = lexer.tokenOffset();
170 qsizetype startLine = lexer.tokenStartLine();
171
172 int token = lexer.lex();
173
174 if (token != QQmlJSGrammar::T_IDENTIFIER || lexer.tokenStartLine() != startLine)
175 return std::nullopt;
176
177 const QString pragmaValue{script.mid(lexer.tokenOffset(), lexer.tokenLength()).toString()};
178 qsizetype endOffset = lexer.tokenLength() + lexer.tokenOffset();
179
180 token = lexer.lex();
181 if (lexer.tokenStartLine() == startLine)
182 return std::nullopt;
183
184 if (pragmaValue == "library"_L1 || pragmaValue == "singleton"_L1) {
185 return PragmaInfo{
186 pragmaValue,
187 startOffset,
188 endOffset - startOffset,
189 token
190 };
191 }
192
193 return std::nullopt;
194}
195
196/*!
197 Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp,
198 then modified to return pragma information.
199
200 Searches for ".pragma <value>" (JavaScript) and "pragma <value>" (QML)
201 declarations within \a script.
202
203 Supported pragmas are: \c library, \c singleton.
204*/
206{
207 QStringList pragmas;
208 QQmlJS::Lexer lexer(nullptr);
209 lexer.setCode(script, 0);
210
211 int token = lexer.lex();
212
213 while (true) {
214 if (token == QQmlJSGrammar::T_DOT) {
215 auto pragmaInfo = processDotPragma(lexer, script);
216 if (!pragmaInfo)
217 return pragmas;
218
219 pragmas.append(pragmaInfo->value);
220 replaceWithSpace(script, pragmaInfo->startOffset, pragmaInfo->length);
221 token = pragmaInfo->nextToken;
222 } else if (token == QQmlJSGrammar::T_PRAGMA) {
223 auto pragmaInfo = processPragma(lexer, script);
224 if (!pragmaInfo)
225 return pragmas;
226
227 pragmas.append(pragmaInfo->value);
228 replaceWithSpace(script, pragmaInfo->startOffset, pragmaInfo->length);
229 token = pragmaInfo->nextToken;
230 } else {
231 return pragmas;
232 }
233 }
234}
235
236QT_END_NAMESPACE
The Location class provides a way to mark a location in a file.
Definition location.h:20
QStringList sourceFileNameFilter() override
Returns a string list containing "*.qml".
void parseSourceFile(const Location &location, const QString &filePath, CppCodeParser &) override
Parses the source file at filePath and inserts the contents into the database.
QStringList extractPragmas(QString &script) const
Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp, then modified to return pragma in...
bool hasError() const
#define COMMAND_QMLSIGNAL
Definition codeparser.h:66
#define COMMAND_QMLSINGLETONTYPE
Definition codeparser.h:68
#define COMMAND_QMLPROPERTYGROUP
Definition codeparser.h:63
#define COMMAND_QMLPROPERTY
Definition codeparser.h:62
#define COMMAND_QMLATTACHEDPROPERTY
Definition codeparser.h:50
#define COMMAND_VARIABLE
Definition codeparser.h:83
#define COMMAND_QMLBASICTYPE
Definition codeparser.h:90
#define COMMAND_QMLMETHOD
Definition codeparser.h:59
#define COMMAND_QMLCLASS
Definition codeparser.h:53
#define COMMAND_QMLVALUETYPE
Definition codeparser.h:52
#define COMMAND_QMLTYPE
Definition codeparser.h:67
#define COMMAND_QMLATTACHEDMETHOD
Definition codeparser.h:49
#define COMMAND_QMLATTACHEDSIGNAL
Definition codeparser.h:51
static std::optional< PragmaInfo > processPragma(QQmlJS::Lexer &lexer, const QStringView script)
Processes a "pragma <value>" declaration (QML syntax).
static std::optional< PragmaInfo > processDotPragma(QQmlJS::Lexer &lexer, const QStringView script)
Processes a ".pragma <value>" declaration (JavaScript syntax).
void replaceWithSpace(QString &str, int idx, int n)
Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
Container for the result of processing a pragma declaration.
qsizetype length
qsizetype startOffset