From 1f0b717ad2c6cd6bd4f7d70efa0bb6cc6f8f14be Mon Sep 17 00:00:00 2001
From: Roberto Raggi <roberto.raggi@nokia.com>
Date: Wed, 3 Feb 2010 14:31:03 +0100
Subject: [PATCH] Completion for global variables in JS.

* Support for lookup JS vs Qml
* Bind global variables.

Done-with: ckamm
---
 src/libs/qmljs/qmljsbind.cpp                  | 120 +++----------
 src/libs/qmljs/qmljsbind.h                    |   6 +
 src/libs/qmljs/qmljscheck.cpp                 |  12 +-
 src/libs/qmljs/qmljscheck.h                   |   4 +-
 src/libs/qmljs/qmljsinterpreter.cpp           | 159 +++++++++++++++++-
 src/libs/qmljs/qmljsinterpreter.h             |  84 ++++++++-
 src/libs/qmljs/qmljslink.cpp                  |  77 ++++-----
 src/libs/qmljs/qmljslink.h                    |   8 -
 src/plugins/qmljseditor/qmlcodecompletion.cpp |  30 ++--
 src/plugins/qmljseditor/qmlhoverhandler.cpp   |   2 +-
 10 files changed, 324 insertions(+), 178 deletions(-)

diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp
index a7da7492439..c7e387bd9a1 100644
--- a/src/libs/qmljs/qmljsbind.cpp
+++ b/src/libs/qmljs/qmljsbind.cpp
@@ -41,88 +41,6 @@ using namespace QmlJS;
 using namespace QmlJS::AST;
 using namespace QmlJS::Interpreter;
 
-namespace {
-
-class ASTObjectValue: public ObjectValue
-{
-    UiQualifiedId *_typeName;
-    UiObjectInitializer *_initializer;
-
-public:
-    ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, Interpreter::Engine *engine)
-        : ObjectValue(engine), _typeName(typeName), _initializer(initializer)
-    {
-    }
-
-    virtual void processMembers(MemberProcessor *processor) const
-    {
-        if (_initializer) {
-            for (UiObjectMemberList *it = _initializer->members; it; it = it->next) {
-                UiObjectMember *member = it->member;
-                if (UiPublicMember *def = cast<UiPublicMember *>(member)) {
-                    if (def->name && def->memberType) {
-                        const QString propName = def->name->asString();
-                        const QString propType = def->memberType->asString();
-
-                        processor->processProperty(propName, engine()->defaultValueForBuiltinType(propType));
-                    }
-                }
-            }
-        }
-        ObjectValue::processMembers(processor);
-    }
-};
-
-class ASTFunctionValue: public FunctionValue
-{
-    FunctionDeclaration *_ast;
-    QList<NameId *> _argumentNames;
-
-public:
-    ASTFunctionValue(FunctionDeclaration *ast, Interpreter::Engine *engine)
-        : FunctionValue(engine), _ast(ast)
-    {
-        setPrototype(engine->functionPrototype());
-
-        for (FormalParameterList *it = ast->formals; it; it = it->next)
-            _argumentNames.append(it->name);
-    }
-
-    FunctionDeclaration *ast() const { return _ast; }
-
-    virtual const Value *returnValue() const
-    {
-        return engine()->undefinedValue();
-    }
-
-    virtual int argumentCount() const
-    {
-        return _argumentNames.size();
-    }
-
-    virtual const Value *argument(int) const
-    {
-        return engine()->undefinedValue();
-    }
-
-    virtual QString argumentName(int index) const
-    {
-        if (index < _argumentNames.size()) {
-            if (NameId *nameId = _argumentNames.at(index))
-                return nameId->asString();
-        }
-
-        return FunctionValue::argumentName(index);
-    }
-
-    virtual bool isVariadic() const
-    {
-        return true;
-    }
-};
-
-} // end of anonymous namespace
-
 Bind::Bind(Document *doc)
     : _doc(doc),
       _currentObjectValue(0),
@@ -130,18 +48,8 @@ Bind::Bind(Document *doc)
       _functionEnvironment(0),
       _rootObjectValue(0)
 {
-    if (!_doc)
-        return;
-
-    if (_doc->qmlProgram()) {
-        _idEnvironment = _interp.newObject(/*prototype =*/ 0);
-        _functionEnvironment = _interp.newObject(/*prototype =*/ 0);
-    } else if (_doc->jsProgram()) {
-        _currentObjectValue = _interp.globalObject();
-        _rootObjectValue = _interp.globalObject();
-    }
-
-    accept(_doc->ast());
+    if (_doc)
+        accept(_doc->ast());
 }
 
 Bind::~Bind()
