Revision control
++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qqmldomastdumper_p.h"
++ #include "qqmldomerrormessage_p.h"
++ #include <QtQml/private/qqmljsast_p.h>
++ #include <QtCore/QDebug>
++ #include <QtCore/QString>
++ #include <QtCore/QTextStream>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QQmlJS {
++ namespace Dom {
++ using namespace AST;
++ /*!
++ \internal
++ \enum QQmlJS::AstDumperOption
++
++ This enum type specifies the options for the AstDumper.
++ The values can be combined with the '|' operator, and checked using the '&' operator.
++
++ \value None
++ Default dumping options
++ \value NoLocations
++ Does not dump SourceLocations, allowing one to compare equivalent AST
++ generated by code formatted differently
++ \value NoAnnotations
++ Does not dump annotations
++ \value DumpNode
++ Does dump a <Node></Node> in preVisit/postVisit
++ */
++
++ /*!
++ \internal
++ \class QQmlJS::AstDumper
++ \brief Dumps or compares AST in an xml like format, mostly for testing/debugging
++
++ Initialize it with a lambda that dumps a string, and configure it with .setX methods.
++ If \l{indent} is set to a non zero value the xml is indented by that amount, and
++ \l{baseIndent} is the initial indent.
++ If \l{emitNode} is true the node tag is emitted in the preVisit/postVisit.
++ If \l{emitLocation} is true the SourceLocations are emitted.
++ If \l{emitAnnotations} is true annotations are emitted
++
++ The implementation has unnecessary roundtrips to QString, but it is supposed to be used
++ for debugging purposes...
++
++ Probably you will not use the visitor at all but rather the static method diff or
++ the qDebug() and ostream operator << that use the visitor...
++
++ \fn AstDumper::diff(AST::Node *n1, AST::Node *n2, int nContext, AstDumperOptions opt, int indent)
++
++ \brief compares the two AST::Node n1 and n2 and returns a string describing their first difference
++
++ If there are no differences the empty string is returned, so .isEmpty() can be use to check
++ for no differences.
++ \l{nContext} decides how much context is printed out.
++
++ */
++
++ // no export, just a supporting class...
++ class AstDumper: public AST::BaseVisitor
++ {
++ public:
++ AstDumper(const std::function<void(QStringView)> &dumper,
++ AstDumperOptions options = AstDumperOption::None, int indent = 1, int baseIndent = 0,
++ function_ref<QStringView(SourceLocation)> loc2str = &noStr)
++ : dumper(dumper), options(options), indent(indent), baseIndent(baseIndent), loc2str(loc2str)
++ {
++ }
++
++ private:
++ void start(QStringView str) {
++ dumper(QString::fromLatin1(" ").repeated(baseIndent));
++ dumper(u"<");
++ dumper(str);
++ dumper(u">\n");
++ baseIndent += indent;
++ }
++
++ void stop(QStringView str) {
++ baseIndent -= indent;
++ dumper(QString::fromLatin1(" ").repeated(baseIndent));
++ dumper(u"</");
++ dumper(str);
++ dumper(u">\n");
++ }
++
++ QString quotedString(const QString &s) {
++ QString res(s);
++ return QStringLiteral(u"\"") + res
++ .replace(QLatin1String("\\"), QLatin1String("\\\\"))
++ .replace(QLatin1String("\""), QLatin1String("\\\"")) + QLatin1String("\"");
++ }
++
++ QString quotedString(QStringView s) {
++ return quotedString(s.toString());
++ }
++
++ QString loc(const SourceLocation &s, bool trim = false) {
++ QString tokenStr;
++ if (s.length > 0)
++ tokenStr = loc2str(s).toString()
++ .replace(QLatin1String("\\"), QLatin1String("\\\\"))
++ .replace(QLatin1String("\""),QLatin1String("\\\""));
++ if (trim)
++ tokenStr = tokenStr.trimmed();
++ if (noLocations() || s == SourceLocation())
++ return QLatin1String("\"%1\"").arg(tokenStr);
++ else {
++ return QLatin1String("\"off:%1 len:%2 l:%3 c:%4 %5\"").arg(QString::number(s.offset), QString::number(s.length), QString::number(s.startLine), QString::number(s.startColumn), tokenStr);
++ }
++ }
++
++ QString semicolonToken(const SourceLocation &s)
++ {
++ if (options & AstDumperOption::SloppyCompare)
++ return QString();
++ return QLatin1String(" semicolonToken=") + loc(s);
++ }
++
++ QString boolStr(bool v) { return (v ? quotedString(u"true"): quotedString(u"false")); }
++ public:
++ bool preVisit(Node *) override { if (dumpNode()) start(u"Node"); return true; }
++ void postVisit(Node *) override { if (dumpNode()) stop(u"Node"); }
++
++ // Ui
++ bool visit(UiProgram *) override { start(u"UiProgram"); return true; }
++ void endVisit(AST::UiProgram *) override { stop(u"UiProgram"); }
++
++ bool visit(UiHeaderItemList *) override { start(u"UiHeaderItemList"); return true; }
++ void endVisit(AST::UiHeaderItemList *) override { stop(u"UiHeaderItemList"); }
++
++ #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
++ bool visit(UiPragmaValueList *el) override {
++ start(QLatin1String("UiPragmaValueList value=%1").arg(el->value));
++ return true;
++ }
++ void endVisit(AST::UiPragmaValueList *) override { stop(u"UiPragmaValueList"); }
++ #endif
++
++ bool visit(UiPragma *el) override {
++ start(QLatin1String("UiPragma name=%1 pragmaToken=%2%3")
++ .arg(quotedString(el->name), loc(el->pragmaToken),
++ semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::UiPragma *) override { stop(u"UiPragma"); }
++
++ bool visit(UiImport *el) override {
++ start(QLatin1String("UiImport fileName=%1 importId=%2 importToken=%3 fileNameToken=%4 "
++ "asToken=%5 importIdToken=%6%7")
++ .arg(quotedString(el->fileName), quotedString(el->importId),
++ loc(el->importToken), loc(el->fileNameToken), loc(el->asToken),
++ loc(el->importIdToken), semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::UiImport *el) override {
++ Node::accept(el->version, this);
++ stop(u"UiImport");
++ }
++
++ bool visit(UiPublicMember *el) override {
++ QString typeStr = ((el->type == UiPublicMember::Signal) ? QLatin1String("Signal") :
++ (el->type == UiPublicMember::Property) ? QLatin1String("Property") : QLatin1String("Unexpected(%1)").arg(QString::number(el->type)));
++ start(QLatin1String("UiPublicMember type=%1 typeModifier=%2 name=%3 isDefaultMember=%4 "
++ "isReadonlyMember=%5 isRequired=%6 "
++ "defaultToken=%7 readonlyToken=%8 propertyToken=%9 requiredToken=%10 "
++ "finalToken=%11 typeModifierToken=%12 typeToken=%13 "
++ "identifierToken=%14 colonToken=%15%16")
++ .arg(quotedString(typeStr), quotedString(el->typeModifier),
++ quotedString(el->name), boolStr(el->isDefaultMember()),
++ boolStr(el->isReadonly()), boolStr(el->isRequired()),
++ loc(el->defaultToken()), loc(el->readonlyToken()),
++ loc(el->propertyToken()), loc(el->requiredToken()),
++ loc(el->finalToken()), loc(el->typeModifierToken), loc(el->typeToken),
++ loc(el->identifierToken), loc(el->colonToken),
++ semicolonToken(el->semicolonToken)));
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ Node::accept(el->memberType, this);
++ return true;
++ }
++ void endVisit(AST::UiPublicMember *el) override {
++ Node::accept(el->parameters, this);
++ stop(u"UiPublicMember");
++ }
++
++ bool visit(AST::UiSourceElement *el) override {
++ start(u"UiSourceElement");
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiSourceElement *) override { stop(u"UiSourceElement"); }
++
++ bool visit(AST::UiObjectDefinition *el) override {
++ start(u"UiObjectDefinition");
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiObjectDefinition *) override { stop(u"UiObjectDefinition"); }
++
++ bool visit(AST::UiObjectInitializer *el) override {
++ start(QLatin1String("UiObjectInitializer lbraceToken=%1 rbraceToken=%2")
++ .arg(loc(el->lbraceToken), loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::UiObjectInitializer *) override { stop(u"UiObjectInitializer"); }
++
++ bool visit(AST::UiObjectBinding *el) override {
++ start(QLatin1String("UiObjectBinding colonToken=%1 hasOnToken=%2")
++ .arg(loc(el->colonToken), boolStr(el->hasOnToken)));
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiObjectBinding *) override { stop(u"UiObjectBinding"); }
++
++ bool visit(AST::UiScriptBinding *el) override {
++ start(QLatin1String("UiScriptBinding colonToken=%1")
++ .arg(loc(el->colonToken)));
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiScriptBinding *) override { stop(u"UiScriptBinding"); }
++
++ bool visit(AST::UiArrayBinding *el) override {
++ start(QLatin1String("UiArrayBinding colonToken=%1 lbracketToken=%2 rbracketToken=%3")
++ .arg(loc(el->colonToken), loc(el->lbracketToken), loc(el->rbracketToken)));
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiArrayBinding *) override { stop(u"UiArrayBinding"); }
++
++ bool visit(AST::UiParameterList *el) override {
++ start(QLatin1String("UiParameterList name=%1 commaToken=%2 propertyTypeToken=%3 identifierToken=%4 colonToken=%5")
++ .arg(quotedString(el->name), loc(el->commaToken), loc(el->propertyTypeToken), loc(el->identifierToken), loc(el->colonToken)));
++ Node::accept(el->type, this);
++ return true;
++ }
++ void endVisit(AST::UiParameterList *el) override {
++ stop(u"UiParameterList");
++ Node::accept(el->next, this); // put other args at the same level as this one...
++ }
++
++ bool visit(AST::UiObjectMemberList *) override { start(u"UiObjectMemberList"); return true; }
++ void endVisit(AST::UiObjectMemberList *) override { stop(u"UiObjectMemberList"); }
++
++ bool visit(AST::UiArrayMemberList *el) override {
++ start(QLatin1String("UiArrayMemberList commaToken=%1")
++ .arg(loc(el->commaToken)));
++ return true;
++ }
++ void endVisit(AST::UiArrayMemberList *) override { stop(u"UiArrayMemberList"); }
++
++ bool visit(AST::UiQualifiedId *el) override {
++ start(QLatin1String("UiQualifiedId name=%1 identifierToken=%2")
++ .arg(quotedString(el->name), loc(el->identifierToken)));
++ Node::accept(el->next, this);
++ return true;
++ }
++ void endVisit(AST::UiQualifiedId *) override { stop(u"UiQualifiedId"); }
++
++ bool visit(AST::UiEnumDeclaration *el) override {
++ start(QLatin1String("UiEnumDeclaration enumToken=%1 rbraceToken=%2 name=%3")
++ .arg(loc(el->enumToken), loc(el->rbraceToken), quotedString(el->name)));
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiEnumDeclaration *) override { stop(u"UiEnumDeclaration"); }
++
++ bool visit(AST::UiEnumMemberList *el) override {
++ start(QLatin1String("UiEnumMemberList member=%1 value=%2 memberToken=%3 valueToken=%4")
++ .arg(quotedString(el->member), quotedString(QString::number(el->value)), loc(el->memberToken), loc(el->valueToken)));
++ return true;
++ }
++ void endVisit(AST::UiEnumMemberList *el) override {
++ stop(u"UiEnumMemberList");
++ Node::accept(el->next, this); // put other enum members at the same level as this one...
++ }
++
++ bool visit(AST::UiVersionSpecifier *el) override {
++ start(QLatin1String("UiVersionSpecifier majorVersion=%1 minorVersion=%2 majorToken=%3 minorToken=%4")
++ .arg(quotedString(QString::number(el->version.majorVersion())),
++ quotedString(QString::number(el->version.minorVersion())),
++ loc(el->majorToken), loc(el->minorToken)));
++ return true;
++ }
++ void endVisit(AST::UiVersionSpecifier *) override { stop(u"UiVersionSpecifier"); }
++
++ bool visit(AST::UiInlineComponent *el) override {
++ start(QLatin1String("UiInlineComponent name=%1 componentToken=%2")
++ .arg(quotedString(el->name), loc(el->componentToken)));
++ if (!noAnnotations()) // put annotations inside the node they refer to
++ Node::accept(el->annotations, this);
++ return true;
++ }
++ void endVisit(AST::UiInlineComponent *) override { stop(u"UiInlineComponent"); }
++
++ bool visit(UiRequired *el) override {
++ start(QLatin1String("UiRequired name=%1 requiredToken=%2%3")
++ .arg(quotedString(el->name), loc(el->requiredToken),
++ semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(UiRequired *) override { stop(u"UiRequired"); }
++
++ bool visit(UiAnnotation *) override {
++ start(u"UiAnnotation");
++ return true;
++ }
++ void endVisit(UiAnnotation *) override { stop(u"UiAnnotation"); }
++
++ bool visit(UiAnnotationList *) override {
++ start(u"UiAnnotationList");
++ return true;
++ }
++ void endVisit(UiAnnotationList *) override { stop(u"UiAnnotationList"); }
++
++ // QQmlJS
++ bool visit(AST::TypeExpression *) override {
++ start(u"TypeExpression");
++ return true;
++ }
++ void endVisit(AST::TypeExpression *) override { stop(u"TypeExpression"); }
++
++ bool visit(AST::ThisExpression *el) override {
++ start(QLatin1String("ThisExpression thisToken=%1")
++ .arg(loc(el->thisToken)));
++ return true;
++ }
++ void endVisit(AST::ThisExpression *) override { stop(u"ThisExpression"); }
++
++ bool visit(AST::IdentifierExpression *el) override {
++ start(QLatin1String("IdentifierExpression name=%1 identifierToken=%2")
++ .arg(quotedString(el->name), loc(el->identifierToken)));
++ return true;
++ }
++ void endVisit(AST::IdentifierExpression *) override { stop(u"IdentifierExpression"); }
++
++ bool visit(AST::NullExpression *el) override {
++ start(QLatin1String("NullExpression nullToken=%1")
++ .arg(loc(el->nullToken)));
++ return true;
++ }
++ void endVisit(AST::NullExpression *) override { stop(u"NullExpression"); }
++
++ bool visit(AST::TrueLiteral *el) override {
++ start(QLatin1String("TrueLiteral trueToken=%1")
++ .arg(loc(el->trueToken)));
++ return true;
++ }
++ void endVisit(AST::TrueLiteral *) override { stop(u"TrueLiteral"); }
++
++ bool visit(AST::FalseLiteral *el) override {
++ start(QLatin1String("FalseLiteral falseToken=%1")
++ .arg(loc(el->falseToken)));
++ return true;
++ }
++ void endVisit(AST::FalseLiteral *) override { stop(u"FalseLiteral"); }
++
++ bool visit(AST::SuperLiteral *el) override {
++ start(QLatin1String("SuperLiteral superToken=%1")
++ .arg(loc(el->superToken)));
++ return true;
++ }
++ void endVisit(AST::SuperLiteral *) override { stop(u"SuperLiteral"); }
++
++ bool visit(AST::StringLiteral *el) override {
++ start(QLatin1String("StringLiteral value=%1 literalToken=%2")
++ .arg(quotedString(el->value), loc(el->literalToken)));
++ return true;
++ }
++ void endVisit(AST::StringLiteral *) override { stop(u"StringLiteral"); }
++
++ bool visit(AST::TemplateLiteral *el) override {
++ start(QLatin1String("TemplateLiteral value=%1 rawValue=%2 literalToken=%3")
++ .arg(quotedString(el->value), quotedString(el->rawValue), loc(el->literalToken)));
++ Node::accept(el->expression, this);
++ return true;
++ }
++ void endVisit(AST::TemplateLiteral *) override { stop(u"TemplateLiteral"); }
++
++ bool visit(AST::NumericLiteral *el) override {
++ start(QLatin1String("NumericLiteral value=%1 literalToken=%2")
++ .arg(quotedString(QString::number(el->value)), loc(el->literalToken)));
++ return true;
++ }
++ void endVisit(AST::NumericLiteral *) override { stop(u"NumericLiteral"); }
++
++ bool visit(AST::RegExpLiteral *el) override {
++ start(QLatin1String("RegExpLiteral pattern=%1 flags=%2 literalToken=%3")
++ .arg(quotedString(el->pattern), quotedString(QString::number(el->flags, 16)), loc(el->literalToken)));
++ return true;
++ }
++ void endVisit(AST::RegExpLiteral *) override { stop(u"RegExpLiteral"); }
++
++ bool visit(AST::ArrayPattern *el) override {
++ start(QLatin1String("ArrayPattern lbracketToken=%1 commaToken=%2 rbracketToken=%3 parseMode=%4")
++ .arg(loc(el->lbracketToken),loc(el->commaToken),loc(el->rbracketToken), quotedString(QString::number(el->parseMode, 16))));
++ return true;
++ }
++ void endVisit(AST::ArrayPattern *) override { stop(u"ArrayPattern"); }
++
++ bool visit(AST::ObjectPattern *el) override {
++ start(QLatin1String("ObjectPattern lbraceToken=%1 rbraceToken=%2 parseMode=%3")
++ .arg(loc(el->lbraceToken), loc(el->rbraceToken), quotedString(QString::number(el->parseMode, 16))));
++ return true;
++ }
++ void endVisit(AST::ObjectPattern *) override { stop(u"ObjectPattern"); }
++
++ bool visit(AST::PatternElementList *) override { start(u"PatternElementList"); return true; }
++ void endVisit(AST::PatternElementList *) override { stop(u"PatternElementList"); }
++
++ bool visit(AST::PatternPropertyList *) override { start(u"PatternPropertyList"); return true; }
++ void endVisit(AST::PatternPropertyList *) override { stop(u"PatternPropertyList"); }
++
++ bool visit(AST::PatternElement *el) override {
++ start(QLatin1String("PatternElement identifierToken=%1 bindingIdentifier=%2 type=%3 scope=%4 isForDeclaration=%5")
++ .arg(loc(el->identifierToken), quotedString(el->bindingIdentifier), quotedString(QString::number(el->type, 16)),
++ quotedString(QString::number(static_cast<int>(el->scope), 16)), boolStr(el->isForDeclaration)));
++ return true;
++ }
++ void endVisit(AST::PatternElement *) override { stop(u"PatternElement"); }
++
++ bool visit(AST::PatternProperty *el) override {
++ start(QLatin1String("PatternProperty identifierToken=%1 bindingIdentifier=%2 type=%3 scope=%4 isForDeclaration=%5 colonToken=%6")
++ .arg(loc(el->identifierToken), quotedString(el->bindingIdentifier), quotedString(QString::number(el->type, 16)),
++ quotedString(QString::number(static_cast<int>(el->scope), 16)), boolStr(el->isForDeclaration), loc(el->colonToken)));
++ return true;
++ }
++ void endVisit(AST::PatternProperty *) override { stop(u"PatternProperty"); }
++
++ bool visit(AST::Elision *el) override {
++ start(QLatin1String("Elision commaToken=%1")
++ .arg(loc(el->commaToken)));
++ return true;
++ }
++ void endVisit(AST::Elision *el) override {
++ stop(u"Elision");
++ Node::accept(el->next, this); // emit other elisions at the same level
++ }
++
++ bool visit(AST::NestedExpression *el) override {
++ start(QLatin1String("NestedExpression lparenToken=%1 rparenToken=%2")
++ .arg(loc(el->lparenToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::NestedExpression *) override { stop(u"NestedExpression"); }
++
++ bool visit(AST::IdentifierPropertyName *el) override {
++ if (options & AstDumperOption::SloppyCompare)
++ start(QLatin1String("StringLiteralOrIdentifierPropertyName id=%1")
++ .arg(quotedString(el->id)));
++ else
++ start(QLatin1String("IdentifierPropertyName id=%1 propertyNameToken=%2")
++ .arg(quotedString(el->id), loc(el->propertyNameToken)));
++ return true;
++ }
++ void endVisit(AST::IdentifierPropertyName *) override {
++ if (options & AstDumperOption::SloppyCompare)
++ stop(u"StringLiteralOrIdentifierPropertyName");
++ else
++ stop(u"IdentifierPropertyName");
++ }
++
++ bool visit(AST::StringLiteralPropertyName *el) override {
++ if (options & AstDumperOption::SloppyCompare)
++ start(QLatin1String("StringLiteralOrIdentifierPropertyName id=%1")
++ .arg(quotedString(el->id)));
++ else
++ start(QLatin1String("StringLiteralPropertyName id=%1 propertyNameToken=%2")
++ .arg(quotedString(el->id), loc(el->propertyNameToken)));
++ return true;
++ }
++ void endVisit(AST::StringLiteralPropertyName *) override {
++ if (options & AstDumperOption::SloppyCompare)
++ stop(u"StringLiteralOrIdentifierPropertyName");
++ else
++ stop(u"StringLiteralPropertyName");
++ }
++
++ bool visit(AST::NumericLiteralPropertyName *el) override {
++ start(QLatin1String("NumericLiteralPropertyName id=%1 propertyNameToken=%2")
++ .arg(quotedString(QString::number(el->id)),loc(el->propertyNameToken)));
++ return true;
++ }
++ void endVisit(AST::NumericLiteralPropertyName *) override { stop(u"NumericLiteralPropertyName"); }
++
++ bool visit(AST::ComputedPropertyName *) override {
++ start(u"ComputedPropertyName");
++ return true;
++ }
++ void endVisit(AST::ComputedPropertyName *) override { stop(u"ComputedPropertyName"); }
++
++ bool visit(AST::ArrayMemberExpression *el) override {
++ start(QLatin1String("ArrayMemberExpression lbraketToken=%1 rbraketToken=%2")
++ .arg(loc(el->lbracketToken), loc(el->rbracketToken)));
++ return true;
++ }
++ void endVisit(AST::ArrayMemberExpression *) override { stop(u"ArrayMemberExpression"); }
++
++ bool visit(AST::FieldMemberExpression *el) override {
++ start(QLatin1String("FieldMemberExpression name=%1 dotToken=%2 identifierToken=%3")
++ .arg(quotedString(el->name), loc(el->dotToken), loc(el->identifierToken)));
++ return true;
++ }
++ void endVisit(AST::FieldMemberExpression *) override { stop(u"FieldMemberExpression"); }
++
++ bool visit(AST::TaggedTemplate *) override {
++ start(u"TaggedTemplate");
++ return true;
++ }
++ void endVisit(AST::TaggedTemplate *) override { stop(u"TaggedTemplate"); }
++
++ bool visit(AST::NewMemberExpression *el) override {
++ start(QLatin1String("NewMemberExpression newToken=%1 lparenToken=%2 rparenToken=%3")
++ .arg(loc(el->newToken), loc(el->lparenToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::NewMemberExpression *) override { stop(u"NewMemberExpression"); }
++
++ bool visit(AST::NewExpression *el) override {
++ start(QLatin1String("NewExpression newToken=%1")
++ .arg(loc(el->newToken)));
++ return true;
++ }
++ void endVisit(AST::NewExpression *) override { stop(u"NewExpression"); }
++
++ bool visit(AST::CallExpression *el) override {
++ start(QLatin1String("CallExpression lparenToken=%1 rparenToken=%2")
++ .arg(loc(el->lparenToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::CallExpression *) override { stop(u"CallExpression"); }
++
++ bool visit(AST::ArgumentList *el) override {
++ start(QLatin1String("ArgumentList commaToken=%1 spreadToken=%2")
++ .arg(loc(el->commaToken), loc(el->spreadToken)));
++ return true;
++ }
++ void endVisit(AST::ArgumentList *) override { stop(u"ArgumentList"); }
++
++ bool visit(AST::PostIncrementExpression *el) override {
++ start(QLatin1String("PostIncrementExpression incrementToken=%1")
++ .arg(loc(el->incrementToken)));
++ return true;
++ }
++ void endVisit(AST::PostIncrementExpression *) override { stop(u"PostIncrementExpression"); }
++
++ bool visit(AST::PostDecrementExpression *el) override {
++ start(QLatin1String("PostDecrementExpression decrementToken=%1")
++ .arg(loc(el->decrementToken)));
++ return true;
++ }
++ void endVisit(AST::PostDecrementExpression *) override { stop(u"PostDecrementExpression"); }
++
++ bool visit(AST::DeleteExpression *el) override {
++ start(QLatin1String("DeleteExpression deleteToken=%1")
++ .arg(loc(el->deleteToken)));
++ return true;
++ }
++ void endVisit(AST::DeleteExpression *) override { stop(u"DeleteExpression"); }
++
++ bool visit(AST::VoidExpression *el) override {
++ start(QLatin1String("VoidExpression voidToken=%1")
++ .arg(loc(el->voidToken)));
++ return true;
++ }
++ void endVisit(AST::VoidExpression *) override { stop(u"VoidExpression"); }
++
++ bool visit(AST::TypeOfExpression *el) override {
++ start(QLatin1String("TypeOfExpression typeofToken=%1")
++ .arg(loc(el->typeofToken)));
++ return true;
++ }
++ void endVisit(AST::TypeOfExpression *) override { stop(u"TypeOfExpression"); }
++
++ bool visit(AST::PreIncrementExpression *el) override {
++ start(QLatin1String("PreIncrementExpression incrementToken=%1")
++ .arg(loc(el->incrementToken)));
++ return true;
++ }
++ void endVisit(AST::PreIncrementExpression *) override { stop(u"PreIncrementExpression"); }
++
++ bool visit(AST::PreDecrementExpression *el) override {
++ start(QLatin1String("PreDecrementExpression decrementToken=%1")
++ .arg(loc(el->decrementToken)));
++ return true;
++ }
++ void endVisit(AST::PreDecrementExpression *) override { stop(u"PreDecrementExpression"); }
++
++ bool visit(AST::UnaryPlusExpression *el) override {
++ start(QLatin1String("UnaryPlusExpression plusToken=%1")
++ .arg(loc(el->plusToken)));
++ return true;
++ }
++ void endVisit(AST::UnaryPlusExpression *) override { stop(u"UnaryPlusExpression"); }
++
++ bool visit(AST::UnaryMinusExpression *el) override {
++ start(QLatin1String("UnaryMinusExpression minusToken=%1")
++ .arg(loc(el->minusToken)));
++ return true;
++ }
++ void endVisit(AST::UnaryMinusExpression *) override { stop(u"UnaryMinusExpression"); }
++
++ bool visit(AST::TildeExpression *el) override {
++ start(QLatin1String("TildeExpression tildeToken=%1")
++ .arg(loc(el->tildeToken)));
++ return true;
++ }
++ void endVisit(AST::TildeExpression *) override { stop(u"TildeExpression"); }
++
++ bool visit(AST::NotExpression *el) override {
++ start(QLatin1String("NotExpression notToken=%1")
++ .arg(loc(el->notToken)));
++ return true;
++ }
++ void endVisit(AST::NotExpression *) override { stop(u"NotExpression"); }
++
++ bool visit(AST::BinaryExpression *el) override {
++ start(QLatin1String("BinaryExpression op=%1 operatorToken=%2")
++ .arg(quotedString(QString::number(el->op,16)), loc(el->operatorToken)));
++ return true;
++ }
++ void endVisit(AST::BinaryExpression *) override { stop(u"BinaryExpression"); }
++
++ bool visit(AST::ConditionalExpression *el) override {
++ start(QLatin1String("ConditionalExpression questionToken=%1 colonToken=%2")
++ .arg(loc(el->questionToken), loc(el->colonToken)));
++ return true;
++ }
++ void endVisit(AST::ConditionalExpression *) override { stop(u"ConditionalExpression"); }
++
++ bool visit(AST::CommaExpression *el) override {
++ start(QLatin1String("Expression commaToken=%1")
++ .arg(loc(el->commaToken)));
++ return true;
++ }
++ void endVisit(AST::CommaExpression *) override { stop(u"Expression"); }
++
++ bool visit(AST::Block *el) override {
++ start(QLatin1String("Block lbraceToken=%1 rbraceToken=%2")
++ .arg(loc(el->lbraceToken), loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::Block *) override { stop(u"Block"); }
++
++ bool visit(AST::StatementList *) override {
++ start(u"StatementList");
++ return true;
++ }
++ void endVisit(AST::StatementList *) override { stop(u"StatementList"); }
++
++ bool visit(AST::VariableStatement *el) override {
++ start(QLatin1String("VariableStatement declarationKindToken=%1")
++ .arg(loc(el->declarationKindToken)));
++ return true;
++ }
++ void endVisit(AST::VariableStatement *) override { stop(u"VariableStatement"); }
++
++ bool visit(AST::VariableDeclarationList *el) override {
++ start(QLatin1String("VariableDeclarationList commaToken=%1")
++ .arg(loc(el->commaToken)));
++ return true;
++ }
++ void endVisit(AST::VariableDeclarationList *) override { stop(u"VariableDeclarationList"); }
++
++ bool visit(AST::EmptyStatement *el) override {
++ start(QLatin1String("EmptyStatement%1").arg(semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::EmptyStatement *) override { stop(u"EmptyStatement"); }
++
++ bool visit(AST::ExpressionStatement *el) override {
++ if (options & AstDumperOption::SloppyCompare)
++ start(u"ExpressionStatement");
++ else
++ start(QLatin1String("ExpressionStatement%1").arg(semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::ExpressionStatement *) override { stop(u"ExpressionStatement"); }
++
++ bool visit(AST::IfStatement *el) override {
++ start(QLatin1String("IfStatement ifToken=%1 lparenToken=%2 rparenToken=%3 elseToken=%4")
++ .arg(loc(el->ifToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->elseToken)));
++ return true;
++ }
++ void endVisit(AST::IfStatement *) override { stop(u"IfStatement"); }
++
++ bool visit(AST::DoWhileStatement *el) override {
++ start(QLatin1String(
++ "DoWhileStatement doToken=%1 whileToken=%2 lparenToken=%3 rparenToken=%4%5")
++ .arg(loc(el->doToken), loc(el->whileToken), loc(el->lparenToken),
++ loc(el->rparenToken), semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::DoWhileStatement *) override { stop(u"DoWhileStatement"); }
++
++ bool visit(AST::WhileStatement *el) override {
++ start(QLatin1String("WhileStatement whileToken=%1 lparenToken=%2 rparenToken=%3")
++ .arg(loc(el->whileToken), loc(el->lparenToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::WhileStatement *) override { stop(u"WhileStatement"); }
++
++ bool visit(AST::ForStatement *el) override {
++ if (options & AstDumperOption::SloppyCompare)
++ start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 rparenToken=%5")
++ .arg(loc(el->forToken), loc(el->lparenToken), loc(el->rparenToken)));
++ else
++ start(QLatin1String("ForStatement forToken=%1 lparenToken=%2 firstSemicolonToken=%3 "
++ "secondSemicolonToken=%4 rparenToken=%5")
++ .arg(loc(el->forToken), loc(el->lparenToken),
++ loc(el->firstSemicolonToken), loc(el->secondSemicolonToken),
++ loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::ForStatement *) override { stop(u"ForStatement"); }
++
++ bool visit(AST::ForEachStatement *el) override {
++ start(QLatin1String("ForEachStatement forToken=%1 lparenToken=%2 inOfToken=%3 rparenToken=%4 type=%5")
++ .arg(loc(el->forToken), loc(el->lparenToken), loc(el->inOfToken), loc(el->rparenToken), quotedString(QString::number(static_cast<int>(el->type), 16))));
++ return true;
++ }
++ void endVisit(AST::ForEachStatement *) override { stop(u"ForEachStatement"); }
++
++ bool visit(AST::ContinueStatement *el) override {
++ start(QLatin1String("ContinueStatement label=%1 continueToken=%2 identifierToken=%3%4")
++ .arg(quotedString(el->label), loc(el->continueToken),
++ loc(el->identifierToken), semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::ContinueStatement *) override { stop(u"ContinueStatement"); }
++
++ bool visit(AST::BreakStatement *el) override {
++ start(QLatin1String("BreakStatement label=%1 breakToken=%2 identifierToken=%3%4")
++ .arg(quotedString(el->label), loc(el->breakToken), loc(el->identifierToken),
++ semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::BreakStatement *) override { stop(u"BreakStatement"); }
++
++ bool visit(AST::ReturnStatement *el) override {
++ start(QLatin1String("ReturnStatement returnToken=%1%2")
++ .arg(loc(el->returnToken), semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::ReturnStatement *) override { stop(u"ReturnStatement"); }
++
++ bool visit(AST::YieldExpression *el) override {
++ start(QLatin1String("YieldExpression isYieldStar=%1 yieldToken=%2")
++ .arg(boolStr(el->isYieldStar), loc(el->yieldToken)));
++ return true;
++ }
++ void endVisit(AST::YieldExpression *) override { stop(u"YieldExpression"); }
++
++ bool visit(AST::WithStatement *el) override {
++ start(QLatin1String("WithStatement withToken=%1 lparenToken=%2 rparenToken=%3")
++ .arg(loc(el->withToken), loc(el->lparenToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::WithStatement *) override { stop(u"WithStatement"); }
++
++ bool visit(AST::SwitchStatement *el) override {
++ start(QLatin1String("SwitchStatement switchToken=%1 lparenToken=%2 rparenToken=%3")
++ .arg(loc(el->switchToken), loc(el->lparenToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::SwitchStatement *) override { stop(u"SwitchStatement"); }
++
++ bool visit(AST::CaseBlock *el) override {
++ start(QLatin1String("CaseBlock lbraceToken=%1 rbraceToken=%2")
++ .arg(loc(el->lbraceToken), loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::CaseBlock *) override { stop(u"CaseBlock"); }
++
++ bool visit(AST::CaseClauses *) override {
++ start(u"CaseClauses");
++ return true;
++ }
++ void endVisit(AST::CaseClauses *) override { stop(u"CaseClauses"); }
++
++ bool visit(AST::CaseClause *el) override {
++ start(QLatin1String("CaseClause caseToken=%1 colonToken=%2")
++ .arg(loc(el->caseToken), loc(el->colonToken)));
++ return true;
++ }
++ void endVisit(AST::CaseClause *) override { stop(u"CaseClause"); }
++
++ bool visit(AST::DefaultClause *el) override {
++ start(QLatin1String("DefaultClause defaultToken=%1 colonToken=%2")
++ .arg(loc(el->defaultToken), loc(el->colonToken)));
++ return true;
++ }
++ void endVisit(AST::DefaultClause *) override { stop(u"DefaultClause"); }
++
++ bool visit(AST::LabelledStatement *el) override {
++ start(QLatin1String("LabelledStatement label=%1 identifierToken=%2 colonToken=%3")
++ .arg(quotedString(el->label), loc(el->identifierToken), loc(el->colonToken)));
++ return true;
++ }
++ void endVisit(AST::LabelledStatement *) override { stop(u"LabelledStatement"); }
++
++ bool visit(AST::ThrowStatement *el) override {
++ start(QLatin1String("ThrowStatement throwToken=%1%2")
++ .arg(loc(el->throwToken), semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::ThrowStatement *) override { stop(u"ThrowStatement"); }
++
++ bool visit(AST::TryStatement *el) override {
++ start(QLatin1String("TryStatement tryToken=%1")
++ .arg(loc(el->tryToken)));
++ return true;
++ }
++ void endVisit(AST::TryStatement *) override { stop(u"TryStatement"); }
++
++ bool visit(AST::Catch *el) override {
++ start(QLatin1String("Catch catchToken=%1 lparenToken=%2 identifierToken=%3 rparenToken=%4")
++ .arg(loc(el->catchToken), loc(el->lparenToken), loc(el->identifierToken), loc(el->rparenToken)));
++ return true;
++ }
++ void endVisit(AST::Catch *) override { stop(u"Catch"); }
++
++ bool visit(AST::Finally *el) override {
++ start(QLatin1String("Finally finallyToken=%1")
++ .arg(loc(el->finallyToken)));
++ return true;
++ }
++ void endVisit(AST::Finally *) override { stop(u"Finally"); }
++
++ bool visit(AST::FunctionDeclaration *el) override {
++ start(QLatin1String("FunctionDeclaration name=%1 isArrowFunction=%2 isGenerator=%3 functionToken=%4 "
++ "identifierToken=%5 lparenToken=%6 rparenToken=%7 lbraceToken=%8 rbraceToken=%9")
++ .arg(quotedString(el->name), boolStr(el->isArrowFunction), boolStr(el->isGenerator),
++ loc(el->functionToken, options & AstDumperOption::SloppyCompare),
++ loc(el->identifierToken), loc(el->lparenToken), loc(el->rparenToken), loc(el->lbraceToken),
++ loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::FunctionDeclaration *) override { stop(u"FunctionDeclaration"); }
++
++ bool visit(AST::FunctionExpression *el) override {
++
++ QString parentheses = options & AstDumperOption::NoLocations
++ ? QLatin1String()
++ : QLatin1String(" lparenToken=%1 rparenToken=%2")
++ .arg(loc(el->lparenToken), loc(el->rparenToken));
++
++ start(QLatin1String("FunctionExpression name=%1 isArrowFunction=%2 isGenerator=%3 "
++ "functionToken=%4 "
++ "identifierToken=%5%6 lbraceToken=%7 "
++ "rbraceToken=%8")
++ .arg(quotedString(el->name), boolStr(el->isArrowFunction),
++ boolStr(el->isGenerator),
++ loc(el->functionToken, options & AstDumperOption::SloppyCompare),
++ loc(el->identifierToken), parentheses, loc(el->lbraceToken),
++ loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::FunctionExpression *) override { stop(u"FunctionExpression"); }
++
++ bool visit(AST::FormalParameterList *) override {
++ start(u"FormalParameterList");
++ return true;
++ }
++ void endVisit(AST::FormalParameterList *) override { stop(u"FormalParameterList"); }
++
++ bool visit(AST::ClassExpression *el) override {
++ start(QLatin1String("ClassExpression name=%1 classToken=%2 identifierToken=%3 lbraceToken=%4 rbraceToken=%5")
++ .arg(quotedString(el->name), loc(el->classToken), loc(el->identifierToken), loc(el->lbraceToken), loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::ClassExpression *) override { stop(u"ClassExpression"); }
++
++ bool visit(AST::ClassDeclaration *el) override {
++ start(QLatin1String("ClassDeclaration name=%1 classToken=%2 identifierToken=%3 lbraceToken=%4 rbraceToken=%5")
++ .arg(quotedString(el->name), loc(el->classToken), loc(el->identifierToken), loc(el->lbraceToken), loc(el->rbraceToken)));
++ return true;
++ }
++ void endVisit(AST::ClassDeclaration *) override { stop(u"ClassDeclaration"); }
++
++ bool visit(AST::ClassElementList *el) override {
++ start(QLatin1String("ClassElementList isStatic=%1")
++ .arg(boolStr(el->isStatic)));
++ return true;
++ }
++ void endVisit(AST::ClassElementList *) override { stop(u"ClassElementList"); }
++
++ bool visit(AST::Program *) override {
++ start(u"Program");
++ return true;
++ }
++ void endVisit(AST::Program *) override { stop(u"Program"); }
++
++ bool visit(AST::NameSpaceImport *el) override {
++ start(QLatin1String("NameSpaceImport starToken=%1 importedBindingToken=%2 importedBinding=%3")
++ .arg(loc(el->starToken), loc(el->importedBindingToken), quotedString(el->importedBinding)));
++ return true;
++ }
++ void endVisit(AST::NameSpaceImport *) override { stop(u"NameSpaceImport"); }
++
++ bool visit(AST::ImportSpecifier *el) override {
++ start(QLatin1String("ImportSpecifier identifierToken=%1 importedBindingToken=%2 identifier=%3 importedBinding=%4")
++ .arg(loc(el->identifierToken), loc(el->importedBindingToken), quotedString(el->identifier), quotedString(el->importedBinding)));
++ return true;
++ }
++ void endVisit(AST::ImportSpecifier *) override { stop(u"ImportSpecifier"); }
++
++ bool visit(AST::ImportsList *el) override {
++ start(QLatin1String("ImportsList importSpecifierToken=%1")
++ .arg(loc(el->importSpecifierToken)));
++ return true;
++ }
++ void endVisit(AST::ImportsList *) override { stop(u"ImportsList"); }
++
++ bool visit(AST::NamedImports *el) override {
++ start(QLatin1String("NamedImports leftBraceToken=%1 rightBraceToken=%2")
++ .arg(loc(el->leftBraceToken), loc(el->rightBraceToken)));
++ return true;
++ }
++ void endVisit(AST::NamedImports *) override { stop(u"NamedImports"); }
++
++ bool visit(AST::FromClause *el) override {
++ start(QLatin1String("FromClause fromToken=%1 moduleSpecifierToken=%2 moduleSpecifier=%3")
++ .arg(loc(el->fromToken), loc(el->moduleSpecifierToken), quotedString(el->moduleSpecifier)));
++ return true;
++ }
++ void endVisit(AST::FromClause *) override { stop(u"FromClause"); }
++
++ bool visit(AST::ImportClause *el) override {
++ start(QLatin1String("ImportClause importedDefaultBindingToken=%1 importedDefaultBinding=%2")
++ .arg(loc(el->importedDefaultBindingToken), quotedString(el->importedDefaultBinding)));
++ return true;
++ }
++ void endVisit(AST::ImportClause *) override { stop(u"ImportClause"); }
++
++ bool visit(AST::ImportDeclaration *el) override {
++ start(QLatin1String("ImportDeclaration importToken=%1 moduleSpecifierToken=%2 moduleSpecifier=%3")
++ .arg(loc(el->importToken), loc(el->moduleSpecifierToken), quotedString(el->moduleSpecifier)));
++ return true;
++ }
++ void endVisit(AST::ImportDeclaration *) override { stop(u"ImportDeclaration"); }
++
++ bool visit(AST::ExportSpecifier *el) override {
++ start(QLatin1String("ExportSpecifier identifierToken=%1 exportedIdentifierToken=%2 identifier=%3 exportedIdentifier=%4")
++ .arg(loc(el->identifierToken), loc(el->exportedIdentifierToken), quotedString(el->identifier), quotedString(el->exportedIdentifier)));
++ return true;
++ }
++ void endVisit(AST::ExportSpecifier *) override { stop(u"ExportSpecifier"); }
++
++ bool visit(AST::ExportsList *) override {
++ start(u"ExportsList");
++ return true;
++ }
++ void endVisit(AST::ExportsList *) override { stop(u"ExportsList"); }
++
++ bool visit(AST::ExportClause *el) override {
++ start(QLatin1String("ExportClause leftBraceToken=%1 rightBraceToken=%2")
++ .arg(loc(el->leftBraceToken), loc(el->rightBraceToken)));
++ return true;
++ }
++ void endVisit(AST::ExportClause *) override { stop(u"ExportClause"); }
++
++ bool visit(AST::ExportDeclaration *el) override {
++ start(QLatin1String("ExportDeclaration exportToken=%1 exportDefault=%3")
++ .arg(loc(el->exportToken), boolStr(el->exportDefault)));
++ return true;
++ }
++ void endVisit(AST::ExportDeclaration *) override { stop(u"ExportDeclaration"); }
++
++ bool visit(AST::ESModule *) override {
++ start(u"ESModule");
++ return true;
++ }
++ void endVisit(AST::ESModule *) override { stop(u"ESModule"); }
++
++ bool visit(AST::DebuggerStatement *el) override {
++ start(QLatin1String("DebuggerStatement debuggerToken=%1%2")
++ .arg(loc(el->debuggerToken), semicolonToken(el->semicolonToken)));
++ return true;
++ }
++ void endVisit(AST::DebuggerStatement *) override { stop(u"DebuggerStatement"); }
++
++ bool visit(AST::Type *) override {
++ start(u"Type");
++ return true;
++ }
++ void endVisit(AST::Type *) override { stop(u"Type"); }
++
++ bool visit(AST::TypeAnnotation *el) override {
++ start(QLatin1String("TypeAnnotation colonToken=%1")
++ .arg(loc(el->colonToken)));
++ return true;
++ }
++ void endVisit(AST::TypeAnnotation *) override { stop(u"TypeAnnotation"); }
++
++ void throwRecursionDepthError() override {
++ qCWarning(domLog) << "Maximum statement or expression depth exceeded in AstDumper";
++ }
++
++ private:
++ // attributes
++ std::function <void (QStringView)> dumper;
++ AstDumperOptions options = AstDumperOption::None;
++ int indent = 0;
++ int baseIndent = 0;
++ function_ref<QStringView(SourceLocation)> loc2str;
++ bool dumpNode(){
++ return options & AstDumperOption::DumpNode;
++ }
++ bool noLocations() {
++ return options & AstDumperOption::NoLocations;
++ }
++ bool noAnnotations() {
++ return options & AstDumperOption::NoAnnotations;
++ }
++ };
++
++ QDebug operator<<(QDebug d, AST::Node *n) {
++ QDebug noQuote = d.noquote().nospace();
++ AstDumper visitor([&noQuote](QStringView s){ noQuote << s; });
++ Node::accept(n, &visitor);
++ return d;
++ }
++
++ QString lineDiff(QString s1, QString s2, int nContext) {
++ QTextStream d1(&s1), d2(&s2);
++ QList<QString> preLines(nContext);
++ int nLine = 0;
++ bool same = true;
++ QString l1, l2;
++ while (same && !d1.atEnd() && !d2.atEnd()) {
++ l1=d1.readLine();
++ l2=d2.readLine();
++ if (l1 == l2)
++ preLines[nLine++ % nContext] = l1;
++ else
++ same = false;
++ }
++ QString res;
++ QTextStream ss(&res);
++ if (!same || !d1.atEnd() || !d2.atEnd()) {
++ for (int iline = qMin(nLine, nContext); iline > 0; --iline) {
++ ss << QLatin1String(" ") << preLines[(nLine - iline) % nContext] << QLatin1String("\n");
++ }
++ int iline = 0;
++ if (!same) {
++ ss << QLatin1String("-") << l1 << QLatin1String("\n");
++ ++iline;
++ }
++ if (same && nContext == 0)
++ nContext = 1;
++ for (;iline < nContext && !d1.atEnd(); iline ++) {
++ l1 = d1.readLine();
++ ss << QLatin1String("-") << l1 << QLatin1String("\n");
++ }
++ iline = 0;
++ if (!same) {
++ ss << QLatin1String("+") << l2 << QLatin1String("\n");
++ ++iline;
++ }
++ for (;iline < nContext && !d2.atEnd(); iline ++) {
++ l2 = d2.readLine();
++ ss << QLatin1String("+") << l2 << QLatin1String("\n");
++ }
++ }
++ return res;
++ }
++
++ QString astNodeDiff(AST::Node *n1, AST::Node *n2, int nContext, AstDumperOptions opt, int indent,
++ function_ref<QStringView(SourceLocation)>loc2str1,
++ function_ref<QStringView(SourceLocation)>loc2str2) {
++ QString s1, s2;
++ QTextStream d1(&s1), d2(&s2);
++ AstDumper visitor1=AstDumper([&d1](QStringView s){ d1 << s; }, opt, indent, 0, loc2str1);
++ AstDumper visitor2=AstDumper([&d2](QStringView s){ d2 << s; }, opt, indent, 0, loc2str2);
++ Node::accept(n1, &visitor1);
++ Node::accept(n2, &visitor2);
++ d1.flush();
++ d2.flush();
++ return lineDiff(s1, s2, nContext);
++ }
++
++ void astNodeDumper(const Sink &s, Node *n, AstDumperOptions opt, int indent, int baseIndent,
++ function_ref<QStringView(SourceLocation)>loc2str)
++ {
++ AstDumper visitor=AstDumper(s, opt, indent, baseIndent, loc2str);
++ Node::accept(n, &visitor);
++ }
++
++ QString astNodeDump(Node *n, AstDumperOptions opt, int indent, int baseIndent,
++ function_ref<QStringView(SourceLocation)>loc2str)
++ {
++ return dumperToString(
++ [n, opt, indent, baseIndent, loc2str = std::move(loc2str)](const Sink &s) {
++ astNodeDumper(s, n, opt, indent, baseIndent, std::move(loc2str));
++ });
++ }
++
++ } // end namespace Dom
++ } // end namespace QQmlJS
++
++ QT_END_NAMESPACE