From 08cfc8f28c1aaae1670fa96fc15e4abe00d72e18 Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Fri, 19 Feb 2010 12:25:26 +0100
Subject: [PATCH] Pass in the whole AST path for scope creation.

Just the declaring member isn't enough.
---
 src/libs/qmljs/qmljscheck.cpp                 |  5 +-
 src/libs/qmljs/qmljsinterpreter.cpp           |  4 +-
 src/libs/qmljs/qmljsinterpreter.h             |  2 +-
 src/libs/qmljs/qmljslink.cpp                  | 52 +++++++++++++------
 src/libs/qmljs/qmljslink.h                    |  2 +-
 .../qmljseditor/qmljscodecompletion.cpp       |  4 +-
 src/plugins/qmljseditor/qmljseditor.cpp       |  2 +-
 src/plugins/qmljseditor/qmljshoverhandler.cpp |  4 +-
 8 files changed, 46 insertions(+), 29 deletions(-)

diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 33392da27c4..383ab9ee008 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -76,11 +76,10 @@ QList<DiagnosticMessage> Check::operator()()
     return _messages;
 }
 
-bool Check::visit(UiProgram *ast)
+bool Check::visit(UiProgram *)
 {
     // build the initial scope chain
-    if (ast->members && ast->members->member)
-        _link.scopeChainAt(_doc, ast->members->member);
+    _link.scopeChainAt(_doc);
 
     return true;
 }
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index f7dc0c5393d..2ea8eb71b62 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -776,10 +776,10 @@ Context::~Context()
 {
 }
 
-void Context::build(Node *node, QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot)
+void Context::build(const QList<AST::Node *> &astPath, Document::Ptr doc, const Snapshot &snapshot)
 {
     Link link(this, doc, snapshot);
-    link.scopeChainAt(doc, node);
+    link.scopeChainAt(doc, astPath);
 }
 
 Engine *Context::engine() const
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index f91add8847e..91e60573c4e 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -264,7 +264,7 @@ public:
     Context(Engine *engine);
     ~Context();
 
-    void build(AST::Node *node, const Document::Ptr doc, const Snapshot &snapshot);
+    void build(const QList<AST::Node *> &astPath, const Document::Ptr doc, const Snapshot &snapshot);
 
     Engine *engine() const;
     const ScopeChain &scopeChain() const;
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 6080ff1168c..b41c7b11ccd 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -29,38 +29,52 @@ Interpreter::Engine *Link::engine()
     return _context->engine();
 }
 
