OSDN Git Service

QmlJS: Fix type detection for alias properties.
authorChristian Kamm <christian.d.kamm@nokia.com>
Wed, 4 May 2011 09:12:45 +0000 (11:12 +0200)
committerChristian Kamm <christian.d.kamm@nokia.com>
Wed, 4 May 2011 09:14:35 +0000 (11:14 +0200)
Task-number: QTCREATORBUG-2306

17 files changed:
src/libs/qmljs/qmljs-lib.pri
src/libs/qmljs/qmljscheck.cpp
src/libs/qmljs/qmljscheck.h
src/libs/qmljs/qmljsdocument.cpp
src/libs/qmljs/qmljsdocument.h
src/libs/qmljs/qmljsevaluate.cpp
src/libs/qmljs/qmljsevaluate.h
src/libs/qmljs/qmljsinterpreter.cpp
src/libs/qmljs/qmljsinterpreter.h
src/libs/qmljs/qmljslink.cpp
src/libs/qmljs/qmljslookupcontext.cpp
src/libs/qmljs/qmljsscopebuilder.cpp
src/libs/qmljs/qmljsscopebuilder.h
src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
src/plugins/qmljseditor/qmljsfindreferences.cpp
src/plugins/qmljseditor/qmljssemantichighlighter.cpp
src/plugins/qmljseditor/qmltaskmanager.cpp

index 9c5b7ee..caffc29 100644 (file)
@@ -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
index 2842bff..9761fd6 100644 (file)
@@ -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) {
index c486aa3..cc7e72b 100644 (file)
@@ -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;
index 467c522..33cfa6e 100644 (file)
@@ -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;
index 2c1a516..529270c 100644 (file)
@@ -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;
index 86471de..deca8f1 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "qmljsevaluate.h"
 #include "qmljsinterpreter.h"
-#include "parser/qmljsparser_p.h"
 #include "parser/qmljsast_p.h"
 #include <QtCore/QDebug>
 
index 7d01a1a..f8519f1 100644 (file)
@@ -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:
index a060fd1..7134633 100644 (file)
@@ -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);
     }
 
index a141586..047aeea 100644 (file)
@@ -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;
index 7040362..65639a9 100644 (file)
@@ -36,7 +36,6 @@
 #include "qmljsdocument.h"
 #include "qmljsbind.h"
 #include "qmljscheck.h"
-#include "qmljsscopebuilder.h"
 #include "qmljsmodelmanagerinterface.h"
 
 #include <QtCore/QFileInfo>
index 26a5ce9..269c938 100644 (file)
@@ -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
index 83388a6..35e4d6d 100644 (file)
@@ -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);
             }
         }
     }
index 56d8647..49a4ccd 100644 (file)
@@ -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;
 };
index c9daa29..a033a68 100644 (file)
@@ -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())
index 4ed053d..496aa0c 100644 (file)
@@ -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));
 
index bdf8f06..e915493 100644 (file)
@@ -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;
index 4f582d0..679c1c7 100644 (file)
@@ -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);