diff options
author | Christian Stenger <christian.stenger@qt.io> | 2019-12-03 11:07:47 +0100 |
---|---|---|
committer | Christian Stenger <christian.stenger@qt.io> | 2019-12-09 11:37:26 +0000 |
commit | 8f682573a819b6665c6b878d3921dc124af842e8 (patch) | |
tree | ef70d31bf0e151d3e83d3b7a3c771163ba68710f | |
parent | e5f1559bb642877ea2bab38e6638579c811ebfa6 (diff) |
QmlJS: Improve support for multiple imports into same alias
Add extra handling for aliased imports to avoid handling
shadowed imports as unknown which in turn ignored them
and marked their members as unknown types and did not
provide auto completion for their members.
Fixes: QTCREATORBUG-15684
Change-Id: Iee1009cbdfde13ce261854c3239b9b50c434f563
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/libs/qmljs/qmljscontext.cpp | 28 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.cpp | 78 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.h | 4 |
3 files changed, 74 insertions, 36 deletions
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<Import> &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<Import> &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<Import> &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<Import> &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<Import> m_imports; + QHash<QString, ObjectValue *> m_aliased; TypeScope *m_typeScope; JSImportScope *m_jsImportScope; bool m_importFailed; |