$$PWD/qmljsrewriter.h \
$$PWD/qmljsicons.h \
$$PWD/qmljsdelta.h \
- $$PWD/qmljstypedescriptionreader.h
+ $$PWD/qmljstypedescriptionreader.h \
+ $$PWD/qmljsscopeastpath.h
SOURCES += \
$$PWD/qmljsbind.cpp \
$$PWD/qmljsrewriter.cpp \
$$PWD/qmljsicons.cpp \
$$PWD/qmljsdelta.cpp \
- $$PWD/qmljstypedescriptionreader.cpp
+ $$PWD/qmljstypedescriptionreader.cpp \
+ $$PWD/qmljsscopeastpath.cpp
RESOURCES += \
$$PWD/qmljs.qrc
} // 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
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) {
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()();
AST::Node *parent(int distance = 0);
Document::Ptr _doc;
- Snapshot _snapshot;
Interpreter::Context _context;
ScopeBuilder _scopeBuilder;
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;
static Document::Ptr create(const QString &fileName);
+ Document::Ptr ptr() const;
+
bool isQmlDocument() const;
bool isJSDocument() const;
QString _path;
QString _componentName;
QString _source;
+ QWeakPointer<Document> _ptr;
// for documentFromSource
friend class Snapshot;
#include "qmljsevaluate.h"
#include "qmljsinterpreter.h"
-#include "parser/qmljsparser_p.h"
#include "parser/qmljsast_p.h"
#include <QtCore/QDebug>
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:
#include "qmljsbind.h"
#include "qmljsscopebuilder.h"
#include "qmljstypedescriptionreader.h"
+#include "qmljsscopeastpath.h"
#include "parser/qmljsast_p.h"
#include <languageutils/fakemetaobject.h>
}
-Context::Context()
- : _engine(new Engine),
+Context::Context(const Snapshot &snapshot)
+ : _snapshot(snapshot),
+ _engine(new Engine),
_qmlScopeObjectIndex(-1),
_qmlScopeObjectSet(false)
{
return _engine.data();
}
+QmlJS::Snapshot Context::snapshot() const
+{
+ return _snapshot;
+}
+
const ScopeChain &Context::scopeChain() const
{
return _scopeChain;
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);
}
class QMLJS_EXPORT Context
{
public:
- Context();
+ Context(const Snapshot &snapshot);
~Context();
Engine *engine() const;
+ Snapshot snapshot() const;
+
const ScopeChain &scopeChain() const;
ScopeChain &scopeChain();
private:
typedef QHash<QString, const Value *> Properties;
+ Snapshot _snapshot;
QSharedPointer<Engine> _engine;
QHash<const ObjectValue *, Properties> _properties;
QHash<const Document *, const TypeEnvironment *> _typeEnvironments;
#include "qmljsdocument.h"
#include "qmljsbind.h"
#include "qmljscheck.h"
-#include "qmljsscopebuilder.h"
#include "qmljsmodelmanagerinterface.h"
#include <QtCore/QFileInfo>
{
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);
}
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)
Snapshot LookupContext::snapshot() const
{
- return d->snapshot;
+ return d->context.snapshot();
}
// the engine is only guaranteed to live as long as the LookupContext
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();
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);
}
}
}
void ScopeBuilder::makeComponentChain(
Document::Ptr doc,
+ const Snapshot &snapshot,
ScopeChain::QmlComponentChain *target,
QHash<Document *, ScopeChain::QmlComponentChain *> *components)
{
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)) {
components->insert(otherDoc.data(), component);
target->instantiatingComponents += component;
- makeComponentChain(otherDoc, component, components);
+ makeComponentChain(otherDoc, snapshot, component, components);
}
}
}
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);
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;
};
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)
{
}
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())
}
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())
#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>
: _doc(doc)
, _snapshot(snapshot)
, _context(context)
- , _builder(context, doc, snapshot)
+ , _builder(context, doc)
{
}
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:
}
// 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));
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;
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);
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);