<commit_before>#include "qmljstypedescriptionreader.h"

#include "parser/qmljsparser_p.h"
#include "parser/qmljslexer_p.h"
#include "parser/qmljsengine_p.h"
#include "parser/qmljsnodepool_p.h"
#include "parser/qmljsast_p.h"
#include "parser/qmljsastvisitor_p.h"

#include "qmljsbind.h"

#include <QtCore/QIODevice>
#include <QtCore/QBuffer>

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace LanguageUtils;

TypeDescriptionReader::TypeDescriptionReader(const QString &data)
    : _source(data)
    , _objects(0)
{
}

TypeDescriptionReader::~TypeDescriptionReader()
{
}

bool TypeDescriptionReader::operator()(QHash<QString, FakeMetaObject::Ptr> *objects)
{
    QString fileName("typeDescription");
    Engine engine;
    NodePool pool(fileName, &engine);

    Lexer lexer(&engine);
    Parser parser(&engine);

    lexer.setCode(_source, /*line = */ 1);

    if (!parser.parse()) {
        _errorMessage = QString("%1:%2: %3").arg(
                    QString::number(parser.errorLineNumber()),
                    QString::number(parser.errorColumnNumber()),
                    parser.errorMessage());
        return false;
    }

    _objects = objects;
    readDocument(parser.ast());

    return _errorMessage.isEmpty();
}

QString TypeDescriptionReader::errorMessage() const
{
    return _errorMessage;
}

void TypeDescriptionReader::readDocument(UiProgram *ast)
{
    if (!ast) {
        addError(SourceLocation(), "Could not parse document");
        return;
    }

    if (!ast->imports || ast->imports->next) {
        addError(SourceLocation(), "Expected a single import");
        return;
    }

    UiImport *import = ast->imports->import;
    if (Bind::toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
        addError(import->importToken, "Expected import of QtQuick.tooling");
        return;
    }

    ComponentVersion version;
    const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
    const int dotIdx = versionString.indexOf(QLatin1Char('.'));
    if (dotIdx != -1) {
        version = ComponentVersion(versionString.left(dotIdx).toInt(),
                                   versionString.mid(dotIdx + 1).toInt());
    }
    if (version != ComponentVersion(1, 0)) {
        addError(import->versionToken, "Expected version 1.0");
        return;
    }

    if (!ast->members || !ast->members->member || ast->members->next) {
        addError(SourceLocation(), "Expected document to contain a single object definition");
        return;
    }

    UiObjectDefinition *module = dynamic_cast<UiObjectDefinition *>(ast->members->member);
    if (!module) {
        addError(SourceLocation(), "Expected document to contain a single object definition");
        return;
    }

    if (Bind::toString(module->qualifiedTypeNameId) != "Module") {
        addError(SourceLocation(), "Expected document to contain a Module {} member");
        return;
    }

    readModule(module);
}

void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
{
    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        if (!component || Bind::toString(component->qualifiedTypeNameId) != "Component") {
            addError(member->firstSourceLocation(), "Expected only 'Component' object definitions");
            return;
        }

        readComponent(component);
    }
}

void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
{
    _errorMessage += QString("%1:%2: %3\n").arg(
                QString::number(loc.startLine),
                QString::number(loc.startColumn),
                message);
}

void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{
    FakeMetaObject::Ptr fmo(new FakeMetaObject);

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (component) {
            QString name = Bind::toString(component->qualifiedTypeNameId);
            if (name == "Property") {
                readProperty(component, fmo);
            } else if (name == "Method" || name == "Signal") {
                readSignalOrMethod(component, name == "Method", fmo);
            } else if (name == "Enum") {
                readEnum(component, fmo);
            } else {
                addError(component->firstSourceLocation(), "Expected only Property, Method, Signal and Enum object definitions");
                return;
            }
        } else if (script) {
            QString name = Bind::toString(script->qualifiedId);
            if (name == "name") {
                fmo->setClassName(readStringBinding(script));
            } else if (name == "prototype") {
                fmo->setSuperclassName(readStringBinding(script));
            } else if (name == "defaultProperty") {
                fmo->setDefaultPropertyName(readStringBinding(script));
            } else if (name == "exports") {
                readExports(script, fmo);
            } else {
                addError(script->firstSourceLocation(), "Expected only name, prototype, defaultProperty and exports script bindings");
                return;
            }
        } else {
            addError(member->firstSourceLocation(), "Expected only script bindings and object definitions");
            return;
        }
    }

    if (fmo->className().isEmpty()) {
        addError(ast->firstSourceLocation(), "Component definition is missing a name binding");
        return;
    }

    _objects->insert(fmo->className(), fmo);
}