-void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
+void Link::scopeChainAt(Document::Ptr doc, const QList<Node *> &astPath)
 {
     ScopeChain &scopeChain = _context->scopeChain();
 
     // ### TODO: This object ought to contain the global namespace additions by QML.
     scopeChain.globalScope = engine()->globalObject();
 
-    if (! doc)
+    if (! doc) {
+        scopeChain.update();
         return;
+    }
 
     Bind *bind = doc->bind();
     QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes;
 
+    int qmlScopeObjectIndex = -1;
+
     if (doc->qmlProgram()) {
         _context->setLookupMode(Context::QmlLookup);
 
         makeComponentChain(doc, &scopeChain.qmlComponentScope, &componentScopes);
 
-        ObjectValue *scopeObject = 0;
-        if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
-            scopeObject = bind->findQmlObject(definition);
-        else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject))
-            scopeObject = bind->findQmlObject(binding);
-
-        if (scopeObject && scopeObject != scopeChain.qmlComponentScope.rootObject)
-            scopeChain.qmlScopeObjects += scopeObject;
+        // find the last object definition or object binding: that is the scope object
+        for (int i = astPath.size() - 1; i >= 0; --i) {
+            Node *node = astPath.at(i);
+
+            ObjectValue *scopeObject = 0;
+            if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(node))
+                scopeObject = bind->findQmlObject(definition);
+            else if (UiObjectBinding *binding = cast<UiObjectBinding *>(node))
+                scopeObject = bind->findQmlObject(binding);
+
+            if (scopeObject) {
+                if (scopeObject != scopeChain.qmlComponentScope.rootObject)
+                    scopeChain.qmlScopeObjects += scopeObject;
+                qmlScopeObjectIndex = i;
+                break;
+            }
+        }
 
         if (const ObjectValue *typeEnvironment = _context->typeEnvironment(doc.data()))
             scopeChain.qmlTypes = typeEnvironment;
     } else {
         // the global scope of a js file does not see the instantiating component
-        if (currentObject != 0) {
+        qDebug() << "ast path length: " << astPath.size();
+        if (astPath.size() != 0) {
             // add scope chains for all components that source this document
             foreach (Document::Ptr otherDoc, _docs) {
                 if (otherDoc->bind()->includedScripts().contains(doc->fileName())) {
@@ -77,13 +91,17 @@ void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
         scopeChain.jsScopes += bind->rootObjectValue();
     }
 
-    if (FunctionDeclaration *fun = cast<FunctionDeclaration *>(currentObject)) {
-        ObjectValue *activation = engine()->newObject(/*prototype = */ 0);
-        for (FormalParameterList *it = fun->formals; it; it = it->next) {
-            if (it->name)
-                activation->setProperty(it->name->asString(), engine()->undefinedValue());
+    for (int i = qmlScopeObjectIndex + 1; i < astPath.size(); ++i) {
+        Node *node = astPath.at(i);
+
+        if (FunctionDeclaration *fun = cast<FunctionDeclaration *>(node)) {
+            ObjectValue *activation = engine()->newObject(/*prototype = */ 0);
+            for (FormalParameterList *it = fun->formals; it; it = it->next) {
+                if (it->name)
+                    activation->setProperty(it->name->asString(), engine()->undefinedValue());
+            }
+            scopeChain.jsScopes += activation;
         }
-        scopeChain.jsScopes += activation;
     }
 
     scopeChain.update();
diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h
index ba767523e20..9bea65b0ea4 100644
--- a/src/libs/qmljs/qmljslink.h
+++ b/src/libs/qmljs/qmljslink.h
@@ -23,7 +23,7 @@ public:
     ~Link();
 
     // Get the scope chain for the currentObject inside doc.
-    void scopeChainAt(Document::Ptr doc, AST::Node *currentObject);
+    void scopeChainAt(Document::Ptr doc, const QList<AST::Node *> &astPath = QList<AST::Node *>());
 
 private:
     Interpreter::Engine *engine();
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp
index 5d00f7a65ac..46e91114c05 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp
@@ -670,8 +670,8 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     Interpreter::Context context(&interp);
 
     // Set up the current scope chain.
-    AST::Node *declaringMember = semanticInfo.declaringMember(editor->position());
-    context.build(declaringMember, document, snapshot);
+    QList<AST::Node *> astPath = semanticInfo.astPath(editor->position());
+    context.build(astPath , document, snapshot);
 
     // Search for the operator that triggered the completion.
     QChar completionOperator;
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index c0ebfcf273b..4f6b32ca103 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -904,7 +904,7 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &
 
     Interpreter::Engine interp;
     Interpreter::Context context(&interp);
-    context.build(semanticInfo.declaringMember(cursorPosition), semanticInfo.document, semanticInfo.snapshot);
+    context.build(semanticInfo.astPath(cursorPosition), semanticInfo.document, semanticInfo.snapshot);
 
     Evaluate check(&context);
     const Interpreter::Value *value = check.reference(node);
diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp
index 9f06b668782..0a561b79720 100644
--- a/src/plugins/qmljseditor/qmljshoverhandler.cpp
+++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp
@@ -168,11 +168,11 @@ void HoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int p
     if (m_helpId.isEmpty() && m_toolTip.isEmpty()) {
         AST::Node *node = semanticInfo.nodeUnderCursor(pos);
         if (node && !(AST::cast<AST::StringLiteral *>(node) != 0 || AST::cast<AST::NumericLiteral *>(node) != 0)) {
-            AST::Node *declaringMember = semanticInfo.declaringMember(pos);
+            QList<AST::Node *> astPath = semanticInfo.astPath(pos);
 
             Interpreter::Engine interp;
             Interpreter::Context context(&interp);
-            context.build(declaringMember, qmlDocument, snapshot);
+            context.build(astPath, qmlDocument, snapshot);
 
             Evaluate check(&context);
             const Interpreter::Value *value = check(node);
-- 
GitLab