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
qqmldomscanner.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
7#include "qqmljsgrammar_p.h"
8
9#include <QtCore/QMetaEnum>
10
11#include <algorithm>
12
13QT_BEGIN_NAMESPACE
14
15using namespace QQmlJS::Dom;
16
17static void addLexToken(QList<Token> &tokens, int tokenKind, QQmlJS::Lexer &lexer,
18 bool &regexpMayFollow)
19{
20 switch (tokenKind) {
21 case QQmlJSGrammar::T_DIVIDE_:
22 case QQmlJSGrammar::T_DIVIDE_EQ:
23 if (lexer.state().currentChar.isSpace()) {
24 regexpMayFollow = false;
25 break;
26 }
27 if (regexpMayFollow) {
28 QQmlJS::Lexer::RegExpBodyPrefix prefix;
29 if (tokenKind == QQmlJSGrammar::T_DIVIDE_)
30 prefix = QQmlJS::Lexer::NoPrefix;
31 else
32 prefix = QQmlJS::Lexer::EqualPrefix;
33 if (lexer.scanRegExp(prefix)) {
34 regexpMayFollow = false;
35 break;
36 } else {
37 qCWarning(domLog) << "lexing error scannign regexp in" << lexer.code()
38 << lexer.errorCode() << lexer.errorMessage();
39 }
40 break;
41 } else if (tokenKind == QQmlJSGrammar::T_DIVIDE_) {
42 regexpMayFollow = true;
43 }
44 Q_FALLTHROUGH();
45
46 case QQmlJSGrammar::T_AND:
47 case QQmlJSGrammar::T_AND_AND:
48 case QQmlJSGrammar::T_AND_EQ:
49 case QQmlJSGrammar::T_ARROW:
50 case QQmlJSGrammar::T_EQ:
51 case QQmlJSGrammar::T_EQ_EQ:
52 case QQmlJSGrammar::T_EQ_EQ_EQ:
53 case QQmlJSGrammar::T_GE:
54 case QQmlJSGrammar::T_GT:
55 case QQmlJSGrammar::T_GT_GT:
56 case QQmlJSGrammar::T_GT_GT_EQ:
57 case QQmlJSGrammar::T_GT_GT_GT:
58 case QQmlJSGrammar::T_GT_GT_GT_EQ:
59 case QQmlJSGrammar::T_LE:
60 case QQmlJSGrammar::T_LT:
61 case QQmlJSGrammar::T_LT_LT:
62 case QQmlJSGrammar::T_LT_LT_EQ:
63 case QQmlJSGrammar::T_MINUS:
64 case QQmlJSGrammar::T_MINUS_EQ:
65 case QQmlJSGrammar::T_MINUS_MINUS:
66 case QQmlJSGrammar::T_NOT:
67 case QQmlJSGrammar::T_NOT_EQ:
68 case QQmlJSGrammar::T_NOT_EQ_EQ:
69 case QQmlJSGrammar::T_OR:
70 case QQmlJSGrammar::T_OR_EQ:
71 case QQmlJSGrammar::T_OR_OR:
72 case QQmlJSGrammar::T_PLUS:
73 case QQmlJSGrammar::T_PLUS_EQ:
74 case QQmlJSGrammar::T_PLUS_PLUS:
75 case QQmlJSGrammar::T_QUESTION:
76 case QQmlJSGrammar::T_QUESTION_DOT:
77 case QQmlJSGrammar::T_QUESTION_QUESTION:
78 case QQmlJSGrammar::T_REMAINDER:
79 case QQmlJSGrammar::T_REMAINDER_EQ:
80 case QQmlJSGrammar::T_STAR:
81 case QQmlJSGrammar::T_STAR_EQ:
82 case QQmlJSGrammar::T_STAR_STAR:
83 case QQmlJSGrammar::T_STAR_STAR_EQ:
84 case QQmlJSGrammar::T_TILDE:
85 case QQmlJSGrammar::T_XOR:
86 case QQmlJSGrammar::T_XOR_EQ:
87
88 case QQmlJSGrammar::T_AT:
89
90 case QQmlJSGrammar::T_AUTOMATIC_SEMICOLON:
91 case QQmlJSGrammar::T_COMPATIBILITY_SEMICOLON:
92 case QQmlJSGrammar::T_SEMICOLON:
93
94 case QQmlJSGrammar::T_COLON:
95 case QQmlJSGrammar::T_COMMA:
96 case QQmlJSGrammar::T_LBRACE:
97 case QQmlJSGrammar::T_LBRACKET:
98 case QQmlJSGrammar::T_LPAREN:
99
100 case QQmlJSGrammar::T_ELLIPSIS:
101 regexpMayFollow = true;
102 break;
103
104 case QQmlJSGrammar::T_FUNCTION:
105 // might contain a space at the end...
106 tokens.append(Token(lexer.tokenStartColumn() - 1,
107 lexer.tokenLength() - ((lexer.tokenText().endsWith(u' ')) ? 1 : 0),
108 tokenKind));
109 return;
110
111 case QQmlJSGrammar::T_DOT:
112 case QQmlJSGrammar::T_RBRACE:
113 case QQmlJSGrammar::T_RBRACKET:
114 case QQmlJSGrammar::T_RPAREN:
115 regexpMayFollow = false;
116 break;
117
118 // template used to expand to a string plus a delimiter for the ${ and }, now
119 // we use a + as delimiter
120 case QQmlJSGrammar::T_TEMPLATE_HEAD:
121 regexpMayFollow = true;
122 tokens.append(Token(lexer.tokenStartColumn() - 1, lexer.tokenLength() - 2, tokenKind));
123 tokens.append(Token(lexer.tokenStartColumn() + lexer.tokenLength() - 3, 2,
124 QQmlJSGrammar::T_PLUS));
125 return;
126 case QQmlJSGrammar::T_TEMPLATE_MIDDLE:
127 regexpMayFollow = true;
128 tokens.append(Token(lexer.tokenStartColumn() - 1, 1, QQmlJSGrammar::T_PLUS));
129 tokens.append(Token(lexer.tokenStartColumn(), lexer.tokenLength() - 3, tokenKind));
130 tokens.append(Token(lexer.tokenStartColumn() + lexer.tokenLength() - 3, 2,
131 QQmlJSGrammar::T_PLUS));
132 return;
133 case QQmlJSGrammar::T_TEMPLATE_TAIL:
134 regexpMayFollow = true;
135 tokens.append(Token(lexer.tokenStartColumn() - 1, 1, QQmlJSGrammar::T_PLUS));
136 tokens.append(Token(lexer.tokenStartColumn(), lexer.tokenLength() - 1, tokenKind));
137 return;
138 case QQmlJSGrammar::T_PARTIAL_TEMPLATE_MIDDLE:
139 regexpMayFollow = true;
140 tokens.append(Token(lexer.tokenStartColumn() - 1, 1, QQmlJSGrammar::T_PLUS));
141 tokens.append(Token(lexer.tokenStartColumn(), lexer.tokenLength() - 1, tokenKind));
142 return;
143 case QQmlJSGrammar::T_MULTILINE_STRING_LITERAL:
144 case QQmlJSGrammar::T_NO_SUBSTITUTION_TEMPLATE:
145 case QQmlJSGrammar::T_STRING_LITERAL:
146 case QQmlJSGrammar::T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL:
147 case QQmlJSGrammar::T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL:
148 case QQmlJSGrammar::T_PARTIAL_TEMPLATE_HEAD:
149 regexpMayFollow = (tokenKind == QQmlJSGrammar::T_TEMPLATE_MIDDLE
150 || tokenKind == QQmlJSGrammar::T_TEMPLATE_HEAD);
151 break;
152
153 case QQmlJSGrammar::T_VERSION_NUMBER:
154 if (lexer.state().currentChar == u'.') {
155 int offset = lexer.tokenStartColumn() - 1;
156 int length = lexer.tokenLength();
157 tokenKind = lexer.lex();
158 Q_ASSERT(tokenKind == QQmlJSGrammar::T_DOT);
159 tokenKind = lexer.lex();
160 Q_ASSERT(tokenKind == QQmlJSGrammar::T_VERSION_NUMBER);
161 length += 1 + lexer.tokenLength();
162 tokens.append(Token(offset, length, QQmlJSGrammar::T_NUMERIC_LITERAL));
163 return;
164 }
165 break;
166
167 default:
168 break;
169 }
170 // avoid newline (on multiline comments/strings)
171 qsizetype len = lexer.code().size();
172 if (lexer.code().endsWith(u'\n'))
173 --len;
174 len -= lexer.tokenStartColumn() - 1;
175 if (len < 0)
176 len = 0;
177 if (lexer.tokenLength() < len)
178 len = lexer.tokenLength();
179 tokens.append(Token(lexer.tokenStartColumn() - 1, len, tokenKind));
180}
181
182bool Token::lexKindIsDelimiter(int kind)
183{
184 switch (kind) {
185 case QQmlJSGrammar::T_AND:
186 case QQmlJSGrammar::T_AND_AND:
187 case QQmlJSGrammar::T_AND_EQ:
188 case QQmlJSGrammar::T_ARROW:
189 case QQmlJSGrammar::T_EQ:
190 case QQmlJSGrammar::T_EQ_EQ:
191 case QQmlJSGrammar::T_EQ_EQ_EQ:
192 case QQmlJSGrammar::T_GE:
193 case QQmlJSGrammar::T_GT:
194 case QQmlJSGrammar::T_GT_GT:
195 case QQmlJSGrammar::T_GT_GT_EQ:
196 case QQmlJSGrammar::T_GT_GT_GT:
197 case QQmlJSGrammar::T_GT_GT_GT_EQ:
198 case QQmlJSGrammar::T_LE:
199 case QQmlJSGrammar::T_LT:
200 case QQmlJSGrammar::T_LT_LT:
201 case QQmlJSGrammar::T_LT_LT_EQ:
202 case QQmlJSGrammar::T_MINUS:
203 case QQmlJSGrammar::T_MINUS_EQ:
204 case QQmlJSGrammar::T_NOT:
205 case QQmlJSGrammar::T_NOT_EQ:
206 case QQmlJSGrammar::T_NOT_EQ_EQ:
207 case QQmlJSGrammar::T_OR:
208 case QQmlJSGrammar::T_OR_EQ:
209 case QQmlJSGrammar::T_OR_OR:
210 case QQmlJSGrammar::T_PLUS:
211 case QQmlJSGrammar::T_PLUS_EQ:
212 case QQmlJSGrammar::T_QUESTION:
213 case QQmlJSGrammar::T_QUESTION_DOT:
214 case QQmlJSGrammar::T_QUESTION_QUESTION:
215 case QQmlJSGrammar::T_REMAINDER:
216 case QQmlJSGrammar::T_REMAINDER_EQ:
217 case QQmlJSGrammar::T_STAR:
218 case QQmlJSGrammar::T_STAR_EQ:
219 case QQmlJSGrammar::T_STAR_STAR:
220 case QQmlJSGrammar::T_STAR_STAR_EQ:
221 case QQmlJSGrammar::T_TILDE:
222 case QQmlJSGrammar::T_XOR:
223 case QQmlJSGrammar::T_XOR_EQ:
224 case QQmlJSGrammar::T_AT:
225 case QQmlJSGrammar::T_COMMA:
226 case QQmlJSGrammar::T_COLON:
227 case QQmlJSGrammar::T_LPAREN:
228 case QQmlJSGrammar::T_DIVIDE_:
229 case QQmlJSGrammar::T_DIVIDE_EQ:
230 return true;
231 default:
232 break;
233 }
234 return false;
235}
236
237bool Token::lexKindIsQmlReserved(int kind)
238{
239 switch (kind) {
240 case QQmlJSGrammar::T_AS:
241 case QQmlJSGrammar::T_IMPORT:
242 case QQmlJSGrammar::T_SIGNAL:
243 case QQmlJSGrammar::T_PROPERTY:
244 case QQmlJSGrammar::T_READONLY:
245 case QQmlJSGrammar::T_COMPONENT:
246 case QQmlJSGrammar::T_REQUIRED:
247 case QQmlJSGrammar::T_ON:
248 case QQmlJSGrammar::T_ENUM:
249 return true;
250 default:
251 break;
252 }
253 return false;
254}
255
256bool Token::lexKindIsComment(int kind)
257{
258 switch (kind) {
259 case QQmlJSGrammar::T_COMMENT:
260 case QQmlJSGrammar::T_PARTIAL_COMMENT:
261 return true;
262 default:
263 break;
264 }
265 return false;
266}
267
268bool Token::lexKindIsJSKeyword(int kind)
269{
270 switch (kind) {
271 case QQmlJSGrammar::T_BREAK:
272 case QQmlJSGrammar::T_CASE:
273 case QQmlJSGrammar::T_CATCH:
274 case QQmlJSGrammar::T_CLASS:
275 case QQmlJSGrammar::T_CONST:
276 case QQmlJSGrammar::T_CONTINUE:
277 case QQmlJSGrammar::T_DEBUGGER:
278 case QQmlJSGrammar::T_DEFAULT:
279 case QQmlJSGrammar::T_DELETE:
280 case QQmlJSGrammar::T_DO:
281 case QQmlJSGrammar::T_ELSE:
282 case QQmlJSGrammar::T_ENUM:
283 case QQmlJSGrammar::T_EXPORT:
284 case QQmlJSGrammar::T_EXTENDS:
285 case QQmlJSGrammar::T_FALSE:
286 case QQmlJSGrammar::T_FINALLY:
287 case QQmlJSGrammar::T_FOR:
288 case QQmlJSGrammar::T_FROM:
289 case QQmlJSGrammar::T_GET:
290 case QQmlJSGrammar::T_IF:
291 case QQmlJSGrammar::T_IN:
292 case QQmlJSGrammar::T_INSTANCEOF:
293 case QQmlJSGrammar::T_LET:
294 case QQmlJSGrammar::T_NEW:
295 case QQmlJSGrammar::T_RETURN:
296 case QQmlJSGrammar::T_SUPER:
297 case QQmlJSGrammar::T_SWITCH:
298 case QQmlJSGrammar::T_THEN:
299 case QQmlJSGrammar::T_THIS:
300 case QQmlJSGrammar::T_THROW:
301 case QQmlJSGrammar::T_VOID:
302 case QQmlJSGrammar::T_WHILE:
303 case QQmlJSGrammar::T_WITH:
304 case QQmlJSGrammar::T_YIELD:
305 case QQmlJSGrammar::T_VAR:
306 case QQmlJSGrammar::T_FUNCTION:
307 return true;
308 default:
309 break;
310 }
311 return false;
312}
313
314bool Token::lexKindIsIdentifier(int kind)
315{
316 switch (kind) {
317 case QQmlJSGrammar::T_IDENTIFIER:
318 case QQmlJSGrammar::T_COMPONENT:
319 case QQmlJSGrammar::T_REQUIRED:
320 case QQmlJSGrammar::T_AS:
321 case QQmlJSGrammar::T_PRAGMA:
322 case QQmlJSGrammar::T_IMPORT:
323 case QQmlJSGrammar::T_ENUM:
324 case QQmlJSGrammar::T_PACKAGE:
325 case QQmlJSGrammar::T_ABSTRACT:
326 case QQmlJSGrammar::T_INTERFACE:
327 case QQmlJSGrammar::T_IMPLEMENTS:
328 case QQmlJSGrammar::T_PUBLIC:
329 case QQmlJSGrammar::T_PROTECTED:
330 case QQmlJSGrammar::T_PRIVATE:
331 case QQmlJSGrammar::T_NATIVE:
332 case QQmlJSGrammar::T_VOLATILE:
333 case QQmlJSGrammar::T_TRANSIENT:
334 case QQmlJSGrammar::T_SYNCHRONIZED:
335 case QQmlJSGrammar::T_THROWS:
336 case QQmlJSGrammar::T_SET:
337 case QQmlJSGrammar::T_SIGNAL:
338 case QQmlJSGrammar::T_PROPERTY:
339 case QQmlJSGrammar::T_READONLY:
340 case QQmlJSGrammar::T_NULL:
341 case QQmlJSGrammar::T_OF:
342 case QQmlJSGrammar::T_ON:
343 case QQmlJSGrammar::T_STATIC:
344 case QQmlJSGrammar::T_TRUE:
345 case QQmlJSGrammar::T_TRY:
346 case QQmlJSGrammar::T_TYPEOF:
347 case QQmlJSGrammar::T_WITHOUTAS:
348 case QQmlJSGrammar::T_FROM:
349 return true;
350 default:
351 break;
352 }
353 return false;
354}
355
356bool Token::lexKindIsStringType(int kind)
357{
358 switch (kind) {
359 case QQmlJSGrammar::T_PARTIAL_TEMPLATE_MIDDLE:
360 case QQmlJSGrammar::T_MULTILINE_STRING_LITERAL:
361 case QQmlJSGrammar::T_NO_SUBSTITUTION_TEMPLATE:
362 case QQmlJSGrammar::T_STRING_LITERAL:
363 case QQmlJSGrammar::T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL:
364 case QQmlJSGrammar::T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL:
365 case QQmlJSGrammar::T_PARTIAL_TEMPLATE_HEAD:
366 return true;
367 default:
368 break;
369 }
370 return false;
371}
372
373bool Token::lexKindIsInvalid(int kind)
374{
375 switch (kind) {
376 case QQmlJSGrammar::T_NONE:
377 case QQmlJSGrammar::T_EOL:
378 case QQmlJSGrammar::EOF_SYMBOL:
379 case QQmlJSGrammar::T_ERROR:
380 case QQmlJSGrammar::T_FEED_JS_EXPRESSION:
381 case QQmlJSGrammar::T_FEED_JS_MODULE:
382 case QQmlJSGrammar::T_FEED_JS_SCRIPT:
383 case QQmlJSGrammar::T_FEED_JS_STATEMENT:
384 case QQmlJSGrammar::T_FEED_UI_OBJECT_MEMBER:
385 case QQmlJSGrammar::T_FEED_UI_PROGRAM:
386 case QQmlJSGrammar::REDUCE_HERE:
387 case QQmlJSGrammar::T_FORCE_BLOCK:
388 case QQmlJSGrammar::T_FORCE_DECLARATION:
389 case QQmlJSGrammar::T_FOR_LOOKAHEAD_OK:
390 return true;
391 default:
392 break;
393 }
394 return false;
395}
396
397void Token::dump(const Sink &s, QStringView line) const
398{
399 s(u"{");
400 sinkInt(s, offset);
401 s(u", ");
402 sinkInt(s, length);
403 s(u", Token::");
404 s(QString::number(lexKind));
405 s(u"}");
406 QStringView value = line.mid(offset, length);
407 if (!value.isEmpty()) {
408 s(u":");
409 sinkEscaped(s, value);
410 }
411}
412
413QList<Token> Scanner::operator()(QStringView text, const Scanner::State &startState)
414{
415 _state = startState;
416 QList<Token> tokens;
417
418 {
419 QQmlJS::Lexer lexer(nullptr, QQmlJS::Lexer::LexMode::LineByLine);
420 lexer.setState(startState.state);
421 QString line = text.toString();
422 if (!(line.endsWith(u"\n") || line.endsWith(u"\r")))
423 line += u'\n';
424 lexer.setCode(line, -1, _qmlMode, QQmlJS::Lexer::CodeContinuation::Continue);
425 while (true) {
426 int tokenKind = lexer.lex();
427 if (tokenKind == QQmlJSGrammar::T_EOL || tokenKind == QQmlJSGrammar::EOF_SYMBOL)
428 break;
429 addLexToken(tokens, tokenKind, lexer, _state.regexpMightFollow);
430 }
431 _state.state = lexer.state();
432 }
433 return tokens;
434}
435
436Scanner::State Scanner::state() const
437{
438 return _state;
439}
440
441bool Scanner::State::isMultiline() const
442{
443 switch (state.tokenKind) {
444 case QQmlJSGrammar::T_PARTIAL_COMMENT:
445 case QQmlJSGrammar::T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL:
446 case QQmlJSGrammar::T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL:
447 case QQmlJSGrammar::T_PARTIAL_TEMPLATE_HEAD:
448 case QQmlJSGrammar::T_PARTIAL_TEMPLATE_MIDDLE:
449 return true;
450 default:
451 break;
452 }
453 return false;
454}
455
456bool Scanner::State::isMultilineComment() const
457{
458 switch (state.tokenKind) {
459 case QQmlJSGrammar::T_PARTIAL_COMMENT:
460 return true;
461 default:
462 break;
463 }
464 return false;
465}
466
467QT_END_NAMESPACE
static void addLexToken(QList< Token > &tokens, int tokenKind, QQmlJS::Lexer &lexer, bool &regexpMayFollow)