@@ -248,6 +156,20 @@ void Bind::accept(Node *node)
     Node::accept(node, this);
 }
 
+bool Bind::visit(AST::UiProgram *)
+{
+    _idEnvironment = _interp.newObject(/*prototype =*/ 0);
+    _functionEnvironment = _interp.newObject(/*prototype =*/ 0);
+    return true;
+}
+
+bool Bind::visit(AST::Program *)
+{
+    _currentObjectValue = _interp.globalObject();
+    _rootObjectValue = _interp.globalObject();
+    return true;
+}
+
 bool Bind::visit(UiImport *ast)
 {
     if (ast->fileName) {
@@ -305,6 +227,16 @@ bool Bind::visit(UiArrayBinding *)
     return true;
 }
 
+bool Bind::visit(VariableDeclaration *ast)
+{
+    if (! ast->name)
+        return false;
+
+    ASTVariableReference *ref = new ASTVariableReference(ast, &_interp);
+    _currentObjectValue->setProperty(ast->name->asString(), ref);
+    return false;
+}
+
 bool Bind::visit(FunctionDeclaration *ast)
 {
     if (!ast->name)
diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h
index d24075e2905..bb64a8685bc 100644
--- a/src/libs/qmljs/qmljsbind.h
+++ b/src/libs/qmljs/qmljsbind.h
@@ -61,6 +61,9 @@ protected:
 
     void accept(AST::Node *node);
 
+    virtual bool visit(AST::UiProgram *ast);
+    virtual bool visit(AST::Program *ast);
+
     // Ui
     virtual bool visit(AST::UiImport *ast);
     virtual bool visit(AST::UiPublicMember *ast);
@@ -68,7 +71,10 @@ protected:
     virtual bool visit(AST::UiObjectBinding *ast);
     virtual bool visit(AST::UiScriptBinding *ast);
     virtual bool visit(AST::UiArrayBinding *ast);
+
+    // QML/JS
     virtual bool visit(AST::FunctionDeclaration *ast);
+    virtual bool visit(AST::VariableDeclaration *ast);
 
 protected:
     Interpreter::ObjectValue *switchObjectValue(Interpreter::ObjectValue *newObjectValue);
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 3d24f79054d..31b4567af3e 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -37,9 +37,9 @@
 using namespace QmlJS;
 using namespace QmlJS::Interpreter;
 
-Check::Check(Link *link)
-    : _engine(link->engine()),
-      _link(link),
+Check::Check(Context *context)
+    : _engine(context->engine()),
+      _context(context),
       _scope(_engine->globalObject()),
       _result(0)
 {
@@ -66,7 +66,7 @@ const Interpreter::Value *Check::check(AST::Node *ast)
     const Value *result = switchResult(previousResult);
 
     if (const Reference *ref = value_cast<const Reference *>(result))
-        result = ref->value(_link->context());
+        result = ref->value(_context);
 
     if (! result)
         result = _engine->undefinedValue();
@@ -165,7 +165,7 @@ bool Check::visit(AST::UiQualifiedId *ast)
     if (! ast->name)
          return false;
 
-    const Value *value = _link->lookup(ast->name->asString());
+    const Value *value = _context->lookup(ast->name->asString());
     if (! ast->next) {
         _result = value;
 
@@ -213,7 +213,7 @@ bool Check::visit(AST::IdentifierExpression *ast)
     if (! ast->name)
         return false;
 
-    _result = _link->lookup(ast->name->asString());
+    _result = _context->lookup(ast->name->asString());
     return false;
 }
 
diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h
index 95fef91b3ff..14a56f8507d 100644
--- a/src/libs/qmljs/qmljscheck.h
+++ b/src/libs/qmljs/qmljscheck.h
@@ -48,7 +48,7 @@ namespace Interpreter {
 class QMLJS_EXPORT Check: protected AST::Visitor
 {
 public:
-    Check(Link *link);
+    Check(Interpreter::Context *context);
     virtual ~Check();
 
     const Interpreter::Value *operator()(AST::Node *ast);
@@ -158,7 +158,7 @@ protected:
 private:
     QmlJS::Document::Ptr _doc;
     Interpreter::Engine *_engine;
-    Link *_link;
+    Interpreter::Context *_context;
     const Interpreter::ObjectValue *_scope;
     const Interpreter::Value *_result;
 };
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index 6d293ac5e37..0a650c98177 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -28,7 +28,8 @@
 **************************************************************************/
 
 #include "qmljsinterpreter.h"
-
+#include "qmljscheck.h"
+#include "parser/qmljsast_p.h"
 #include <QtCore/QMetaObject>
 #include <QtCore/QMetaProperty>
 #include <QtCore/QDebug>
@@ -42,6 +43,7 @@
 #endif
 
 using namespace QmlJS::Interpreter;
+using namespace QmlJS::AST;
 
 namespace {
 
@@ -677,7 +679,8 @@ void StringValue::accept(ValueVisitor *visitor) const
 
 
 Context::Context(Engine *engine)
-    : _engine(engine)
+    : _engine(engine),
+      _lookupMode(JSLookup)
 {
 }
 
@@ -690,6 +693,46 @@ Engine *Context::engine() const
     return _engine;
 }
 
+Context::ScopeChain Context::scopeChain() const
+{
+    return _scopeChain;
+}
+
+Context::LookupMode Context::lookupMode() const
+{
+    return _lookupMode;
+}
+
+void Context::setLookupMode(LookupMode lookupMode)
+{
+    _lookupMode = lookupMode;
+}
+
+void Context::pushScope(const ObjectValue *object)
+{
+    _scopeChain.append(object);
+}
+
+void Context::popScope()
+{
+    _scopeChain.removeLast();
+}
+
+const Value *Context::lookup(const QString &name) const
+{
+    for (int index = _scopeChain.size() - 1; index != -1; --index) {
+        const ObjectValue *scope = _scopeChain.at(index);
+
+        if (const Value *member = scope->lookupMember(name)) {
+            if (_lookupMode == JSLookup || ! dynamic_cast<const ASTVariableReference *>(member)) {
+                return member;
+            }
+        }
+    }
+
+    return _engine->undefinedValue();
+}
+
 const Value *Context::property(const ObjectValue *object, const QString &name) const
 {
     const Properties properties = _properties.value(object);
@@ -701,14 +744,21 @@ void Context::setProperty(const ObjectValue *object, const QString &name, const
     _properties[object].insert(name, value);
 }
 
-Reference::Reference()
+Reference::Reference(Engine *engine)
+    : _engine(engine)
 {
+    _engine->registerValue(this);
 }
 
 Reference::~Reference()
 {
 }
 
+Engine *Reference::engine() const
+{
+    return _engine;
+}
+
 const Reference *Reference::asReference() const
 {
     return this;
@@ -719,6 +769,10 @@ void Reference::accept(ValueVisitor *visitor) const
     visitor->visit(this);
 }
 
+const Value *Reference::value(Context *) const
+{
+    return _engine->undefinedValue();
+}
 
 MemberProcessor::MemberProcessor()
 {
@@ -757,7 +811,7 @@ ObjectValue::ObjectValue(Engine *engine)
     : _engine(engine),
       _prototype(0)
 {
-    engine->registerObject(this);
+    engine->registerValue(this);
 }
 
 ObjectValue::~ObjectValue()
@@ -1314,7 +1368,7 @@ Engine::Engine()
 
 Engine::~Engine()
 {
-    qDeleteAll(_registeredObjects);
+    qDeleteAll(_registeredValues);
 }
 
 const NullValue *Engine::nullValue() const
@@ -1456,9 +1510,9 @@ const ObjectValue *Engine::mathObject() const
     return _mathObject;
 }
 
-void Engine::registerObject(ObjectValue *object)
+void Engine::registerValue(Value *value)
 {
-    _registeredObjects.append(object);
+    _registeredValues.append(value);
 }
 
 const Value *Engine::convertToBoolean(const Value *value)
@@ -1802,3 +1856,94 @@ QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix,
 }
 #endif
 
+
+
+ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, Engine *engine)
+    : ObjectValue(engine), _typeName(typeName), _initializer(initializer)
+{
+}
+
+ASTObjectValue::~ASTObjectValue()
+{
+}
+
+void ASTObjectValue::processMembers(MemberProcessor *processor) const
+{
+    if (_initializer) {
+        for (UiObjectMemberList *it = _initializer->members; it; it = it->next) {
+            UiObjectMember *member = it->member;
+            if (UiPublicMember *def = cast<UiPublicMember *>(member)) {
+                if (def->name && def->memberType) {
+                    const QString propName = def->name->asString();
+                    const QString propType = def->memberType->asString();
+
+                    processor->processProperty(propName, engine()->defaultValueForBuiltinType(propType));
+                }
+            }
+        }
+    }
+    ObjectValue::processMembers(processor);
+}
+
+ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, Engine *engine)
+    : Reference(engine), _ast(ast)
+{
+}
+
+ASTVariableReference::~ASTVariableReference()
+{
+}
+
+const Value *ASTVariableReference::value(Context *context) const
+{
+    Check check(context);
+    return check(_ast->expression);
+}
+
+ASTFunctionValue::ASTFunctionValue(FunctionDeclaration *ast, Engine *engine)
+    : FunctionValue(engine), _ast(ast)
+{
+    setPrototype(engine->functionPrototype());
+
+    for (FormalParameterList *it = ast->formals; it; it = it->next)
+        _argumentNames.append(it->name);
+}
+
+ASTFunctionValue::~ASTFunctionValue()
+{
+}
+
+FunctionDeclaration *ASTFunctionValue::ast() const
+{
+    return _ast;
+}
+
+const Value *ASTFunctionValue::returnValue() const
+{
+    return engine()->undefinedValue();
+}
+
+int ASTFunctionValue::argumentCount() const
+{
+    return _argumentNames.size();
+}
+
+const Value *ASTFunctionValue::argument(int) const
+{
+    return engine()->undefinedValue();
+}
+
+QString ASTFunctionValue::argumentName(int index) const
+{
+    if (index < _argumentNames.size()) {
+        if (NameId *nameId = _argumentNames.at(index))
+            return nameId->asString();
+    }
+
+    return FunctionValue::argumentName(index);
+}
+
+bool ASTFunctionValue::isVariadic() const
+{
+    return true;
+}
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index f6cc9dc5201..1d3e261e883 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -32,6 +32,7 @@
 
 #include <qmljs/qmljs_global.h>
 #include <qmljs/qmljsmetatypesystem.h>
+#include <qmljs/parser/qmljsastfwd_p.h>
 
 #include <QtCore/QList>
 #include <QtCore/QString>
@@ -39,6 +40,9 @@
 #include <QtCore/QSet>
 
 namespace QmlJS {
+
+class NameId;
+
 namespace Interpreter {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -207,11 +211,28 @@ public:
 
 class QMLJS_EXPORT Context
 {
+public:
+    typedef QList<const ObjectValue *> ScopeChain;
+
+    enum LookupMode {
+        JSLookup,
+        QmlLookup
+    };
+
 public:
     Context(Engine *engine);
-    virtual ~Context();
+    ~Context();
 
     Engine *engine() const;
+    ScopeChain scopeChain() const;
+
+    LookupMode lookupMode() const;
+    void setLookupMode(LookupMode lookupMode);
+
+    void pushScope(const ObjectValue *object);
+    void popScope();
+
+    const Value *lookup(const QString &name) const;
 
     const Value *property(const ObjectValue *object, const QString &name) const;
     void setProperty(const ObjectValue *object, const QString &name, const Value *value);
@@ -220,20 +241,26 @@ private:
     typedef QHash<QString, const Value *> Properties;
 
     Engine *_engine;
+    LookupMode _lookupMode;
     QHash<const ObjectValue *, Properties> _properties;
+    ScopeChain _scopeChain;
 };
 
 class QMLJS_EXPORT Reference: public Value
 {
 public:
-    Reference();
+    Reference(Engine *engine);
     virtual ~Reference();
 
-    virtual const Value *value(const Context *context) const = 0;
+    Engine *engine() const;
+    virtual const Value *value(Context *context) const;
 
     // Value interface
     virtual const Reference *asReference() const;
     virtual void accept(ValueVisitor *) const;
+
+private:
+    Engine *_engine;
 };
 
 class QMLJS_EXPORT ObjectValue: public Value
@@ -506,8 +533,6 @@ public:
     ObjectValue *globalObject() const;
     const ObjectValue *mathObject() const;
 
-    void registerObject(ObjectValue *object);
-
     // prototypes
     ObjectValue *objectPrototype() const;
     ObjectValue *functionPrototype() const;
@@ -539,6 +564,8 @@ public:
     const MetaTypeSystem &metaTypeSystem() const
     { return _metaTypeSystem; }
 
+    void registerValue(Value *value); // internal
+
 private:
     void initializePrototypes();
 
@@ -575,7 +602,7 @@ private:
     NumberValue _numberValue;
     BooleanValue _booleanValue;
     StringValue _stringValue;
-    QList<ObjectValue *> _registeredObjects;
+    QList<Value *> _registeredValues;
 
     ConvertToNumber _convertToNumber;
     ConvertToString _convertToString;
@@ -585,6 +612,51 @@ private:
     MetaTypeSystem _metaTypeSystem;
 };
 
+
+// internal
+class QMLJS_EXPORT ASTObjectValue: public ObjectValue
+{
+    AST::UiQualifiedId *_typeName;
+    AST::UiObjectInitializer *_initializer;
+
+public:
+    ASTObjectValue(AST::UiQualifiedId *typeName, AST::UiObjectInitializer *initializer, Engine *engine);
+    virtual ~ASTObjectValue();
+
+    virtual void processMembers(MemberProcessor *processor) const;
+};
+
+class QMLJS_EXPORT ASTVariableReference: public Reference
+{
+    AST::VariableDeclaration *_ast;
+
+public:
+    ASTVariableReference(AST::VariableDeclaration *ast, Engine *engine);
+    virtual ~ASTVariableReference();
+
+    virtual const Value *value(Context *context) const;
+};
+
+class QMLJS_EXPORT ASTFunctionValue: public FunctionValue
+{
+    AST::FunctionDeclaration *_ast;
+    QList<NameId *> _argumentNames;
+
+public:
+    ASTFunctionValue(AST::FunctionDeclaration *ast, Engine *engine);
+    virtual ~ASTFunctionValue();
+
+    AST::FunctionDeclaration *ast() const;
+
+    virtual const Value *returnValue() const;
+    virtual int argumentCount() const;
+    virtual const Value *argument(int) const;
+    virtual QString argumentName(int index) const;
+    virtual bool isVariadic() const;
+};
+
+
+
 } } // end of namespace QmlJS::Interpreter
 
 #endif // QMLJS_INTERPRETER_H
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 5af7edf8308..6ce97d04f62 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -14,7 +14,6 @@ using namespace QmlJS::AST;
 
 Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engine *interp)
     : _snapshot(snapshot)