void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
{
    FakeMetaMethod fmm;
    // ### confusion between Method and Slot. Method should be removed.
    if (isMethod)
        fmm.setMethodType(FakeMetaMethod::Slot);
    else
        fmm.setMethodType(FakeMetaMethod::Signal);

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (component) {
            QString name = Bind::toString(component->qualifiedTypeNameId);
            if (name == "Parameter") {
                readParameter(component, &fmm);
            } else {
                addError(component->firstSourceLocation(), "Expected only Parameter object definitions");
                return;
            }
        } else if (script) {
            QString name = Bind::toString(script->qualifiedId);
            if (name == "name") {
                fmm.setMethodName(readStringBinding(script));
            } else if (name == "type") {<commit_msg>QmlJS: Fix qml type info by reintroducing workaround exports.<commit_after>#include "qmljstypedescriptionreader.h"

#include "parser/qmljsparser_p.h"
#include "parser/qmljslexer_p.h"
#include "parser/qmljsengine_p.h"
#include "parser/qmljsnodepool_p.h"
#include "parser/qmljsast_p.h"
#include "parser/qmljsastvisitor_p.h"

#include "qmljsbind.h"

#include <QtCore/QIODevice>
#include <QtCore/QBuffer>

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace LanguageUtils;

TypeDescriptionReader::TypeDescriptionReader(const QString &data)
    : _source(data)
    , _objects(0)
{
}

TypeDescriptionReader::~TypeDescriptionReader()
{
}

bool TypeDescriptionReader::operator()(QHash<QString, FakeMetaObject::Ptr> *objects)
{
    QString fileName("typeDescription");
    Engine engine;
    NodePool pool(fileName, &engine);

    Lexer lexer(&engine);
    Parser parser(&engine);

    lexer.setCode(_source, /*line = */ 1);

    if (!parser.parse()) {
        _errorMessage = QString("%1:%2: %3").arg(
                    QString::number(parser.errorLineNumber()),
                    QString::number(parser.errorColumnNumber()),
                    parser.errorMessage());
        return false;
    }

    _objects = objects;
    readDocument(parser.ast());

    return _errorMessage.isEmpty();
}

QString TypeDescriptionReader::errorMessage() const
{
    return _errorMessage;
}

void TypeDescriptionReader::readDocument(UiProgram *ast)
{
    if (!ast) {
        addError(SourceLocation(), "Could not parse document");
        return;
    }

    if (!ast->imports || ast->imports->next) {
        addError(SourceLocation(), "Expected a single import");
        return;
    }

    UiImport *import = ast->imports->import;
    if (Bind::toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
        addError(import->importToken, "Expected import of QtQuick.tooling");
        return;
    }

    ComponentVersion version;
    const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
    const int dotIdx = versionString.indexOf(QLatin1Char('.'));
    if (dotIdx != -1) {
        version = ComponentVersion(versionString.left(dotIdx).toInt(),
                                   versionString.mid(dotIdx + 1).toInt());
    }
    if (version != ComponentVersion(1, 0)) {
        addError(import->versionToken, "Expected version 1.0");
        return;
    }

    if (!ast->members || !ast->members->member || ast->members->next) {
        addError(SourceLocation(), "Expected document to contain a single object definition");
        return;
    }

    UiObjectDefinition *module = dynamic_cast<UiObjectDefinition *>(ast->members->member);
    if (!module) {
        addError(SourceLocation(), "Expected document to contain a single object definition");
        return;
    }

    if (Bind::toString(module->qualifiedTypeNameId) != "Module") {
        addError(SourceLocation(), "Expected document to contain a Module {} member");
        return;
    }

    readModule(module);
}

void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
{
    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        if (!component || Bind::toString(component->qualifiedTypeNameId) != "Component") {
            addError(member->firstSourceLocation(), "Expected only 'Component' object definitions");
            return;
        }

        readComponent(component);
    }
}

void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
{
    _errorMessage += QString("%1:%2: %3\n").arg(
                QString::number(loc.startLine),
                QString::number(loc.startColumn),
                message);
}

void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{
    FakeMetaObject::Ptr fmo(new FakeMetaObject);

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (component) {
            QString name = Bind::toString(component->qualifiedTypeNameId);
            if (name == "Property") {
                readProperty(component, fmo);
            } else if (name == "Method" || name == "Signal") {
                readSignalOrMethod(component, name == "Method", fmo);
            } else if (name == "Enum") {
                readEnum(component, fmo);
            } else {
                addError(component->firstSourceLocation(), "Expected only Property, Method, Signal and Enum object definitions");
                return;
            }
        } else if (script) {
            QString name = Bind::toString(script->qualifiedId);
            if (name == "name") {
                fmo->setClassName(readStringBinding(script));
            } else if (name == "prototype") {
                fmo->setSuperclassName(readStringBinding(script));
            } else if (name == "defaultProperty") {
                fmo->setDefaultPropertyName(readStringBinding(script));
            } else if (name == "exports") {
                readExports(script, fmo);
            } else {
                addError(script->firstSourceLocation(), "Expected only name, prototype, defaultProperty and exports script bindings");
                return;
            }
        } else {
            addError(member->firstSourceLocation(), "Expected only script bindings and object definitions");
            return;
        }
    }

    if (fmo->className().isEmpty()) {
        addError(ast->firstSourceLocation(), "Component definition is missing a name binding");
        return;
    }

    // ### for backwards compatibility until fixed: export by cpp name
    fmo->addExport(fmo->className(), "", ComponentVersion());
    _objects->insert(fmo->className(), fmo);
}

void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
{
    FakeMetaMethod fmm;
    // ### confusion between Method and Slot. Method should be removed.
    if (isMethod)
        fmm.setMethodType(FakeMetaMethod::Slot);
    else
        fmm.setMethodType(FakeMetaMethod::Signal);

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (component) {
            QString name = Bind::toString(component->qualifiedTypeNameId);
            if (name == "Parameter") {
                readParameter(component, &fmm);
            } else {
                addError(component->firstSourceLocation(), "Expected only Parameter object definitions");
                return;
            }
        } else if (script) {
            QString name = Bind::toString(script->qualifiedId);
            if (name == "name") {
                fmm.setMethodName(readStringBinding(script));
            } else if (name == "type") {
{'fraction_non_alphanumeric': 0.12694535557261497, 'fraction_numerical': 0.0022332333030916324, 'mean_word_length': 2.8377075522228172, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7580534', 'n_tokens_mistral': 3963, 'n_tokens_neox': 3849, 'n_words': 845}