aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@qt.io>2019-12-03 11:07:47 +0100
committerChristian Stenger <christian.stenger@qt.io>2019-12-09 11:37:26 +0000
commit8f682573a819b6665c6b878d3921dc124af842e8 (patch)
treeef70d31bf0e151d3e83d3b7a3c771163ba68710f
parente5f1559bb642877ea2bab38e6638579c811ebfa6 (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.cpp28
-rw-r--r--src/libs/qmljs/qmljsinterpreter.cpp78
-rw-r--r--src/libs/qmljs/qmljsinterpreter.h4
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(&copyProcessor);
+
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;