From 02b7eacf4b1884e11c2c8668080af1a3742cbf5e Mon Sep 17 00:00:00 2001 From: Christian Kamm <christian.d.kamm@nokia.com> Date: Wed, 4 May 2011 11:12:45 +0200 Subject: [PATCH] QmlJS: Fix type detection for alias properties. Task-number: QTCREATORBUG-2306 --- src/libs/qmljs/qmljs-lib.pri | 6 +- src/libs/qmljs/qmljscheck.cpp | 10 ++- src/libs/qmljs/qmljscheck.h | 3 +- src/libs/qmljs/qmljsdocument.cpp | 6 ++ src/libs/qmljs/qmljsdocument.h | 3 + src/libs/qmljs/qmljsevaluate.cpp | 1 - src/libs/qmljs/qmljsevaluate.h | 3 + src/libs/qmljs/qmljsinterpreter.cpp | 27 ++++++- src/libs/qmljs/qmljsinterpreter.h | 5 +- src/libs/qmljs/qmljslink.cpp | 1 - src/libs/qmljs/qmljslookupcontext.cpp | 15 ++-- src/libs/qmljs/qmljsscopebuilder.cpp | 15 ++-- src/libs/qmljs/qmljsscopebuilder.h | 6 +- .../designercore/model/texttomodelmerger.cpp | 10 ++- .../qmljseditor/qmljsfindreferences.cpp | 80 +------------------ .../qmljseditor/qmljssemantichighlighter.cpp | 4 +- src/plugins/qmljseditor/qmltaskmanager.cpp | 4 +- 17 files changed, 84 insertions(+), 115 deletions(-) diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index 9c5b7eec9e2..caffc29abb7 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -27,7 +27,8 @@ HEADERS += \ $$PWD/qmljsrewriter.h \ $$PWD/qmljsicons.h \ $$PWD/qmljsdelta.h \ - $$PWD/qmljstypedescriptionreader.h + $$PWD/qmljstypedescriptionreader.h \ + $$PWD/qmljsscopeastpath.h SOURCES += \ $$PWD/qmljsbind.cpp \ @@ -46,7 +47,8 @@ SOURCES += \ $$PWD/qmljsrewriter.cpp \ $$PWD/qmljsicons.cpp \ $$PWD/qmljsdelta.cpp \ - $$PWD/qmljstypedescriptionreader.cpp + $$PWD/qmljstypedescriptionreader.cpp \ + $$PWD/qmljsscopeastpath.cpp RESOURCES += \ $$PWD/qmljs.qrc diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 2842bff5492..9761fd66567 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -365,11 +365,10 @@ private: } // end of anonymous namespace -Check::Check(Document::Ptr doc, const Snapshot &snapshot, const Context *linkedContextNoScope) +Check::Check(Document::Ptr doc, const Context *linkedContextNoScope) : _doc(doc) - , _snapshot(snapshot) , _context(*linkedContextNoScope) - , _scopeBuilder(&_context, doc, snapshot) + , _scopeBuilder(&_context, doc) , _options(WarnDangerousNonStrictEqualityChecks | WarnBlocks | WarnWith | WarnVoid | WarnCommaExpression | WarnExpressionStatement | WarnAssignInCondition | WarnUseBeforeDeclaration | WarnDuplicateDeclaration @@ -844,12 +843,17 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id) if (!value) { error(id->identifierToken, Check::tr("'%1' is not a valid property name").arg(propertyName)); + return 0; } // can't look up members for attached properties if (isAttachedProperty) return 0; + // resolve references + if (const Reference *ref = value->asReference()) + value = _context.lookupReference(ref); + // member lookup const UiQualifiedId *idPart = id; while (idPart->next) { diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index c486aa3002c..cc7e72bca11 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -52,7 +52,7 @@ class QMLJS_EXPORT Check: protected AST::Visitor typedef QSet<QString> StringSet; public: - Check(Document::Ptr doc, const Snapshot &snapshot, const Interpreter::Context *linkedContextNoScope); + Check(Document::Ptr doc, const Interpreter::Context *linkedContextNoScope); virtual ~Check(); QList<DiagnosticMessage> operator()(); @@ -125,7 +125,6 @@ private: AST::Node *parent(int distance = 0); Document::Ptr _doc; - Snapshot _snapshot; Interpreter::Context _context; ScopeBuilder _scopeBuilder; diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index 467c522c74c..33cfa6e246f 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -126,9 +126,15 @@ Document::~Document() Document::Ptr Document::create(const QString &fileName) { Document::Ptr doc(new Document(fileName)); + doc->_ptr = doc; return doc; } +Document::Ptr Document::ptr() const +{ + return _ptr.toStrongRef(); +} + bool Document::isQmlDocument() const { return _isQmlDocument; diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 2c1a516cac9..529270ced77 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -64,6 +64,8 @@ public: static Document::Ptr create(const QString &fileName); + Document::Ptr ptr() const; + bool isQmlDocument() const; bool isJSDocument() const; @@ -113,6 +115,7 @@ private: QString _path; QString _componentName; QString _source; + QWeakPointer<Document> _ptr; // for documentFromSource friend class Snapshot; diff --git a/src/libs/qmljs/qmljsevaluate.cpp b/src/libs/qmljs/qmljsevaluate.cpp index 86471de684b..deca8f14065 100644 --- a/src/libs/qmljs/qmljsevaluate.cpp +++ b/src/libs/qmljs/qmljsevaluate.cpp @@ -32,7 +32,6 @@ #include "qmljsevaluate.h" #include "qmljsinterpreter.h" -#include "parser/qmljsparser_p.h" #include "parser/qmljsast_p.h" #include <QtCore/QDebug> diff --git a/src/libs/qmljs/qmljsevaluate.h b/src/libs/qmljs/qmljsevaluate.h index 7d01a1a083b..f8519f127d0 100644 --- a/src/libs/qmljs/qmljsevaluate.h +++ b/src/libs/qmljs/qmljsevaluate.h @@ -52,7 +52,10 @@ public: Evaluate(const Interpreter::Context *context); virtual ~Evaluate(); + // evaluate ast in the given context const Interpreter::Value *operator()(AST::Node *ast); + + // evaluate, but stop when encountering a Reference const Interpreter::Value *reference(AST::Node *ast); protected: diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index a060fd1c0b6..71346335633 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -36,6 +36,7 @@ #include "qmljsbind.h" #include "qmljsscopebuilder.h" #include "qmljstypedescriptionreader.h" +#include "qmljsscopeastpath.h" #include "parser/qmljsast_p.h" #include <languageutils/fakemetaobject.h> @@ -1378,8 +1379,9 @@ QList<const ObjectValue *> ScopeChain::all() const } -Context::Context() - : _engine(new Engine), +Context::Context(const Snapshot &snapshot) + : _snapshot(snapshot), + _engine(new Engine), _qmlScopeObjectIndex(-1), _qmlScopeObjectSet(false) { @@ -1395,6 +1397,11 @@ Engine *Context::engine() const return _engine.data(); } +QmlJS::Snapshot Context::snapshot() const +{ + return _snapshot; +} + const ScopeChain &Context::scopeChain() const { return _scopeChain; @@ -3270,8 +3277,20 @@ bool ASTPropertyReference::getSourceLocation(QString *fileName, int *line, int * const Value *ASTPropertyReference::value(const Context *context) const { if (_ast->expression - && (!_ast->memberType || _ast->memberType->asString() == QLatin1String("variant"))) { - Evaluate check(context); + && (!_ast->memberType || _ast->memberType->asString() == QLatin1String("variant") + || _ast->memberType->asString() == QLatin1String("alias"))) { + + // Adjust the context for the current location - expensive! + // ### Improve efficiency by caching the 'use chain' constructed in ScopeBuilder. + + QmlJS::Document::Ptr doc = _doc->ptr(); + Context localContext(*context); + QmlJS::ScopeBuilder builder(&localContext, doc); + + int offset = _ast->expression->firstSourceLocation().begin(); + builder.push(ScopeAstPath(doc)(offset)); + + Evaluate check(&localContext); return check(_ast->expression); } diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index a14158678b4..047aeea15b2 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -317,10 +317,12 @@ private: class QMLJS_EXPORT Context { public: - Context(); + Context(const Snapshot &snapshot); ~Context(); Engine *engine() const; + Snapshot snapshot() const; + const ScopeChain &scopeChain() const; ScopeChain &scopeChain(); @@ -340,6 +342,7 @@ public: private: typedef QHash<QString, const Value *> Properties; + Snapshot _snapshot; QSharedPointer<Engine> _engine; QHash<const ObjectValue *, Properties> _properties; QHash<const Document *, const TypeEnvironment *> _typeEnvironments; diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 70403625430..65639a98f8d 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -36,7 +36,6 @@ #include "qmljsdocument.h" #include "qmljsbind.h" #include "qmljscheck.h" -#include "qmljsscopebuilder.h" #include "qmljsmodelmanagerinterface.h" #include <QtCore/QFileInfo> diff --git a/src/libs/qmljs/qmljslookupcontext.cpp b/src/libs/qmljs/qmljslookupcontext.cpp index 26a5ce968ac..269c9383b04 100644 --- a/src/libs/qmljs/qmljslookupcontext.cpp +++ b/src/libs/qmljs/qmljslookupcontext.cpp @@ -43,13 +43,13 @@ class QmlJS::LookupContextData { public: LookupContextData(Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path) - : doc(doc), - snapshot(snapshot) + : context(snapshot), + doc(doc) { // since we keep the document and snapshot around, we don't need to keep the Link instance Link link(&context, snapshot, ModelManagerInterface::instance()->importPaths()); - ScopeBuilder scopeBuilder(&context, doc, snapshot); + ScopeBuilder scopeBuilder(&context, doc); scopeBuilder.push(path); } @@ -57,16 +57,15 @@ public: const Interpreter::Context &linkedContextWithoutScope, const QList<AST::Node *> &path) : context(linkedContextWithoutScope), - doc(doc), - snapshot(snapshot) + doc(doc) { - ScopeBuilder scopeBuilder(&context, doc, snapshot); + ScopeBuilder scopeBuilder(&context, doc); scopeBuilder.push(path); } Interpreter::Context context; Document::Ptr doc; - Snapshot snapshot; + // snapshot is in context }; LookupContext::LookupContext(Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path) @@ -112,7 +111,7 @@ Document::Ptr LookupContext::document() const Snapshot LookupContext::snapshot() const { - return d->snapshot; + return d->context.snapshot(); } // the engine is only guaranteed to live as long as the LookupContext diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp index 83388a69568..35e4d6d66eb 100644 --- a/src/libs/qmljs/qmljsscopebuilder.cpp +++ b/src/libs/qmljs/qmljsscopebuilder.cpp @@ -41,9 +41,8 @@ using namespace QmlJS; using namespace QmlJS::Interpreter; using namespace QmlJS::AST; -ScopeBuilder::ScopeBuilder(Context *context, Document::Ptr doc, const Snapshot &snapshot) +ScopeBuilder::ScopeBuilder(Context *context, Document::Ptr doc) : _doc(doc) - , _snapshot(snapshot) , _context(context) { initializeScopeChain(); @@ -121,25 +120,26 @@ void ScopeBuilder::initializeScopeChain() Bind *bind = _doc->bind(); QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes; + const Snapshot &snapshot = _context->snapshot(); ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain; scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain); if (_doc->qmlProgram()) { componentScopes.insert(_doc.data(), chain); - makeComponentChain(_doc, chain, &componentScopes); + makeComponentChain(_doc, snapshot, chain, &componentScopes); if (const TypeEnvironment *typeEnvironment = _context->typeEnvironment(_doc.data())) { scopeChain.qmlTypes = typeEnvironment; } } else { // add scope chains for all components that import this file - foreach (Document::Ptr otherDoc, _snapshot) { + foreach (Document::Ptr otherDoc, snapshot) { foreach (const ImportInfo &import, otherDoc->bind()->imports()) { if (import.type() == ImportInfo::FileImport && _doc->fileName() == import.name()) { ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain; componentScopes.insert(otherDoc.data(), component); chain->instantiatingComponents += component; - makeComponentChain(otherDoc, component, &componentScopes); + makeComponentChain(otherDoc, snapshot, component, &componentScopes); } } } @@ -155,6 +155,7 @@ void ScopeBuilder::initializeScopeChain() void ScopeBuilder::makeComponentChain( Document::Ptr doc, + const Snapshot &snapshot, ScopeChain::QmlComponentChain *target, QHash<Document *, ScopeChain::QmlComponentChain *> *components) { @@ -164,7 +165,7 @@ void ScopeBuilder::makeComponentChain( Bind *bind = doc->bind(); // add scopes for all components instantiating this one - foreach (Document::Ptr otherDoc, _snapshot) { + foreach (Document::Ptr otherDoc, snapshot) { if (otherDoc == doc) continue; if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) { @@ -175,7 +176,7 @@ void ScopeBuilder::makeComponentChain( components->insert(otherDoc.data(), component); target->instantiatingComponents += component; - makeComponentChain(otherDoc, component, components); + makeComponentChain(otherDoc, snapshot, component, components); } } } diff --git a/src/libs/qmljs/qmljsscopebuilder.h b/src/libs/qmljs/qmljsscopebuilder.h index 56d8647e21b..49a4ccd9872 100644 --- a/src/libs/qmljs/qmljsscopebuilder.h +++ b/src/libs/qmljs/qmljsscopebuilder.h @@ -47,7 +47,7 @@ namespace AST { class QMLJS_EXPORT ScopeBuilder { public: - ScopeBuilder(Interpreter::Context *context, Document::Ptr doc, const Snapshot &snapshot); + ScopeBuilder(Interpreter::Context *context, Document::Ptr doc); ~ScopeBuilder(); void push(AST::Node *node); @@ -58,14 +58,14 @@ public: private: void initializeScopeChain(); - void makeComponentChain(Document::Ptr doc, Interpreter::ScopeChain::QmlComponentChain *target, + void makeComponentChain(Document::Ptr doc, const Snapshot &snapshot, + Interpreter::ScopeChain::QmlComponentChain *target, QHash<Document *, Interpreter::ScopeChain::QmlComponentChain *> *components); void setQmlScopeObject(AST::Node *node); const Interpreter::Value *scopeObjectLookup(AST::UiQualifiedId *id); Document::Ptr _doc; - Snapshot _snapshot; Interpreter::Context *_context; QList<AST::Node *> _nodes; }; diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index c9daa2920c6..a033a6831eb 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -282,10 +282,10 @@ public: const QStringList importPaths) : m_snapshot(snapshot) , m_doc(doc) - , m_context(new Interpreter::Context) + , m_context(new Interpreter::Context(snapshot)) , m_link(m_context, snapshot, importPaths, doc, &m_diagnosticLinkMessages) , m_lookupContext(LookupContext::create(doc, snapshot, *m_context, QList<AST::Node*>())) - , m_scopeBuilder(m_context, doc, snapshot) + , m_scopeBuilder(m_context, doc) { } @@ -381,6 +381,10 @@ public: if (isAttachedProperty) return false; + // resolve references + if (const Interpreter::Reference *ref = value->asReference()) + value = m_context->lookupReference(ref); + // member lookup const UiQualifiedId *idPart = id; if (prefix.isEmpty()) @@ -664,7 +668,7 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH } if (view()->checkSemanticErrors()) { - Check check(doc, snapshot, m_lookupContext->context()); + Check check(doc, m_lookupContext->context()); check.setOptions(check.options() & ~Check::ErrCheckTypeErrors); foreach (const QmlJS::DiagnosticMessage &diagnosticMessage, check()) if (diagnosticMessage.isError()) diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 4ed053d82d8..496aa0c0c4d 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -47,6 +47,7 @@ #include <qmljs/qmljslink.h> #include <qmljs/qmljsevaluate.h> #include <qmljs/qmljsscopebuilder.h> +#include <qmljs/qmljsscopeastpath.h> #include <qmljs/parser/qmljsastvisitor_p.h> #include <qmljs/parser/qmljsast_p.h> @@ -80,7 +81,7 @@ public: : _doc(doc) , _snapshot(snapshot) , _context(context) - , _builder(context, doc, snapshot) + , _builder(context, doc) { } @@ -286,79 +287,6 @@ private: const ObjectValue *_scope; }; -class ScopeAstPath: protected Visitor -{ -public: - ScopeAstPath(Document::Ptr doc) - : _doc(doc) - { - } - - QList<Node *> operator()(quint32 offset) - { - _result.clear(); - _offset = offset; - if (_doc) - Node::accept(_doc->ast(), this); - return _result; - } - -protected: - void accept(AST::Node *node) - { AST::Node::acceptChild(node, this); } - - using Visitor::visit; - - virtual bool preVisit(Node *node) - { - if (Statement *stmt = node->statementCast()) { - return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation()); - } else if (ExpressionNode *exp = node->expressionCast()) { - return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation()); - } else if (UiObjectMember *ui = node->uiObjectMemberCast()) { - return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation()); - } - return true; - } - - virtual bool visit(AST::UiObjectDefinition *node) - { - _result.append(node); - Node::accept(node->initializer, this); - return false; - } - - virtual bool visit(AST::UiObjectBinding *node) - { - _result.append(node); - Node::accept(node->initializer, this); - return false; - } - - virtual bool visit(AST::FunctionDeclaration *node) - { - return visit(static_cast<FunctionExpression *>(node)); - } - - virtual bool visit(AST::FunctionExpression *node) - { - Node::accept(node->formals, this); - _result.append(node); - Node::accept(node->body, this); - return false; - } - -private: - bool containsOffset(SourceLocation start, SourceLocation end) - { - return _offset >= start.begin() && _offset <= end.end(); - } - - QList<Node *> _result; - Document::Ptr _doc; - quint32 _offset; -}; - class FindTargetExpression: protected Visitor { public: @@ -621,13 +549,13 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future, } // find the scope for the name we're searching - Context context; + Context context(snapshot); Document::Ptr doc = snapshot.document(fileName); if (!doc) return; Link link(&context, snapshot, ModelManagerInterface::instance()->importPaths()); - ScopeBuilder builder(&context, doc, snapshot); + ScopeBuilder builder(&context, doc); ScopeAstPath astPath(doc); builder.push(astPath(offset)); diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp index bdf8f06cd7a..e915493d816 100644 --- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp +++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp @@ -132,11 +132,11 @@ SemanticInfo SemanticHighlighter::semanticInfo(const SemanticHighlighterSource & semanticInfo.snapshot = snapshot; semanticInfo.document = doc; - QmlJS::Interpreter::Context *ctx = new QmlJS::Interpreter::Context; + QmlJS::Interpreter::Context *ctx = new QmlJS::Interpreter::Context(snapshot); QmlJS::Link link(ctx, snapshot, QmlJS::ModelManagerInterface::instance()->importPaths(), doc, &semanticInfo.semanticMessages); semanticInfo.m_context = QSharedPointer<const QmlJS::Interpreter::Context>(ctx); - QmlJS::Check checker(doc, snapshot, ctx); + QmlJS::Check checker(doc, ctx); semanticInfo.semanticMessages.append(checker()); return semanticInfo; diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp index 4f582d07576..679c1c76f88 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.cpp +++ b/src/plugins/qmljseditor/qmltaskmanager.cpp @@ -72,7 +72,7 @@ QmlTaskManager::QmlTaskManager(QObject *parent) : void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future, Snapshot snapshot, QStringList files, QStringList importPaths) { - Interpreter::Context ctx; + Interpreter::Context ctx(snapshot); QHash<QString, QList<DiagnosticMessage> > linkMessages; Link link(&ctx, snapshot, importPaths, &linkMessages); @@ -86,7 +86,7 @@ void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future result.messages = document->diagnosticMessages(); result.messages += linkMessages.value(fileName); - Check checker(document, snapshot, &ctx); + Check checker(document, &ctx); result.messages.append(checker()); future.reportResult(result); -- GitLab