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
33DumpAstVisitor::DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment)
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
56bool 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
64static 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
74static 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
123QString DumpAstVisitor::formatComment(const Comment &comment) const
124{
125 QString result;
126
127 bool useMultilineComment = 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
148QString DumpAstVisitor::getComment(Node *node, Comment::Location location) const
149{
150 const auto& comments = m_comment->attachedComments();
151 if (!comments.contains(akey: node))
152 return "";
153
154 auto comment = comments[node];
155
156 if (comment.m_location != location)
157 return "";
158
159 return formatComment(comment);
160}
161
162QString DumpAstVisitor::getListItemComment(SourceLocation srcLocation,
163 Comment::Location location) const {
164 const auto& comments = m_comment->listComments();
165
166 if (!comments.contains(akey: srcLocation.begin()))
167 return "";
168
169 auto comment = comments[srcLocation.begin()];
170
171 if (comment.m_location != location)
172 return "";
173
174 return formatComment(comment);
175}
176
177QString DumpAstVisitor::getOrphanedComments(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
194QString 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
204QString 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
213QString 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
258QString 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
273QString 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
283QString 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
293QString 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
309QString 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
320QString 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
357QString 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
375QString 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
537QString 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
549QString 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
572QString 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
582QString 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
594bool 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
611QString 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
637QString 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
873QString 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 commentFront = getComment(node: item->statement, location: Comment::Location::Front);
888 QString commentBackInline = 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
900bool DumpAstVisitor::visit(UiPublicMember *node) {
901
902 QString commentFront = getComment(node, location: Comment::Location::Front);
903 QString commentBackInline = 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
973QString 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
983QString DumpAstVisitor::formatLine(QString line, bool newline) const {
984 QString result = generateIndent() + line;
985 if (newline)
986 result += "\n";
987
988 return result;
989}
990
991void DumpAstVisitor::addNewLine(bool always) {
992 if (!always && m_result.endsWith(s: "\n\n"))
993 return;
994
995 m_result += "\n";
996}
997
998void 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
1006QHash<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
1052bool DumpAstVisitor::visit(UiInlineComponent *node)
1053{
1054 m_component_name = node->name.toString();
1055 return true;
1056}
1057
1058bool 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
1091void 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
1104bool 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
1116void DumpAstVisitor::endVisit(UiEnumDeclaration *) {
1117 m_indentLevel--;
1118 addLine(line: "}");
1119
1120 addNewLine();
1121}
1122
1123bool 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
1144bool 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
1185bool 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
1220void DumpAstVisitor::endVisit(UiArrayBinding *) {
1221 m_indentLevel--;
1222 m_scope_properties.pop();
1223 addLine(line: "]");
1224}
1225
1226bool 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
1256void DumpAstVisitor::endVisit(FunctionDeclaration *node)
1257{
1258 m_result += parseStatementList(list: node->body);
1259 m_indentLevel--;
1260 addLine(line: "}");
1261 addNewLine();
1262}
1263
1264bool 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
1303void 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
1313bool 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
1341bool 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
1353bool 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
1378void 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

source code of qtdeclarative/tools/qmlformat/dumpastvisitor.cpp