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