-    , _interp(interp)
     , _context(interp)
 {
     _docs = reachableDocuments(currentDoc, snapshot);
@@ -41,64 +40,58 @@ Context *Link::context()
     return &_context;
 }
 
-Link::ScopeChain Link::scopeChain() const
-{
-    return _scopeChain;
-}
-
 Interpreter::Engine *Link::engine()
 {
-    return _interp;
+    return _context.engine();
 }
 
 void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
 {
-    _scopeChain.clear();
+    _context.pushScope(engine()->globalObject());
 
-    if (! doc) {
-        _scopeChain.append(_interp->globalObject());
+    if (! doc)
         return;
-    }
+
+    if (doc->qmlProgram() != 0)
+        _context.setLookupMode(Context::QmlLookup);
 
     BindPtr bind = doc->bind();
 
     // Build the scope chain.
-    _scopeChain.append(_typeEnvironments.value(doc.data()));
-    _scopeChain.append(bind->_idEnvironment);
-    _scopeChain.append(bind->_functionEnvironment);
 
-    foreach (const QString &scriptFile, doc->bind()->includedScripts()) {
-        if (Document::Ptr scriptDoc = _snapshot.document(scriptFile)) {
-            if (scriptDoc->jsProgram()) {
-                _scopeChain.append(scriptDoc->bind()->_rootObjectValue);
-            }
-        }
-    }
+    // ### FIXME: May want to link to instantiating components from here.
 
+    if (bind->_rootObjectValue)
+        _context.pushScope(bind->_rootObjectValue);
 
-    if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject)) {
-        ObjectValue *scopeObject = bind->_qmlObjects.value(definition);
-        _scopeChain.append(scopeObject);
+    ObjectValue *scopeObject = 0;
+    if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
+        scopeObject = bind->_qmlObjects.value(definition);
+    else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject))
+        scopeObject = bind->_qmlObjects.value(binding);
 
