diff --git a/src/libs/qmljs/qmljscontext.cpp b/src/libs/qmljs/qmljscontext.cpp index 3e90a40db81..8cf845a6233 100644 --- a/src/libs/qmljs/qmljscontext.cpp +++ b/src/libs/qmljs/qmljscontext.cpp @@ -104,6 +104,9 @@ const Imports *Context::imports(const QmlJS::Document *doc) const const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId *qmlTypeName, UiQualifiedId *qmlTypeNameEnd) const { + if (!qmlTypeName) + return nullptr; + const Imports *importsObj = imports(doc); if (!importsObj) return nullptr; @@ -111,8 +114,13 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId if (!objectValue) return nullptr; - for (UiQualifiedId *iter = qmlTypeName; objectValue && iter && iter != qmlTypeNameEnd; - iter = iter->next) { + UiQualifiedId *iter = qmlTypeName; + if (const ObjectValue *value = importsObj->aliased(iter->name.toString())) { + objectValue = value; + iter = iter->next; + } + + for ( ; objectValue && iter && iter != qmlTypeNameEnd; iter = iter->next) { const Value *value = objectValue->lookupMember(iter->name.toString(), this, nullptr, false); if (!value) return nullptr; @@ -125,6 +133,9 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QStringList &qmlTypeName) const { + if (qmlTypeName.isEmpty()) + return nullptr; + const Imports *importsObj = imports(doc); if (!importsObj) return nullptr; @@ -132,11 +143,14 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QString if (!objectValue) return nullptr; - foreach (const QString &name, qmlTypeName) { - if (!objectValue) - return nullptr; - - const Value *value = objectValue->lookupMember(name, this); + auto iter = qmlTypeName.cbegin(); + if (const ObjectValue *value = importsObj->aliased(*iter)) { + objectValue = value; + ++iter; + } + auto end = qmlTypeName.cend(); + for ( ; objectValue && iter != end; ) { + const Value *value = objectValue->lookupMember(*iter, this); if (!value) return nullptr; diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 825ead91092..695cad16008 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -2355,6 +2355,12 @@ TypeScope::TypeScope(const Imports *imports, ValueOwner *valueOwner) const Value *TypeScope::lookupMember(const QString &name, const Context *context, const ObjectValue **foundInObject, bool) const { + if (const ObjectValue *value = m_imports->resolveAliasAndMarkUsed(name)) { + if (foundInObject) + *foundInObject = this; + return value; + } + const QList &imports = m_imports->all(); for (int pos = imports.size(); --pos >= 0; ) { const Import &i = imports.at(pos); @@ -2365,16 +2371,6 @@ const Value *TypeScope::lookupMember(const QString &name, const Context *context if (info.type() == ImportType::File || info.type() == ImportType::QrcFile) continue; - if (!info.as().isEmpty()) { - if (info.as() == name) { - if (foundInObject) - *foundInObject = this; - i.used = true; // FIXME: This evilly modifies a 'const' object - return import; - } - continue; - } - if (const Value *v = import->lookupMember(name, context, foundInObject)) { i.used = true; return v; @@ -2418,26 +2414,10 @@ JSImportScope::JSImportScope(const Imports *imports, ValueOwner *valueOwner) const Value *JSImportScope::lookupMember(const QString &name, const Context *, const ObjectValue **foundInObject, bool) const { - const QList &imports = m_imports->all(); - for (int pos = imports.size(); --pos >= 0; ) { - const Import &i = imports.at(pos); - const ObjectValue *import = i.object; - const ImportInfo &info = i.info; - - // JS imports are always: import "somefile.js" as Foo - if (info.type() != ImportType::File && info.type() != ImportType::QrcFile) - continue; - - if (info.as() == name) { - if (foundInObject) - *foundInObject = this; - i.used = true; - return import; - } - } + const ObjectValue *value = m_imports->resolveAliasAndMarkUsed(name); if (foundInObject) - *foundInObject = nullptr; - return nullptr; + *foundInObject = value ? this : nullptr; + return value; } void JSImportScope::processMembers(MemberProcessor *processor) const @@ -2464,10 +2444,31 @@ Imports::Imports(ValueOwner *valueOwner) , m_importFailed(false) {} +class MemberCopy : public MemberProcessor +{ +public: + explicit MemberCopy(ObjectValue *value) : m_value(value) {} + bool processProperty(const QString &name, const Value *value, + const PropertyInfo & /*propertyInfo*/) override + { + m_value->setMember(name, value); + return true; + } +private: + ObjectValue *m_value = nullptr; +}; + void Imports::append(const Import &import) { // when doing lookup, imports with 'as' clause are looked at first if (!import.info.as().isEmpty()) { + const QString alias = import.info.as(); + if (!m_aliased.contains(alias)) + m_aliased.insert(alias, m_typeScope->valueOwner()->newObject(nullptr)); + ObjectValue *obj = m_aliased[alias]; + MemberCopy copyProcessor(obj); + import.object->processMembers(©Processor); + m_imports.append(import); } else { // find first as-import and prepend @@ -2554,6 +2555,11 @@ const QList &Imports::all() const return m_imports; } +const ObjectValue *Imports::aliased(const QString &name) const +{ + return m_aliased.value(name, nullptr); +} + const TypeScope *Imports::typeScope() const { return m_typeScope; @@ -2564,6 +2570,20 @@ const JSImportScope *Imports::jsImportScope() const return m_jsImportScope; } +const ObjectValue *Imports::resolveAliasAndMarkUsed(const QString &name) const +{ + if (const ObjectValue *value = m_aliased.value(name, nullptr)) { + // mark all respective ImportInfo objects to avoid dropping imports (QmlDesigner) on rewrite + for (const Import &i : qAsConst(m_imports)) { + const ImportInfo &info = i.info; + if (info.as() == name) + i.used = true; // FIXME: This evilly modifies a 'const' object + } + return value; + } + return nullptr; +} + #ifdef QT_DEBUG class MemberDumper: public MemberProcessor diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index a726b3bff16..0a52b34999d 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -1094,10 +1094,13 @@ public: bool importFailed() const; const QList &all() const; + const ObjectValue *aliased(const QString &name) const; const TypeScope *typeScope() const; const JSImportScope *jsImportScope() const; + const ObjectValue *resolveAliasAndMarkUsed(const QString &name) const; + #ifdef QT_DEBUG void dump() const; #endif @@ -1106,6 +1109,7 @@ private: // holds imports in the order they appeared, // lookup order is back to front QList m_imports; + QHash m_aliased; TypeScope *m_typeScope; JSImportScope *m_jsImportScope; bool m_importFailed;