From 33a130e2220054427c1c5539d5ebf108bbf75cdc Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Thu, 23 Jun 2011 10:25:43 +0200
Subject: [PATCH] QmlJS: Fix completion inside grouped propery bindings.

Task-number: QTCREATORBUG-3541
Change-Id: Ida8e59b65836c8515fbfbd2a9e4737d9ae04e76c
Reviewed-on: http://codereview.qt.nokia.com/639
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
---
 src/libs/qmljs/qmljsbind.cpp                  | 18 ++++++++-------
 src/libs/qmljs/qmljscheck.cpp                 |  6 ++---
 .../qmljseditor/qmljscompletionassist.cpp     | 23 ++++++++++++++++++-
 3 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp
index acf87e5f8dd..fb4291cd695 100644
--- a/src/libs/qmljs/qmljsbind.cpp
+++ b/src/libs/qmljs/qmljsbind.cpp
@@ -308,17 +308,18 @@ bool Bind::visit(UiObjectDefinition *ast)
 {
     // an UiObjectDefinition may be used to group property bindings
     // think anchors { ... }
-    bool isGroupedBinding = false;
-    for (UiQualifiedId *it = ast->qualifiedTypeNameId; it; it = it->next) {
-        if (!it->next && it->name)
-            isGroupedBinding = it->name->asString().at(0).isLower();
-    }
+    bool isGroupedBinding = ast->qualifiedTypeNameId
+            && ast->qualifiedTypeNameId->name
+            && ast->qualifiedTypeNameId->name->asString().at(0).isLower();
 
     if (!isGroupedBinding) {
         ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer);
         _qmlObjects.insert(ast, value);
     } else {
         _groupedPropertyBindings.insert(ast);
+        Interpreter::ObjectValue *oldObjectValue = switchObjectValue(0);
+        accept(ast->initializer);
+        switchObjectValue(oldObjectValue);
     }
 
     return false;
@@ -337,7 +338,7 @@ bool Bind::visit(UiObjectBinding *ast)
 
 bool Bind::visit(UiScriptBinding *ast)
 {
-    if (toString(ast->qualifiedId) == QLatin1String("id")) {
+    if (_currentObjectValue && toString(ast->qualifiedId) == QLatin1String("id")) {
         if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement))
             if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression))
                 if (i->name)
@@ -369,7 +370,8 @@ bool Bind::visit(VariableDeclaration *ast)
         return false;
 
     ASTVariableReference *ref = new ASTVariableReference(ast, &_engine);
-    _currentObjectValue->setMember(ast->name->asString(), ref);
+    if (_currentObjectValue)
+        _currentObjectValue->setMember(ast->name->asString(), ref);
     return true;
 }
 
@@ -380,7 +382,7 @@ bool Bind::visit(FunctionExpression *ast)
     //    return false;
 
     ASTFunctionValue *function = new ASTFunctionValue(ast, _doc, &_engine);
-    if (ast->name && cast<FunctionDeclaration *>(ast))
+    if (_currentObjectValue && ast->name && cast<FunctionDeclaration *>(ast))
         _currentObjectValue->setMember(ast->name->asString(), function);
 
     // build function scope
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index be1ce15c1c3..1639c5dcd0f 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -461,9 +461,9 @@ bool Check::visit(UiObjectBinding *ast)
 void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                            UiObjectInitializer *initializer)
 {
-    // If the 'typeId' starts with a lower-case letter, it doesn't define
-    // a new object instance. For instance: anchors { ... }
-    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
+    // Don't do type checks if it's a grouped property binding.
+    // For instance: anchors { ... }
+    if (_doc->bind()->isGroupedPropertyBinding(ast)) {
         checkScopeObjectMember(typeId);
         // ### don't give up!
         return;
diff --git a/src/plugins/qmljseditor/qmljscompletionassist.cpp b/src/plugins/qmljseditor/qmljscompletionassist.cpp
index 2e09bfc4d6c..017fffeeda7 100644
--- a/src/plugins/qmljseditor/qmljscompletionassist.cpp
+++ b/src/plugins/qmljseditor/qmljscompletionassist.cpp
@@ -433,8 +433,10 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface
 
     const Interpreter::ObjectValue *qmlScopeType = 0;
     if (contextFinder.isInQmlContext()) {
+        // find the enclosing qml object
         // ### this should use semanticInfo.declaringMember instead, but that may also return functions
-        for (int i = path.size() - 1; i >= 0; --i) {
+        int i;
+        for (i = path.size() - 1; i >= 0; --i) {
             AST::Node *node = path[i];
             if (AST::cast<AST::UiObjectDefinition *>(node) || AST::cast<AST::UiObjectBinding *>(node)) {
                 qmlScopeType = document->bind()->findQmlObject(node);
@@ -442,6 +444,25 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface
                     break;
             }
         }
+        // grouped property bindings change the scope type
+        for (i++; i < path.size(); ++i) {
+            AST::UiObjectDefinition *objDef = AST::cast<AST::UiObjectDefinition *>(path[i]);
+            if (!objDef || !document->bind()->isGroupedPropertyBinding(objDef))
+                break;
+            const Interpreter::ObjectValue *newScopeType = qmlScopeType;
+            for (AST::UiQualifiedId *it = objDef->qualifiedTypeNameId; it; it = it->next) {
+                if (!newScopeType || !it->name) {
+                    newScopeType = 0;
+                    break;
+                }
+                const Interpreter::Value *v = newScopeType->lookupMember(it->name->asString(), context);
+                v = context->lookupReference(v);
+                newScopeType = Interpreter::value_cast<const Interpreter::ObjectValue *>(v);
+            }
+            if (!newScopeType)
+                break;
+            qmlScopeType = newScopeType;
+        }
         // fallback to getting the base type object
         if (!qmlScopeType)
             qmlScopeType = context->lookupType(document.data(), contextFinder.qmlObjectTypeName());
-- 
GitLab