-        // ### FIXME: should add the root regardless
-        if (scopeObject != bind->_rootObjectValue)
-            _scopeChain.append(bind->_rootObjectValue);
-    }
-
-    _scopeChain.append(bind->_interp.globalObject());
+    if (scopeObject && scopeObject != bind->_rootObjectValue)
+        _context.pushScope(scopeObject);
 
-    // May want to link to instantiating components from here.
-}
+    const QStringList &includedScripts = bind->includedScripts();
+    for (int index = includedScripts.size() - 1; index != -1; --index) {
+        const QString &scriptFile = includedScripts.at(index);
 
-const Value *Link::lookup(const QString &name) const
-{
-    foreach (const ObjectValue *scope, _scopeChain) {
-        if (const Value *member = scope->lookupMember(name)) {
-            return member;
+        if (Document::Ptr scriptDoc = _snapshot.document(scriptFile)) {
+            if (scriptDoc->jsProgram()) {
+                _context.pushScope(scriptDoc->bind()->_rootObjectValue);
+            }
         }
     }
 
-    return _interp->undefinedValue();
+    if (bind->_functionEnvironment)
+        _context.pushScope(bind->_functionEnvironment);
+
+    if (bind->_idEnvironment)
+        _context.pushScope(bind->_idEnvironment);
+
+    if (const ObjectValue *typeEnvironment = _typeEnvironments.value(doc.data()))
+        _context.pushScope(typeEnvironment);
 }
 
 void Link::linkImports()
@@ -106,7 +99,7 @@ void Link::linkImports()
     foreach (Document::Ptr doc, _docs) {
         BindPtr bind = doc->bind();
 
-        ObjectValue *typeEnv = _interp->newObject(/*prototype =*/0);
+        ObjectValue *typeEnv = engine()->newObject(/*prototype =*/0); // ### FIXME
         _typeEnvironments.insert(doc.data(), typeEnv);
 
         // Populate the _typeEnvironment with imports.
@@ -205,7 +198,7 @@ void Link::importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
             continue;
 
         if (directoryImport && import->importId && !importNamespace) {
-            importNamespace = _interp->newObject(/*prototype =*/0);
+            importNamespace = engine()->newObject(/*prototype =*/0);
             typeEnv->setProperty(import->importId->asString(), importNamespace);
         }
 
@@ -234,7 +227,7 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A
     ObjectValue *namespaceObject = 0;
 
     if (import->importId) { // with namespace we insert an object in the type env. to hold the imported types
-        namespaceObject = _interp->newObject(/*prototype */ 0);
+        namespaceObject = engine()->newObject(/*prototype */ 0);
         typeEnv->setProperty(import->importId->asString(), namespaceObject);
 
     } else { // without namespace we insert all types directly into the type env.
@@ -260,7 +253,7 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A
             }
         }
 #ifndef NO_DECLARATIVE_BACKEND
-        foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
+        foreach (QmlObjectValue *object, engine()->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
             namespaceObject->setProperty(object->qmlTypeName(), object);
         }
 #endif // NO_DECLARATIVE_BACKEND
diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h
index 8b622140ed0..8c346ac62be 100644
--- a/src/libs/qmljs/qmljslink.h
+++ b/src/libs/qmljs/qmljslink.h
@@ -18,23 +18,17 @@ namespace QmlJS {
 */
 class QMLJS_EXPORT Link
 {
-public:
-    typedef QList<const Interpreter::ObjectValue *> ScopeChain;
-
 public:
     // Link all documents in snapshot reachable from doc.
     Link(Document::Ptr doc, const Snapshot &snapshot, Interpreter::Engine *interp);
     ~Link();
 
     Interpreter::Context *context();
-    ScopeChain scopeChain() const;
     Interpreter::Engine *engine();
 
     // Get the scope chain for the currentObject inside doc.
     void scopeChainAt(Document::Ptr doc, AST::Node *currentObject);
 
-    const Interpreter::Value *lookup(const QString &name) const;
-
 private:
     static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
     static const Interpreter::ObjectValue *lookupType(Interpreter::ObjectValue *env, AST::UiQualifiedId *id);
@@ -51,11 +45,9 @@ private:
 
 private:
     Snapshot _snapshot;
-    Interpreter::Engine *_interp;
     Interpreter::Context _context;
     QList<Document::Ptr> _docs;
     QHash<Document *, Interpreter::ObjectValue *> _typeEnvironments;
-    ScopeChain _scopeChain;
 };
 
 } // namespace QmlJS
diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp
index 6346b372aae..fed3ee48e2c 100644
--- a/src/plugins/qmljseditor/qmlcodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp
@@ -129,12 +129,12 @@ class EnumerateProperties: private Interpreter::MemberProcessor
     QSet<const Interpreter::ObjectValue *> _processed;
     QHash<QString, const Interpreter::Value *> _properties;
     bool _globalCompletion;
-    Link *_link;
+    Interpreter::Context *_context;
 
 public:
-    EnumerateProperties(Link *link)
+    EnumerateProperties(Interpreter::Context *context)
         : _globalCompletion(false),
-          _link(link)
+          _context(context)
     {
     }
 
@@ -158,23 +158,30 @@ public:
         _processed.clear();
         _properties.clear();
 
-        foreach (const Interpreter::ObjectValue *scope, _link->scopeChain())
+        foreach (const Interpreter::ObjectValue *scope, _context->scopeChain())
             enumerateProperties(scope);
 
         return _properties;
     }
 
 private:
+    void insertProperty(const QString &name, const Interpreter::Value *value)
+    {
+        if (_context->lookupMode() == Interpreter::Context::JSLookup ||
+                ! dynamic_cast<const Interpreter::ASTVariableReference *>(value))
+            _properties.insert(name, value);
+    }
+
     virtual bool processProperty(const QString &name, const Interpreter::Value *value)
     {
-        _properties.insert(name, value);
+        insertProperty(name, value);
         return true;
     }
 
     virtual bool processEnumerator(const QString &name, const Interpreter::Value *value)
     {
         if (! _globalCompletion)
-            _properties.insert(name, value);
+            insertProperty(name, value);
         return true;
     }
 
@@ -186,14 +193,14 @@ private:
     virtual bool processSlot(const QString &name, const Interpreter::Value *value)
     {
         if (! _globalCompletion)
-            _properties.insert(name, value);
+            insertProperty(name, value);
         return true;
     }
 
     virtual bool processGeneratedSlot(const QString &name, const Interpreter::Value *value)
     {
         if (_globalCompletion)
-            _properties.insert(name, value);
+            insertProperty(name, value);
         return true;
     }
 
@@ -646,7 +653,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
 
     AST::Node *declaringMember = semanticInfo.declaringMember(editor->position());
     link.scopeChainAt(document, declaringMember);
-    Link::ScopeChain scope = link.scopeChain();
 
     // Search for the operator that triggered the completion.
     QChar completionOperator;
@@ -656,7 +662,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
             (completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
         // It's a global completion.
-        EnumerateProperties enumerateProperties(&link);
+        EnumerateProperties enumerateProperties(link.context());
         enumerateProperties.setGlobalCompletion(true);
         QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties());
         while (it.hasNext()) {
@@ -679,14 +685,14 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         //qDebug() << "expression:" << expression;
 
         if (expression  != 0) {
-            Check evaluate(&link);
+            Check evaluate(link.context());
 
             // Evaluate the expression under cursor.
             const Interpreter::Value *value = interp.convertToObject(evaluate(expression));
             //qDebug() << "type:" << interp.typeId(value);
 
             if (value && completionOperator == QLatin1Char('.')) { // member completion
-                EnumerateProperties enumerateProperties(&link);
+                EnumerateProperties enumerateProperties(link.context());
                 QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
                 while (it.hasNext()) {
                     it.next();
diff --git a/src/plugins/qmljseditor/qmlhoverhandler.cpp b/src/plugins/qmljseditor/qmlhoverhandler.cpp
index 167a7c887d0..8b94716a170 100644
--- a/src/plugins/qmljseditor/qmlhoverhandler.cpp
+++ b/src/plugins/qmljseditor/qmlhoverhandler.cpp
@@ -175,7 +175,7 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
             Link link(qmlDocument, snapshot, &interp);
             link.scopeChainAt(qmlDocument, declaringMember);
 
-            Check check(&link);
+            Check check(link.context());
             const Interpreter::Value *value = check(node);
 
             QStringList baseClasses;
-- 
GitLab