1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "dumpastvisitor.h" |
30 | |
31 | #include <QtQml/private/qqmljslexer_p.h> |
32 | |
33 | DumpAstVisitor::(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *) |
34 | : m_engine(engine), m_comment(comment) |
35 | { |
36 | // Add all completely orphaned comments |
37 | m_result += getOrphanedComments(node: nullptr); |
38 | |
39 | m_scope_properties.push(t: ScopeProperties {}); |
40 | |
41 | rootNode->accept(visitor: this); |
42 | |
43 | // We need to get rid of one new-line so our output doesn't append an empty line |
44 | m_result.chop(n: 1); |
45 | |
46 | // Remove trailing whitespace |
47 | QStringList lines = m_result.split(sep: "\n" ); |
48 | for (QString& line : lines) { |
49 | while (line.endsWith(s: " " )) |
50 | line.chop(n: 1); |
51 | } |
52 | |
53 | m_result = lines.join(sep: "\n" ); |
54 | } |
55 | |
56 | bool DumpAstVisitor::preVisit(Node *el) |
57 | { |
58 | UiObjectMember *m = el->uiObjectMemberCast(); |
59 | if (m != 0) |
60 | Node::accept(node: m->annotations, visitor: this); |
61 | return true; |
62 | } |
63 | |
64 | static QString parseUiQualifiedId(UiQualifiedId *id) |
65 | { |
66 | QString name = id->name.toString(); |
67 | for (auto *item = id->next; item != nullptr; item = item->next) { |
68 | name += "." + item->name; |
69 | } |
70 | |
71 | return name; |
72 | } |
73 | |
74 | static QString operatorToString(int op) |
75 | { |
76 | switch (op) |
77 | { |
78 | case QSOperator::Add: return "+" ; |
79 | case QSOperator::And: return "&&" ; |
80 | case QSOperator::InplaceAnd: return "&=" ; |
81 | case QSOperator::Assign: return "=" ; |
82 | case QSOperator::BitAnd: return "&" ; |
83 | case QSOperator::BitOr: return "|" ; |
84 | case QSOperator::BitXor: return "^" ; |
85 | case QSOperator::InplaceSub: return "-=" ; |
86 | case QSOperator::Div: return "/" ; |
87 | case QSOperator::InplaceDiv: return "/=" ; |
88 | case QSOperator::Equal: return "==" ; |
89 | case QSOperator::Exp: return "**" ; |
90 | case QSOperator::InplaceExp: return "**=" ; |
91 | case QSOperator::Ge: return ">=" ; |
92 | case QSOperator::Gt: return ">" ; |
93 | case QSOperator::In: return "in" ; |
94 | case QSOperator::InplaceAdd: return "+=" ; |
95 | case QSOperator::InstanceOf: return "instanceof" ; |
96 | case QSOperator::Le: return "<=" ; |
97 | case QSOperator::LShift: return "<<" ; |
98 | case QSOperator::InplaceLeftShift: return "<<=" ; |
99 | case QSOperator::Lt: return "<" ; |
100 | case QSOperator::Mod: return "%" ; |
101 | case QSOperator::InplaceMod: return "%=" ; |
102 | case QSOperator::Mul: return "*" ; |
103 | case QSOperator::InplaceMul: return "*=" ; |
104 | case QSOperator::NotEqual: return "!=" ; |
105 | case QSOperator::Or: return "||" ; |
106 | case QSOperator::InplaceOr: return "|=" ; |
107 | case QSOperator::RShift: return ">>" ; |
108 | case QSOperator::InplaceRightShift: return ">>=" ; |
109 | case QSOperator::StrictEqual: return "===" ; |
110 | case QSOperator::StrictNotEqual: return "!==" ; |
111 | case QSOperator::Sub: return "-" ; |
112 | case QSOperator::URShift: return ">>>" ; |
113 | case QSOperator::InplaceURightShift: return ">>>=" ; |
114 | case QSOperator::InplaceXor: return "^=" ; |
115 | case QSOperator::As: return "as" ; |
116 | case QSOperator::Coalesce: return "??" ; |
117 | case QSOperator::Invalid: |
118 | default: |
119 | return "INVALID" ; |
120 | } |
121 | } |
122 | |
123 | QString DumpAstVisitor::(const Comment &) const |
124 | { |
125 | QString result; |
126 | |
127 | bool = comment.isMultiline() && !comment.isSyntheticMultiline(); |
128 | |
129 | if (useMultilineComment) |
130 | result += "/*" ; |
131 | else |
132 | result += "//" ; |
133 | |
134 | result += comment.m_text; |
135 | |
136 | if (comment.isSyntheticMultiline()) |
137 | result = result.replace(before: "\n" ,after: "\n" + formatLine(line: "//" , newline: false)); |
138 | |
139 | if (comment.m_location == Comment::Location::Back_Inline) |
140 | result.prepend(s: " " ); |
141 | |
142 | if (useMultilineComment) |
143 | result += "*/" ; |
144 | |
145 | return result; |
146 | } |
147 | |
148 | QString DumpAstVisitor::(Node *node, Comment::Location location) const |
149 | { |
150 | const auto& = m_comment->attachedComments(); |
151 | if (!comments.contains(akey: node)) |
152 | return "" ; |
153 | |
154 | auto = comments[node]; |
155 | |
156 | if (comment.m_location != location) |
157 | return "" ; |
158 | |
159 | return formatComment(comment); |
160 | } |
161 | |
162 | QString DumpAstVisitor::(SourceLocation srcLocation, |
163 | Comment::Location location) const { |
164 | const auto& = m_comment->listComments(); |
165 | |
166 | if (!comments.contains(akey: srcLocation.begin())) |
167 | return "" ; |
168 | |
169 | auto = comments[srcLocation.begin()]; |
170 | |
171 | if (comment.m_location != location) |
172 | return "" ; |
173 | |
174 | return formatComment(comment); |
175 | } |
176 | |
177 | QString DumpAstVisitor::(Node *node) const { |
178 | const auto& orphans = m_comment->orphanComments()[node]; |
179 | |
180 | if (orphans.size() == 0) |
181 | return "" ; |
182 | |
183 | QString result = "" ; |
184 | |
185 | for (const Comment& orphan : orphans) { |
186 | result += formatLine(line: formatComment(comment: orphan)); |
187 | } |
188 | |
189 | result += "\n" ; |
190 | |
191 | return result; |
192 | } |
193 | |
194 | QString DumpAstVisitor::parseArgumentList(ArgumentList *list) |
195 | { |
196 | QString result = "" ; |
197 | |
198 | for (auto *item = list; item != nullptr; item = item->next) |
199 | result += parseExpression(expression: item->expression) + (item->next != nullptr ? ", " : "" ); |
200 | |
201 | return result; |
202 | } |
203 | |
204 | QString DumpAstVisitor::parseUiParameterList(UiParameterList *list) { |
205 | QString result = "" ; |
206 | |
207 | for (auto *item = list; item != nullptr; item = item->next) |
208 | result += parseUiQualifiedId(id: item->type) + " " + item->name + (item->next != nullptr ? ", " : "" ); |
209 | |
210 | return result; |
211 | } |
212 | |
213 | QString DumpAstVisitor::parsePatternElement(PatternElement *element, bool scope) |
214 | { |
215 | switch (element->type) |
216 | { |
217 | case PatternElement::Literal: |
218 | return parseExpression(expression: element->initializer); |
219 | case PatternElement::Binding: { |
220 | QString result = "" ; |
221 | QString expr = parseExpression(expression: element->initializer); |
222 | |
223 | if (scope) { |
224 | switch (element->scope) { |
225 | case VariableScope::NoScope: |
226 | break; |
227 | case VariableScope::Let: |
228 | result = "let " ; |
229 | break; |
230 | case VariableScope::Const: |
231 | result = "const " ; |
232 | break; |
233 | case VariableScope::Var: |
234 | result = "var " ; |
235 | break; |
236 | } |
237 | } |
238 | |
239 | if (element->bindingIdentifier.isEmpty()) |
240 | result += parseExpression(expression: element->bindingTarget); |
241 | else |
242 | result += element->bindingIdentifier.toString(); |
243 | |
244 | if (element->typeAnnotation != nullptr) |
245 | result += ": " + parseType(type: element->typeAnnotation->type); |
246 | |
247 | if (!expr.isEmpty()) |
248 | result += " = " +expr; |
249 | |
250 | return result; |
251 | } |
252 | default: |
253 | m_error = true; |
254 | return "pe_unknown" ; |
255 | } |
256 | } |
257 | |
258 | QString escapeString(QString string) |
259 | { |
260 | // Handle escape sequences |
261 | string = string.replace(before: "\r" , after: "\\r" ).replace(before: "\n" , after: "\\n" ).replace(before: "\t" , after: "\\t" ) |
262 | .replace(before: "\b" ,after: "\\b" ).replace(before: "\v" , after: "\\v" ).replace(before: "\f" , after: "\\f" ); |
263 | |
264 | // Escape backslash |
265 | string = string.replace(before: "\\" , after: "\\\\" ); |
266 | |
267 | // Escape " |
268 | string = string.replace(before: "\"" , after: "\\\"" ); |
269 | |
270 | return "\"" + string + "\"" ; |
271 | } |
272 | |
273 | QString DumpAstVisitor::parsePatternElementList(PatternElementList *list) |
274 | { |
275 | QString result = "" ; |
276 | |
277 | for (auto *item = list; item != nullptr; item = item->next) |
278 | result += parsePatternElement(element: item->element) + (item->next != nullptr ? ", " : "" ); |
279 | |
280 | return result; |
281 | } |
282 | |
283 | QString DumpAstVisitor::parseFormalParameterList(FormalParameterList *list) |
284 | { |
285 | QString result = "" ; |
286 | |
287 | for (auto *item = list; item != nullptr; item = item->next) |
288 | result += parsePatternElement(element: item->element) + (item->next != nullptr ? ", " : "" ); |
289 | |
290 | return result; |
291 | } |
292 | |
293 | QString DumpAstVisitor::parsePatternProperty(PatternProperty *property) |
294 | { |
295 | switch (property->type) { |
296 | case PatternElement::Getter: |
297 | return "get " +parseFunctionExpression(expression: cast<FunctionExpression *>(ast: property->initializer), omitFunction: true); |
298 | case PatternElement::Setter: |
299 | return "set " +parseFunctionExpression(expression: cast<FunctionExpression *>(ast: property->initializer), omitFunction: true); |
300 | default: |
301 | if (property->name->kind == Node::Kind_ComputedPropertyName) { |
302 | return "[" +parseExpression(expression: cast<ComputedPropertyName *>(ast: property->name)->expression)+"]: " +parsePatternElement(element: property, scope: false); |
303 | } else { |
304 | return escapeString(string: property->name->asString())+": " +parsePatternElement(element: property, scope: false); |
305 | } |
306 | } |
307 | } |
308 | |
309 | QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list) |
310 | { |
311 | QString result = "" ; |
312 | |
313 | for (auto *item = list; item != nullptr; item = item->next) { |
314 | result += formatLine(line: parsePatternProperty(property: item->property) + (item->next != nullptr ? "," : "" )); |
315 | } |
316 | |
317 | return result; |
318 | } |
319 | |
320 | QString DumpAstVisitor::parseFunctionExpression(FunctionExpression *functExpr, bool omitFunction) |
321 | { |
322 | m_indentLevel++; |
323 | QString result; |
324 | |
325 | if (!functExpr->isArrowFunction) { |
326 | result += omitFunction ? "" : "function" ; |
327 | |
328 | if (functExpr->isGenerator) |
329 | result += "*" ; |
330 | |
331 | if (!functExpr->name.isEmpty()) |
332 | result += (omitFunction ? "" : " " ) + functExpr->name; |
333 | |
334 | result += "(" +parseFormalParameterList(list: functExpr->formals)+")" ; |
335 | |
336 | if (functExpr->typeAnnotation != nullptr) |
337 | result += " : " + parseType(type: functExpr->typeAnnotation->type); |
338 | |
339 | result += " {\n" + parseStatementList(list: functExpr->body); |
340 | } else { |
341 | result += "(" +parseFormalParameterList(list: functExpr->formals)+")" ; |
342 | |
343 | if (functExpr->typeAnnotation != nullptr) |
344 | result += " : " + parseType(type: functExpr->typeAnnotation->type); |
345 | |
346 | result += " => {\n" + parseStatementList(list: functExpr->body); |
347 | } |
348 | |
349 | m_indentLevel--; |
350 | |
351 | result += formatLine(line: "}" , newline: false); |
352 | |
353 | return result; |
354 | |
355 | } |
356 | |
357 | QString DumpAstVisitor::parseType(Type *type) { |
358 | QString result = parseUiQualifiedId(id: type->typeId); |
359 | |
360 | if (type->typeArguments != nullptr) { |
361 | TypeArgumentList *list = cast<TypeArgumentList *>(ast: type->typeArguments); |
362 | |
363 | result += "<" ; |
364 | |
365 | for (auto *item = list; item != nullptr; item = item->next) { |
366 | result += parseType(type: item->typeId) + (item->next != nullptr ? ", " : "" ); |
367 | } |
368 | |
369 | result += ">" ; |
370 | } |
371 | |
372 | return result; |
373 | } |
374 | |
375 | QString DumpAstVisitor::parseExpression(ExpressionNode *expression) |
376 | { |
377 | if (expression == nullptr) |
378 | return "" ; |
379 | |
380 | switch (expression->kind) |
381 | { |
382 | case Node::Kind_ArrayPattern: |
383 | return "[" +parsePatternElementList(list: cast<ArrayPattern *>(ast: expression)->elements)+"]" ; |
384 | case Node::Kind_IdentifierExpression: |
385 | return cast<IdentifierExpression*>(ast: expression)->name.toString(); |
386 | case Node::Kind_FieldMemberExpression: { |
387 | auto *fieldMemberExpr = cast<FieldMemberExpression *>(ast: expression); |
388 | QString result = parseExpression(expression: fieldMemberExpr->base); |
389 | |
390 | // If we're operating on a numeric literal, always put it in braces |
391 | if (fieldMemberExpr->base->kind == Node::Kind_NumericLiteral) |
392 | result = "(" + result + ")" ; |
393 | |
394 | result += "." + fieldMemberExpr->name.toString(); |
395 | |
396 | return result; |
397 | } |
398 | case Node::Kind_ArrayMemberExpression: { |
399 | auto *arrayMemberExpr = cast<ArrayMemberExpression *>(ast: expression); |
400 | return parseExpression(expression: arrayMemberExpr->base) |
401 | + "[" + parseExpression(expression: arrayMemberExpr->expression) + "]" ; |
402 | } |
403 | case Node::Kind_NestedExpression: |
404 | return "(" +parseExpression(expression: cast<NestedExpression *>(ast: expression)->expression)+")" ; |
405 | case Node::Kind_TrueLiteral: |
406 | return "true" ; |
407 | case Node::Kind_FalseLiteral: |
408 | return "false" ; |
409 | case Node::Kind_FunctionExpression: |
410 | { |
411 | auto *functExpr = cast<FunctionExpression *>(ast: expression); |
412 | return parseFunctionExpression(functExpr); |
413 | } |
414 | case Node::Kind_NullExpression: |
415 | return "null" ; |
416 | case Node::Kind_ThisExpression: |
417 | return "this" ; |
418 | case Node::Kind_PostIncrementExpression: |
419 | return parseExpression(expression: cast<PostIncrementExpression *>(ast: expression)->base)+"++" ; |
420 | case Node::Kind_PreIncrementExpression: |
421 | return "++" +parseExpression(expression: cast<PreIncrementExpression *>(ast: expression)->expression); |
422 | case Node::Kind_PostDecrementExpression: |
423 | return parseExpression(expression: cast<PostDecrementExpression *>(ast: expression)->base)+"--" ; |
424 | case Node::Kind_PreDecrementExpression: |
425 | return "--" +parseExpression(expression: cast<PreDecrementExpression *>(ast: expression)->expression); |
426 | case Node::Kind_NumericLiteral: |
427 | return QString::number(cast<NumericLiteral *>(ast: expression)->value); |
428 | case Node::Kind_TemplateLiteral: { |
429 | auto firstSrcLoc = cast<TemplateLiteral *>(ast: expression)->firstSourceLocation(); |
430 | auto lastSrcLoc = cast<TemplateLiteral *>(ast: expression)->lastSourceLocation(); |
431 | return m_engine->code().mid(position: static_cast<int>(firstSrcLoc.begin()), |
432 | n: static_cast<int>(lastSrcLoc.end() - firstSrcLoc.begin())); |
433 | } |
434 | case Node::Kind_StringLiteral: { |
435 | auto srcLoc = cast<StringLiteral *>(ast: expression)->firstSourceLocation(); |
436 | return m_engine->code().mid(position: static_cast<int>(srcLoc.begin()), |
437 | n: static_cast<int>(srcLoc.end() - srcLoc.begin())); |
438 | } |
439 | case Node::Kind_BinaryExpression: { |
440 | auto *binExpr = expression->binaryExpressionCast(); |
441 | return parseExpression(expression: binExpr->left) + " " + operatorToString(op: binExpr->op) |
442 | + " " + parseExpression(expression: binExpr->right); |
443 | } |
444 | case Node::Kind_CallExpression: { |
445 | auto *callExpr = cast<CallExpression *>(ast: expression); |
446 | |
447 | return parseExpression(expression: callExpr->base) + "(" + parseArgumentList(list: callExpr->arguments) + ")" ; |
448 | } |
449 | case Node::Kind_NewExpression: |
450 | return "new " +parseExpression(expression: cast<NewExpression *>(ast: expression)->expression); |
451 | case Node::Kind_NewMemberExpression: { |
452 | auto *newMemberExpression = cast<NewMemberExpression *>(ast: expression); |
453 | return "new " +parseExpression(expression: newMemberExpression->base) |
454 | + "(" +parseArgumentList(list: newMemberExpression->arguments)+")" ; |
455 | } |
456 | case Node::Kind_DeleteExpression: |
457 | return "delete " + parseExpression(expression: cast<DeleteExpression *>(ast: expression)->expression); |
458 | case Node::Kind_VoidExpression: |
459 | return "void " + parseExpression(expression: cast<VoidExpression *>(ast: expression)->expression); |
460 | case Node::Kind_TypeOfExpression: |
461 | return "typeof " + parseExpression(expression: cast<TypeOfExpression *>(ast: expression)->expression); |
462 | case Node::Kind_UnaryPlusExpression: |
463 | return "+" + parseExpression(expression: cast<UnaryPlusExpression *>(ast: expression)->expression); |
464 | case Node::Kind_UnaryMinusExpression: |
465 | return "-" + parseExpression(expression: cast<UnaryMinusExpression *>(ast: expression)->expression); |
466 | case Node::Kind_NotExpression: |
467 | return "!" + parseExpression(expression: cast<NotExpression *>(ast: expression)->expression); |
468 | case Node::Kind_TildeExpression: |
469 | return "~" + parseExpression(expression: cast<TildeExpression *>(ast: expression)->expression); |
470 | case Node::Kind_ConditionalExpression: { |
471 | auto *condExpr = cast<ConditionalExpression *>(ast: expression); |
472 | |
473 | QString result = "" ; |
474 | |
475 | result += parseExpression(expression: condExpr->expression) + " ? " ; |
476 | result += parseExpression(expression: condExpr->ok) + " : " ; |
477 | result += parseExpression(expression: condExpr->ko); |
478 | |
479 | return result; |
480 | } |
481 | case Node::Kind_YieldExpression: { |
482 | auto *yieldExpr = cast<YieldExpression*>(ast: expression); |
483 | |
484 | QString result = "yield" ; |
485 | |
486 | if (yieldExpr->isYieldStar) |
487 | result += "*" ; |
488 | |
489 | if (yieldExpr->expression) |
490 | result += " " + parseExpression(expression: yieldExpr->expression); |
491 | |
492 | return result; |
493 | } |
494 | case Node::Kind_ObjectPattern: { |
495 | auto *objectPattern = cast<ObjectPattern*>(ast: expression); |
496 | QString result = "{\n" ; |
497 | |
498 | m_indentLevel++; |
499 | result += parsePatternPropertyList(list: objectPattern->properties); |
500 | m_indentLevel--; |
501 | |
502 | result += formatLine(line: "}" , newline: false); |
503 | |
504 | return result; |
505 | } |
506 | case Node::Kind_Expression: { |
507 | auto* expr = cast<Expression*>(ast: expression); |
508 | return parseExpression(expression: expr->left)+", " +parseExpression(expression: expr->right); |
509 | } |
510 | case Node::Kind_Type: { |
511 | auto* type = reinterpret_cast<Type*>(expression); |
512 | return parseType(type); |
513 | } |
514 | case Node::Kind_RegExpLiteral: { |
515 | auto* regexpLiteral = cast<RegExpLiteral*>(ast: expression); |
516 | QString result = "/" +regexpLiteral->pattern+"/" ; |
517 | |
518 | if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Unicode) |
519 | result += "u" ; |
520 | if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Global) |
521 | result += "g" ; |
522 | if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Multiline) |
523 | result += "m" ; |
524 | if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Sticky) |
525 | result += "y" ; |
526 | if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_IgnoreCase) |
527 | result += "i" ; |
528 | |
529 | return result; |
530 | } |
531 | default: |
532 | m_error = true; |
533 | return "unknown_expression_" +QString::number(expression->kind); |
534 | } |
535 | } |
536 | |
537 | QString DumpAstVisitor::parseVariableDeclarationList(VariableDeclarationList *list) |
538 | { |
539 | QString result = "" ; |
540 | |
541 | for (auto *item = list; item != nullptr; item = item->next) { |
542 | result += parsePatternElement(element: item->declaration, scope: (item == list)) |
543 | + (item->next != nullptr ? ", " : "" ); |
544 | } |
545 | |
546 | return result; |
547 | } |
548 | |
549 | QString DumpAstVisitor::parseCaseBlock(CaseBlock *block) |
550 | { |
551 | QString result = "{\n" ; |
552 | |
553 | for (auto *item = block->clauses; item != nullptr; item = item->next) { |
554 | result += formatLine(line: "case " +parseExpression(expression: item->clause->expression)+":" ); |
555 | m_indentLevel++; |
556 | result += parseStatementList(list: item->clause->statements); |
557 | m_indentLevel--; |
558 | } |
559 | |
560 | if (block->defaultClause) { |
561 | result += formatLine(line: "default:" ); |
562 | m_indentLevel++; |
563 | result += parseStatementList(list: block->defaultClause->statements); |
564 | m_indentLevel--; |
565 | } |
566 | |
567 | result += formatLine(line: "}" , newline: false); |
568 | |
569 | return result; |
570 | } |
571 | |
572 | QString DumpAstVisitor::parseExportSpecifier(ExportSpecifier *specifier) |
573 | { |
574 | QString result = specifier->identifier.toString(); |
575 | |
576 | if (!specifier->exportedIdentifier.isEmpty()) |
577 | result += " as " + specifier->exportedIdentifier; |
578 | |
579 | return result; |
580 | } |
581 | |
582 | QString DumpAstVisitor::parseExportsList(ExportsList *list) |
583 | { |
584 | QString result = "" ; |
585 | |
586 | for (auto *item = list; item != nullptr; item = item->next) { |
587 | result += formatLine(line: parseExportSpecifier(specifier: item->exportSpecifier) |
588 | + (item->next != nullptr ? "," : "" )); |
589 | } |
590 | |
591 | return result; |
592 | } |
593 | |
594 | bool needsSemicolon(int kind) |
595 | { |
596 | switch (kind) { |
597 | case Node::Kind_ForStatement: |
598 | case Node::Kind_ForEachStatement: |
599 | case Node::Kind_IfStatement: |
600 | case Node::Kind_SwitchStatement: |
601 | case Node::Kind_WhileStatement: |
602 | case Node::Kind_DoWhileStatement: |
603 | case Node::Kind_TryStatement: |
604 | case Node::Kind_WithStatement: |
605 | return false; |
606 | default: |
607 | return true; |
608 | } |
609 | } |
610 | |
611 | QString DumpAstVisitor::parseBlock(Block *block, bool hasNext, bool allowBraceless) |
612 | { |
613 | bool hasOneLine = |
614 | (block->statements != nullptr && block->statements->next == nullptr) && allowBraceless; |
615 | |
616 | QString result = hasOneLine ? "\n" : "{\n" ; |
617 | m_indentLevel++; |
618 | result += parseStatementList(list: block->statements); |
619 | m_indentLevel--; |
620 | |
621 | if (hasNext) |
622 | result += formatLine(line: hasOneLine ? "" : "} " , newline: false); |
623 | |
624 | if (!hasNext && !hasOneLine) |
625 | result += formatLine(line: "}" , newline: false); |
626 | |
627 | if (block->statements) { |
628 | m_blockNeededBraces |= !needsSemicolon(kind: block->statements->statement->kind) |
629 | || (block->statements->next != nullptr); |
630 | } else { |
631 | m_blockNeededBraces = true; |
632 | } |
633 | |
634 | return result; |
635 | } |
636 | |
637 | QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext, |
638 | bool blockAllowBraceless) |
639 | { |
640 | if (statement == nullptr) |
641 | return "" ; |
642 | |
643 | switch (statement->kind) |
644 | { |
645 | case Node::Kind_EmptyStatement: |
646 | return "" ; |
647 | case Node::Kind_ExpressionStatement: |
648 | return parseExpression(expression: cast<ExpressionStatement *>(ast: statement)->expression); |
649 | case Node::Kind_VariableStatement: |
650 | return parseVariableDeclarationList(list: cast<VariableStatement *>(ast: statement)->declarations); |
651 | case Node::Kind_ReturnStatement: |
652 | return "return " +parseExpression(expression: cast<ReturnStatement *>(ast: statement)->expression); |
653 | case Node::Kind_ContinueStatement: |
654 | return "continue" ; |
655 | case Node::Kind_BreakStatement: |
656 | return "break" ; |
657 | case Node::Kind_SwitchStatement: { |
658 | auto *switchStatement = cast<SwitchStatement *>(ast: statement); |
659 | |
660 | QString result = "switch (" +parseExpression(expression: switchStatement->expression)+") " ; |
661 | |
662 | result += parseCaseBlock(block: switchStatement->block); |
663 | |
664 | return result; |
665 | } |
666 | case Node::Kind_IfStatement: { |
667 | auto *ifStatement = cast<IfStatement *>(ast: statement); |
668 | |
669 | m_blockNeededBraces = !blockAllowBraceless; |
670 | |
671 | QString ifFalse = parseStatement(statement: ifStatement->ko, blockHasNext: false, blockAllowBraceless: true); |
672 | QString ifTrue = parseStatement(statement: ifStatement->ok, blockHasNext: !ifFalse.isEmpty(), blockAllowBraceless: true); |
673 | |
674 | bool ifTrueBlock = ifStatement->ok->kind == Node::Kind_Block; |
675 | bool ifFalseBlock = ifStatement->ko |
676 | ? (ifStatement->ko->kind == Node::Kind_Block || ifStatement->ko->kind == Node::Kind_IfStatement) |
677 | : false; |
678 | |
679 | if (m_blockNeededBraces) { |
680 | ifFalse = parseStatement(statement: ifStatement->ko, blockHasNext: false, blockAllowBraceless: false); |
681 | ifTrue = parseStatement(statement: ifStatement->ok, blockHasNext: !ifFalse.isEmpty(), blockAllowBraceless: false); |
682 | } |
683 | |
684 | if (ifStatement->ok->kind != Node::Kind_Block) |
685 | ifTrue += ";" ; |
686 | |
687 | if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) |
688 | ifFalse += ";" ; |
689 | |
690 | QString result = "if (" + parseExpression(expression: ifStatement->expression) + ")" ; |
691 | |
692 | if (m_blockNeededBraces) { |
693 | if (ifStatement->ok->kind != Node::Kind_Block) { |
694 | QString result = "{\n" ; |
695 | m_indentLevel++; |
696 | result += formatLine(line: ifTrue); |
697 | m_indentLevel--; |
698 | result += formatLine(line: "} " , newline: false); |
699 | ifTrue = result; |
700 | ifTrueBlock = true; |
701 | } |
702 | |
703 | if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) { |
704 | QString result = "{\n" ; |
705 | m_indentLevel++; |
706 | result += formatLine(line: ifFalse); |
707 | m_indentLevel--; |
708 | result += formatLine(line: "} " , newline: false); |
709 | ifFalse = result; |
710 | ifFalseBlock = true; |
711 | } |
712 | } |
713 | |
714 | if (ifTrueBlock) { |
715 | result += " " + ifTrue; |
716 | } else { |
717 | result += "\n" ; |
718 | m_indentLevel++; |
719 | result += formatLine(line: ifTrue); |
720 | m_indentLevel--; |
721 | } |
722 | |
723 | if (!ifFalse.isEmpty()) |
724 | { |
725 | if (ifTrueBlock) |
726 | result += "else" ; |
727 | else |
728 | result += formatLine(line: "else" , newline: false); |
729 | |
730 | if (ifFalseBlock) { |
731 | // Blocks generate an extra newline that we don't want here. |
732 | if (!m_blockNeededBraces && ifFalse.endsWith(s: QLatin1String("\n" ))) |
733 | ifFalse.chop(n: 1); |
734 | |
735 | result += " " + ifFalse; |
736 | } else { |
737 | result += "\n" ; |
738 | m_indentLevel++; |
739 | result += formatLine(line: ifFalse, newline: false); |
740 | m_indentLevel--; |
741 | } |
742 | } |
743 | |
744 | return result; |
745 | } |
746 | case Node::Kind_ForStatement: { |
747 | auto *forStatement = cast<ForStatement *>(ast: statement); |
748 | |
749 | QString expr = parseExpression(expression: forStatement->expression); |
750 | QString result = "for (" ; |
751 | |
752 | result += parseVariableDeclarationList(list: forStatement->declarations); |
753 | |
754 | result += "; " ; |
755 | |
756 | result += parseExpression(expression: forStatement->condition) + "; " ; |
757 | result += parseExpression(expression: forStatement->expression)+")" ; |
758 | |
759 | const QString statement = parseStatement(statement: forStatement->statement); |
760 | |
761 | if (!statement.isEmpty()) |
762 | result += " " +statement; |
763 | else |
764 | result += ";" ; |
765 | |
766 | return result; |
767 | } |
768 | case Node::Kind_ForEachStatement: { |
769 | auto *forEachStatement = cast<ForEachStatement *>(ast: statement); |
770 | |
771 | QString result = "for (" ; |
772 | |
773 | PatternElement *patternElement = cast<PatternElement *>(ast: forEachStatement->lhs); |
774 | |
775 | if (patternElement != nullptr) |
776 | result += parsePatternElement(element: patternElement); |
777 | else |
778 | result += parseExpression(expression: forEachStatement->lhs->expressionCast()); |
779 | |
780 | switch (forEachStatement->type) |
781 | { |
782 | case ForEachType::In: |
783 | result += " in " ; |
784 | break; |
785 | case ForEachType::Of: |
786 | result += " of " ; |
787 | break; |
788 | } |
789 | |
790 | result += parseExpression(expression: forEachStatement->expression) + ")" ; |
791 | |
792 | const QString statement = parseStatement(statement: forEachStatement->statement); |
793 | |
794 | if (!statement.isEmpty()) |
795 | result += " " +statement; |
796 | else |
797 | result += ";" ; |
798 | |
799 | return result; |
800 | } |
801 | case Node::Kind_WhileStatement: { |
802 | auto *whileStatement = cast<WhileStatement *>(ast: statement); |
803 | |
804 | m_blockNeededBraces = false; |
805 | |
806 | auto statement = parseStatement(statement: whileStatement->statement, blockHasNext: false, blockAllowBraceless: true); |
807 | |
808 | QString result = "while (" +parseExpression(expression: whileStatement->expression) + ")" ; |
809 | |
810 | if (!statement.isEmpty()) |
811 | result += (m_blockNeededBraces ? " " : "" ) + statement; |
812 | else |
813 | result += ";" ; |
814 | |
815 | return result; |
816 | } |
817 | case Node::Kind_DoWhileStatement: { |
818 | auto *doWhileStatement = cast<DoWhileStatement *>(ast: statement); |
819 | return "do " + parseBlock(block: cast<Block *>(ast: doWhileStatement->statement), hasNext: true, allowBraceless: false) |
820 | + "while (" + parseExpression(expression: doWhileStatement->expression) + ")" ; |
821 | } |
822 | case Node::Kind_TryStatement: { |
823 | auto *tryStatement = cast<TryStatement *>(ast: statement); |
824 | |
825 | Catch *catchExpr = tryStatement->catchExpression; |
826 | Finally *finallyExpr = tryStatement->finallyExpression; |
827 | |
828 | QString result; |
829 | |
830 | result += "try " + parseBlock(block: cast<Block *>(ast: tryStatement->statement), hasNext: true, allowBraceless: false); |
831 | |
832 | result += "catch (" + parsePatternElement(element: catchExpr->patternElement, scope: false) + ") " |
833 | + parseBlock(block: cast<Block *>(ast: catchExpr->statement), hasNext: finallyExpr, allowBraceless: false); |
834 | |
835 | if (finallyExpr) { |
836 | result += "finally " + parseBlock(block: cast<Block *>(ast: tryStatement->statement), hasNext: false, allowBraceless: false); |
837 | } |
838 | |
839 | return result; |
840 | } |
841 | case Node::Kind_Block: { |
842 | return parseBlock(block: cast<Block *>(ast: statement), hasNext: blockHasNext, allowBraceless: blockAllowBraceless); |
843 | } |
844 | case Node::Kind_ThrowStatement: |
845 | return "throw " +parseExpression(expression: cast<ThrowStatement *>(ast: statement)->expression); |
846 | case Node::Kind_LabelledStatement: { |
847 | auto *labelledStatement = cast<LabelledStatement *>(ast: statement); |
848 | QString result = labelledStatement->label+":\n" ; |
849 | result += formatLine(line: parseStatement(statement: labelledStatement->statement), newline: false); |
850 | |
851 | return result; |
852 | } |
853 | case Node::Kind_WithStatement: { |
854 | auto *withStatement = cast<WithStatement *>(ast: statement); |
855 | return "with (" + parseExpression(expression: withStatement->expression) + ") " |
856 | + parseStatement(statement: withStatement->statement); |
857 | } |
858 | case Node::Kind_DebuggerStatement: { |
859 | return "debugger" ; |
860 | } |
861 | case Node::Kind_ExportDeclaration: |
862 | m_error = true; |
863 | return "export_decl_unsupported" ; |
864 | case Node::Kind_ImportDeclaration: |
865 | m_error = true; |
866 | return "import_decl_unsupported" ; |
867 | default: |
868 | m_error = true; |
869 | return "unknown_statement_" +QString::number(statement->kind); |
870 | } |
871 | } |
872 | |
873 | QString DumpAstVisitor::parseStatementList(StatementList *list) |
874 | { |
875 | QString result = "" ; |
876 | |
877 | if (list == nullptr) |
878 | return "" ; |
879 | |
880 | result += getOrphanedComments(node: list); |
881 | |
882 | for (auto *item = list; item != nullptr; item = item->next) { |
883 | QString statement = parseStatement(statement: item->statement->statementCast(), blockHasNext: false, blockAllowBraceless: true); |
884 | if (statement.isEmpty()) |
885 | continue; |
886 | |
887 | QString = getComment(node: item->statement, location: Comment::Location::Front); |
888 | QString = getComment(node: item->statement, location: Comment::Location::Back_Inline); |
889 | |
890 | if (!commentFront.isEmpty()) |
891 | result += formatLine(line: commentFront); |
892 | |
893 | result += formatLine(line: statement + (needsSemicolon(kind: item->statement->kind) ? ";" : "" ) |
894 | + commentBackInline); |
895 | } |
896 | |
897 | return result; |
898 | } |
899 | |
900 | bool DumpAstVisitor::visit(UiPublicMember *node) { |
901 | |
902 | QString = getComment(node, location: Comment::Location::Front); |
903 | QString = getComment(node, location: Comment::Location::Back_Inline); |
904 | |
905 | switch (node->type) |
906 | { |
907 | case UiPublicMember::Signal: |
908 | if (scope().m_firstSignal) { |
909 | if (scope().m_firstOfAll) |
910 | scope().m_firstOfAll = false; |
911 | else |
912 | addNewLine(); |
913 | |
914 | scope().m_firstSignal = false; |
915 | } |
916 | |
917 | addLine(line: commentFront); |
918 | addLine(line: "signal " +node->name.toString()+"(" +parseUiParameterList(list: node->parameters) + ")" |
919 | + commentBackInline); |
920 | break; |
921 | case UiPublicMember::Property: { |
922 | if (scope().m_firstProperty) { |
923 | if (scope().m_firstOfAll) |
924 | scope().m_firstOfAll = false; |
925 | else |
926 | addNewLine(); |
927 | |
928 | scope().m_firstProperty = false; |
929 | } |
930 | |
931 | const bool is_required = node->requiredToken.isValid(); |
932 | const bool is_default = node->defaultToken.isValid(); |
933 | const bool is_readonly = node->readonlyToken.isValid(); |
934 | const bool has_type_modifier = node->typeModifierToken.isValid(); |
935 | |
936 | QString prefix = "" ; |
937 | QString statement = parseStatement(statement: node->statement); |
938 | |
939 | if (!statement.isEmpty()) |
940 | statement.prepend(s: ": " ); |
941 | |
942 | if (is_required) |
943 | prefix += "required " ; |
944 | |
945 | if (is_default) |
946 | prefix += "default " ; |
947 | |
948 | if (is_readonly) |
949 | prefix += "readonly " ; |
950 | |
951 | QString member_type = parseUiQualifiedId(id: node->memberType); |
952 | |
953 | if (has_type_modifier) |
954 | member_type = node->typeModifier + "<" + member_type + ">" ; |
955 | |
956 | addLine(line: commentFront); |
957 | if (is_readonly && statement.isEmpty() |
958 | && scope().m_bindings.contains(akey: node->name.toString())) { |
959 | m_result += formatLine(line: prefix + "property " + member_type + " " , newline: false); |
960 | |
961 | scope().m_pendingBinding = true; |
962 | } else { |
963 | addLine(line: prefix + "property " + member_type + " " |
964 | + node->name+statement + commentBackInline); |
965 | } |
966 | break; |
967 | } |
968 | } |
969 | |
970 | return true; |
971 | } |
972 | |
973 | QString DumpAstVisitor::generateIndent() const { |
974 | constexpr int IDENT_WIDTH = 4; |
975 | |
976 | QString indent = "" ; |
977 | for (int i = 0; i < IDENT_WIDTH*m_indentLevel; i++) |
978 | indent += " " ; |
979 | |
980 | return indent; |
981 | } |
982 | |
983 | QString DumpAstVisitor::formatLine(QString line, bool newline) const { |
984 | QString result = generateIndent() + line; |
985 | if (newline) |
986 | result += "\n" ; |
987 | |
988 | return result; |
989 | } |
990 | |
991 | void DumpAstVisitor::addNewLine(bool always) { |
992 | if (!always && m_result.endsWith(s: "\n\n" )) |
993 | return; |
994 | |
995 | m_result += "\n" ; |
996 | } |
997 | |
998 | void DumpAstVisitor::addLine(QString line) { |
999 | // addLine does not support empty lines, use addNewLine(true) for that |
1000 | if (line.isEmpty()) |
1001 | return; |
1002 | |
1003 | m_result += formatLine(line); |
1004 | } |
1005 | |
1006 | QHash<QString, UiObjectMember*> findBindings(UiObjectMemberList *list) { |
1007 | QHash<QString, UiObjectMember*> bindings; |
1008 | |
1009 | // This relies on RestructureASTVisitor having run beforehand |
1010 | |
1011 | for (auto *item = list; item != nullptr; item = item->next) { |
1012 | switch (item->member->kind) { |
1013 | case Node::Kind_UiPublicMember: { |
1014 | UiPublicMember *member = cast<UiPublicMember *>(ast: item->member); |
1015 | |
1016 | if (member->type != UiPublicMember::Property) |
1017 | continue; |
1018 | |
1019 | bindings[member->name.toString()] = nullptr; |
1020 | |
1021 | break; |
1022 | } |
1023 | case Node::Kind_UiObjectBinding: { |
1024 | UiObjectBinding *binding = cast<UiObjectBinding *>(ast: item->member); |
1025 | |
1026 | const QString name = parseUiQualifiedId(id: binding->qualifiedId); |
1027 | |
1028 | if (bindings.contains(akey: name)) |
1029 | bindings[name] = binding; |
1030 | |
1031 | break; |
1032 | } |
1033 | case Node::Kind_UiArrayBinding: { |
1034 | UiArrayBinding *binding = cast<UiArrayBinding *>(ast: item->member); |
1035 | |
1036 | const QString name = parseUiQualifiedId(id: binding->qualifiedId); |
1037 | |
1038 | if (bindings.contains(akey: name)) |
1039 | bindings[name] = binding; |
1040 | |
1041 | break; |
1042 | } |
1043 | case Node::Kind_UiScriptBinding: |
1044 | // We can ignore UiScriptBindings since those are actually properly attached to the property |
1045 | break; |
1046 | } |
1047 | } |
1048 | |
1049 | return bindings; |
1050 | } |
1051 | |
1052 | bool DumpAstVisitor::visit(UiInlineComponent *node) |
1053 | { |
1054 | m_component_name = node->name.toString(); |
1055 | return true; |
1056 | } |
1057 | |
1058 | bool DumpAstVisitor::visit(UiObjectDefinition *node) { |
1059 | if (scope().m_firstObject) { |
1060 | if (scope().m_firstOfAll) |
1061 | scope().m_firstOfAll = false; |
1062 | else |
1063 | addNewLine(); |
1064 | |
1065 | scope().m_firstObject = false; |
1066 | } |
1067 | |
1068 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1069 | addLine(line: getComment(node, location: Comment::Location::Front_Inline)); |
1070 | |
1071 | QString component = "" ; |
1072 | |
1073 | if (!m_component_name.isEmpty()) { |
1074 | component = "component " +m_component_name+": " ; |
1075 | m_component_name = "" ; |
1076 | } |
1077 | |
1078 | addLine(line: component + parseUiQualifiedId(id: node->qualifiedTypeNameId) + " {" ); |
1079 | |
1080 | m_indentLevel++; |
1081 | |
1082 | ScopeProperties props; |
1083 | props.m_bindings = findBindings(list: node->initializer->members); |
1084 | m_scope_properties.push(t: props); |
1085 | |
1086 | m_result += getOrphanedComments(node); |
1087 | |
1088 | return true; |
1089 | } |
1090 | |
1091 | void DumpAstVisitor::endVisit(UiObjectDefinition *node) { |
1092 | m_indentLevel--; |
1093 | |
1094 | m_scope_properties.pop(); |
1095 | |
1096 | bool need_comma = scope().m_inArrayBinding && scope().m_lastInArrayBinding != node; |
1097 | |
1098 | addLine(line: need_comma ? "}," : "}" ); |
1099 | addLine(line: getComment(node, location: Comment::Location::Back)); |
1100 | if (!scope().m_inArrayBinding) |
1101 | addNewLine(); |
1102 | } |
1103 | |
1104 | bool DumpAstVisitor::visit(UiEnumDeclaration *node) { |
1105 | |
1106 | addNewLine(); |
1107 | |
1108 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1109 | addLine(line: "enum " + node->name + " {" ); |
1110 | m_indentLevel++; |
1111 | m_result += getOrphanedComments(node); |
1112 | |
1113 | return true; |
1114 | } |
1115 | |
1116 | void DumpAstVisitor::endVisit(UiEnumDeclaration *) { |
1117 | m_indentLevel--; |
1118 | addLine(line: "}" ); |
1119 | |
1120 | addNewLine(); |
1121 | } |
1122 | |
1123 | bool DumpAstVisitor::visit(UiEnumMemberList *node) { |
1124 | for (auto *members = node; members != nullptr; members = members->next) { |
1125 | |
1126 | addLine(line: getListItemComment(srcLocation: members->memberToken, location: Comment::Location::Front)); |
1127 | |
1128 | QString line = members->member.toString(); |
1129 | |
1130 | if (members->valueToken.isValid()) |
1131 | line += " = " +QString::number(members->value); |
1132 | |
1133 | if (members->next != nullptr) |
1134 | line += "," ; |
1135 | |
1136 | line += getListItemComment(srcLocation: members->memberToken, location: Comment::Location::Back_Inline); |
1137 | |
1138 | addLine(line); |
1139 | } |
1140 | |
1141 | return true; |
1142 | } |
1143 | |
1144 | bool DumpAstVisitor::visit(UiScriptBinding *node) { |
1145 | if (scope().m_firstBinding) { |
1146 | if (scope().m_firstOfAll) |
1147 | scope().m_firstOfAll = false; |
1148 | else |
1149 | addNewLine(); |
1150 | |
1151 | if (parseUiQualifiedId(id: node->qualifiedId) != "id" ) |
1152 | scope().m_firstBinding = false; |
1153 | } |
1154 | |
1155 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1156 | |
1157 | bool multiline = !needsSemicolon(kind: node->statement->kind); |
1158 | |
1159 | if (multiline) { |
1160 | m_indentLevel++; |
1161 | } |
1162 | |
1163 | QString statement = parseStatement(statement: node->statement); |
1164 | |
1165 | if (multiline) { |
1166 | statement = "{\n" + formatLine(line: statement); |
1167 | m_indentLevel--; |
1168 | statement += formatLine(line: "}" , newline: false); |
1169 | } |
1170 | |
1171 | QString result = parseUiQualifiedId(id: node->qualifiedId) + ":" ; |
1172 | |
1173 | if (!statement.isEmpty()) |
1174 | result += " " +statement; |
1175 | else |
1176 | result += ";" ; |
1177 | |
1178 | result += getComment(node, location: Comment::Location::Back_Inline); |
1179 | |
1180 | addLine(line: result); |
1181 | |
1182 | return true; |
1183 | } |
1184 | |
1185 | bool DumpAstVisitor::visit(UiArrayBinding *node) { |
1186 | if (!scope().m_pendingBinding && scope().m_firstBinding) { |
1187 | if (scope().m_firstOfAll) |
1188 | scope().m_firstOfAll = false; |
1189 | else |
1190 | addNewLine(); |
1191 | |
1192 | scope().m_firstBinding = false; |
1193 | } |
1194 | |
1195 | if (scope().m_pendingBinding) { |
1196 | m_result += parseUiQualifiedId(id: node->qualifiedId)+ ": [\n" ; |
1197 | scope().m_pendingBinding = false; |
1198 | } else { |
1199 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1200 | addLine(line: parseUiQualifiedId(id: node->qualifiedId)+ ": [" ); |
1201 | } |
1202 | |
1203 | m_indentLevel++; |
1204 | |
1205 | ScopeProperties props; |
1206 | props.m_inArrayBinding = true; |
1207 | |
1208 | for (auto *item = node->members; item != nullptr; item = item->next) { |
1209 | if (item->next == nullptr) |
1210 | props.m_lastInArrayBinding = item->member; |
1211 | } |
1212 | |
1213 | m_scope_properties.push(t: props); |
1214 | |
1215 | m_result += getOrphanedComments(node); |
1216 | |
1217 | return true; |
1218 | } |
1219 | |
1220 | void DumpAstVisitor::endVisit(UiArrayBinding *) { |
1221 | m_indentLevel--; |
1222 | m_scope_properties.pop(); |
1223 | addLine(line: "]" ); |
1224 | } |
1225 | |
1226 | bool DumpAstVisitor::visit(FunctionDeclaration *node) { |
1227 | if (scope().m_firstFunction) { |
1228 | if (scope().m_firstOfAll) |
1229 | scope().m_firstOfAll = false; |
1230 | else |
1231 | addNewLine(); |
1232 | |
1233 | scope().m_firstFunction = false; |
1234 | } |
1235 | |
1236 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1237 | |
1238 | QString head = "function" ; |
1239 | |
1240 | if (node->isGenerator) |
1241 | head += "*" ; |
1242 | |
1243 | head += " " +node->name+"(" +parseFormalParameterList(list: node->formals)+")" ; |
1244 | |
1245 | if (node->typeAnnotation != nullptr) |
1246 | head += " : " + parseType(type: node->typeAnnotation->type); |
1247 | |
1248 | head += " {" ; |
1249 | |
1250 | addLine(line: head); |
1251 | m_indentLevel++; |
1252 | |
1253 | return true; |
1254 | } |
1255 | |
1256 | void DumpAstVisitor::endVisit(FunctionDeclaration *node) |
1257 | { |
1258 | m_result += parseStatementList(list: node->body); |
1259 | m_indentLevel--; |
1260 | addLine(line: "}" ); |
1261 | addNewLine(); |
1262 | } |
1263 | |
1264 | bool DumpAstVisitor::visit(UiObjectBinding *node) { |
1265 | if (!scope().m_pendingBinding && scope().m_firstObject) { |
1266 | if (scope().m_firstOfAll) |
1267 | scope().m_firstOfAll = false; |
1268 | else |
1269 | addNewLine(); |
1270 | |
1271 | scope().m_firstObject = false; |
1272 | } |
1273 | |
1274 | QString name = parseUiQualifiedId(id: node->qualifiedTypeNameId); |
1275 | |
1276 | QString result = name; |
1277 | |
1278 | ScopeProperties props; |
1279 | props.m_bindings = findBindings(list: node->initializer->members); |
1280 | m_scope_properties.push(t: props); |
1281 | |
1282 | if (node->hasOnToken) |
1283 | result += " on " +parseUiQualifiedId(id: node->qualifiedId); |
1284 | else |
1285 | result.prepend(s: parseUiQualifiedId(id: node->qualifiedId) + ": " ); |
1286 | |
1287 | if (scope().m_pendingBinding) { |
1288 | m_result += result + " {\n" ; |
1289 | |
1290 | scope().m_pendingBinding = false; |
1291 | } else { |
1292 | addNewLine(); |
1293 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1294 | addLine(line: getComment(node, location: Comment::Location::Front_Inline)); |
1295 | addLine(line: result + " {" ); |
1296 | } |
1297 | |
1298 | m_indentLevel++; |
1299 | |
1300 | return true; |
1301 | } |
1302 | |
1303 | void DumpAstVisitor::endVisit(UiObjectBinding *node) { |
1304 | m_indentLevel--; |
1305 | m_scope_properties.pop(); |
1306 | |
1307 | addLine(line: "}" ); |
1308 | addLine(line: getComment(node, location: Comment::Location::Back)); |
1309 | |
1310 | addNewLine(); |
1311 | } |
1312 | |
1313 | bool DumpAstVisitor::visit(UiImport *node) { |
1314 | scope().m_firstOfAll = false; |
1315 | |
1316 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1317 | |
1318 | QString result = "import " ; |
1319 | |
1320 | if (!node->fileName.isEmpty()) |
1321 | result += escapeString(string: node->fileName.toString()); |
1322 | else |
1323 | result += parseUiQualifiedId(id: node->importUri); |
1324 | |
1325 | if (node->version) { |
1326 | result += " " + QString::number(node->version->majorVersion) + "." |
1327 | + QString::number(node->version->minorVersion); |
1328 | } |
1329 | |
1330 | if (node->asToken.isValid()) { |
1331 | result +=" as " + node->importId; |
1332 | } |
1333 | |
1334 | result += getComment(node, location: Comment::Location::Back_Inline); |
1335 | |
1336 | addLine(line: result); |
1337 | |
1338 | return true; |
1339 | } |
1340 | |
1341 | bool DumpAstVisitor::visit(UiPragma *node) { |
1342 | scope().m_firstOfAll = false; |
1343 | |
1344 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1345 | QString result = "pragma " + node->name; |
1346 | result += getComment(node, location: Comment::Location::Back_Inline); |
1347 | |
1348 | addLine(line: result); |
1349 | |
1350 | return true; |
1351 | } |
1352 | |
1353 | bool DumpAstVisitor::visit(UiAnnotation *node) |
1354 | { |
1355 | if (scope().m_firstObject) { |
1356 | if (scope().m_firstOfAll) |
1357 | scope().m_firstOfAll = false; |
1358 | else |
1359 | addNewLine(); |
1360 | |
1361 | scope().m_firstObject = false; |
1362 | } |
1363 | |
1364 | addLine(line: getComment(node, location: Comment::Location::Front)); |
1365 | addLine(line: QLatin1String("@" ) + parseUiQualifiedId(id: node->qualifiedTypeNameId) + " {" ); |
1366 | |
1367 | m_indentLevel++; |
1368 | |
1369 | ScopeProperties props; |
1370 | props.m_bindings = findBindings(list: node->initializer->members); |
1371 | m_scope_properties.push(t: props); |
1372 | |
1373 | m_result += getOrphanedComments(node); |
1374 | |
1375 | return true; |
1376 | } |
1377 | |
1378 | void DumpAstVisitor::endVisit(UiAnnotation *node) { |
1379 | m_indentLevel--; |
1380 | |
1381 | m_scope_properties.pop(); |
1382 | |
1383 | addLine(line: "}" ); |
1384 | addLine(line: getComment(node, location: Comment::Location::Back)); |
1385 | } |
1386 | |