diff options
author | Fawzi Mohamed <fawzi.mohamed@nokia.com> | 2011-06-06 11:17:12 +0200 |
---|---|---|
committer | Fawzi Mohamed <fawzi.mohamed@nokia.com> | 2011-06-06 14:08:15 +0200 |
commit | 752ea4311108a0818ca238609e315f20a6d5309a (patch) | |
tree | b4f945dbc952327095426f5d7fcdbbfc41289d53 /src/plugins/qmljseditor/qmljsfindreferences.cpp | |
parent | 994c29f6abd7b5a4d96b9cc4c9d55069a296a81b (diff) | |
download | qt-creator-752ea4311108a0818ca238609e315f20a6d5309a.tar.gz |
Qmljs: find usages for types and packages
detects and finds usages of types and packages in qml and javascript
Change-Id: Id13f48e435258ff10ab3e6f49049f7bb602a900f
Reviewed-on: http://codereview.qt.nokia.com/277
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Christian Kamm <christian.d.kamm@nokia.com>
Diffstat (limited to 'src/plugins/qmljseditor/qmljsfindreferences.cpp')
-rw-r--r-- | src/plugins/qmljseditor/qmljsfindreferences.cpp | 298 |
1 files changed, 272 insertions, 26 deletions
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 57c120ad1b..26e9bbc27e 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -288,9 +288,155 @@ private: const ObjectValue *_scope; }; +class FindTypeUsages: protected Visitor +{ +public: + typedef QList<AST::SourceLocation> Result; + + FindTypeUsages(Document::Ptr doc, Context *context) + : _doc(doc) + , _context(context) + , _builder(context, doc) + { + _builder.initializeRootScope(); + } + + Result operator()(const QString &name, const ObjectValue *typeValue) + { + _name = name; + _typeValue = typeValue; + _usages.clear(); + if (_doc) + Node::accept(_doc->ast(), this); + return _usages; + } + +protected: + void accept(AST::Node *node) + { AST::Node::acceptChild(node, this); } + + using Visitor::visit; + + virtual bool visit(AST::UiPublicMember *node) + { + if (node->memberType && node->memberType->asString() == _name){ + const ObjectValue * tVal = _context->lookupType(_doc.data(), QStringList(_name)); + if (tVal == _typeValue) + _usages.append(node->typeToken); + } + return true; + } + + virtual bool visit(AST::UiObjectDefinition *node) + { + checkTypeName(node->qualifiedTypeNameId); + _builder.push(node); + Node::accept(node->initializer, this); + _builder.pop(); + return false; + } + + virtual bool visit(AST::UiObjectBinding *node) + { + checkTypeName(node->qualifiedTypeNameId); + _builder.push(node); + Node::accept(node->initializer, this); + _builder.pop(); + return false; + } + + virtual bool visit(AST::IdentifierExpression *node) + { + if (!node->name || node->name->asString() != _name) + return false; + + const ObjectValue *scope; + const Value *objV = _context->lookup(_name, &scope); + if (objV == _typeValue) + _usages.append(node->identifierToken); + return false; + } + + virtual bool visit(AST::FieldMemberExpression *node) + { + if (!node->name || node->name->asString() != _name) + return true; + Evaluate evaluate(_context); + const Value *lhsValue = evaluate(node->base); + if (!lhsValue) + return true; + const ObjectValue *lhsObj = lhsValue->asObjectValue(); + if (lhsObj && lhsObj->lookupMember(_name, _context) == _typeValue) + _usages.append(node->identifierToken); + return true; + } + + virtual bool visit(AST::FunctionDeclaration *node) + { + return visit(static_cast<FunctionExpression *>(node)); + } + + virtual bool visit(AST::FunctionExpression *node) + { + Node::accept(node->formals, this); + _builder.push(node); + Node::accept(node->body, this); + _builder.pop(); + return false; + } + + virtual bool visit(AST::VariableDeclaration *node) + { + Node::accept(node->expression, this); + return false; + } + + virtual bool visit(UiImport *ast) + { + if (ast && ast->importId && ast->importId->asString() == _name) { + const Imports *imp = _context->imports(_doc.data()); + if (!imp) + return false; + if (_context->lookupType(_doc.data(), QStringList(_name)) == _typeValue) + _usages.append(ast->importIdToken); + } + return false; + } + + +private: + bool checkTypeName(UiQualifiedId *id) + { + for (UiQualifiedId *att = id; att; att = att->next){ + if (att->name && att->name->asString() == _name) { + const ObjectValue *objectValue = _context->lookupType(_doc.data(), id, att->next); + if (_typeValue == objectValue){ + _usages.append(att->identifierToken); + return true; + } + } + } + return false; + } + + Result _usages; + + Document::Ptr _doc; + Context *_context; + ScopeBuilder _builder; + + QString _name; + const ObjectValue *_typeValue; +}; + class FindTargetExpression: protected Visitor { public: + enum Kind { + ExpKind, + TypeKind + }; + FindTargetExpression(Document::Ptr doc, Context *context) : _doc(doc), _context(context) { @@ -302,6 +448,7 @@ public: _scope = 0; _objectNode = 0; _offset = offset; + _typeKind = ExpKind; if (_doc) Node::accept(_doc->ast(), this); } @@ -316,6 +463,14 @@ public: return _scope; } + Kind typeKind(){ + return _typeKind; + } + + const Value *targetValue(){ + return _targetValue; + } + protected: void accept(AST::Node *node) { AST::Node::acceptChild(node, this); } @@ -336,8 +491,15 @@ protected: virtual bool visit(IdentifierExpression *node) { - if (containsOffset(node->identifierToken)) + if (containsOffset(node->identifierToken)) { _name = node->name->asString(); + if ((!_name.isEmpty()) && _name.at(0).isUpper()) { + // a possible type + _targetValue = _context->lookupType(_doc.data(), QStringList(_name)); + _scope = 0; + _typeKind = TypeKind; + } + } return true; } @@ -346,6 +508,19 @@ protected: if (containsOffset(node->identifierToken)) { setScope(node->base); _name = node->name->asString(); + if ((!_name.isEmpty()) && _name.at(0).isUpper()) { + // a possible type + Evaluate evaluate(_context); + const Value *lhsValue = evaluate(node->base); + if (!lhsValue) + return true; + const ObjectValue *lhsObj = lhsValue->asObjectValue(); + if (lhsObj) { + _scope = lhsObj; + _targetValue = value_cast<const ObjectValue*>(lhsObj->lookupMember(_name, _context)); + _typeKind = TypeKind; + } + } return false; } return true; @@ -363,7 +538,8 @@ protected: virtual bool visit(UiObjectBinding *node) { - if (!checkBindingName(node->qualifiedId)) { + if ((!checkTypeName(node->qualifiedTypeNameId)) && + (!checkBindingName(node->qualifiedId))) { Node *oldObjectNode = _objectNode; _objectNode = node; accept(node->initializer); @@ -374,16 +550,26 @@ protected: virtual bool visit(UiObjectDefinition *node) { - Node *oldObjectNode = _objectNode; - _objectNode = node; - accept(node->initializer); - _objectNode = oldObjectNode; + if (!checkTypeName(node->qualifiedTypeNameId)) { + Node *oldObjectNode = _objectNode; + _objectNode = node; + accept(node->initializer); + _objectNode = oldObjectNode; + } return false; } virtual bool visit(UiPublicMember *node) { - if (containsOffset(node->identifierToken)) { + if (containsOffset(node->typeToken)){ + if (node->memberType){ + _name = node->memberType->asString(); + _targetValue = _context->lookupType(_doc.data(), QStringList(_name)); + _scope = 0; + _typeKind = TypeKind; + } + return false; + } else if (containsOffset(node->identifierToken)) { _scope = _doc->bind()->findQmlObject(_objectNode); _name = node->name->asString(); return false; @@ -435,6 +621,20 @@ private: return false; } + bool checkTypeName(UiQualifiedId *id) + { + for (UiQualifiedId *att = id; att; att = att->next) { + if (att->name && containsOffset(att->identifierToken)) { + _targetValue = _context->lookupType(_doc.data(), id, att->next); + _scope = 0; + _name = att->name->asString(); + _typeKind = TypeKind; + return true; + } + } + return false; + } + void setScope(Node *node) { Evaluate evaluate(_context); @@ -445,12 +645,23 @@ private: QString _name; const ObjectValue *_scope; + const Value *_targetValue; Node *_objectNode; Document::Ptr _doc; Context *_context; quint32 _offset; + Kind _typeKind; }; +static QString matchingLine(unsigned position, const QString &source) +{ + int start = source.lastIndexOf(QLatin1Char('\n'), position); + start += 1; + int end = source.indexOf(QLatin1Char('\n'), position); + + return source.mid(start, end - start); +} + class ProcessFile: public std::unary_function<QString, QList<FindReferences::Usage> > { const Snapshot &snapshot; @@ -485,14 +696,39 @@ public: return usages; } +}; - static QString matchingLine(unsigned position, const QString &source) +class SearchFileForType: public std::unary_function<QString, QList<FindReferences::Usage> > +{ + const Context &context; + typedef FindReferences::Usage Usage; + QString name; + const ObjectValue *scope; + +public: + SearchFileForType(const Context &context, + QString name, + const ObjectValue *scope) + : context(context), name(name), scope(scope) + { } + + QList<Usage> operator()(const QString &fileName) { - int start = source.lastIndexOf(QLatin1Char('\n'), position); - start += 1; - int end = source.indexOf(QLatin1Char('\n'), position); + QList<Usage> usages; - return source.mid(start, end - start); + Document::Ptr doc = context.snapshot().document(fileName); + if (!doc) + return usages; + + Context contextCopy(context); + + // find all idenfifier expressions, try to resolve them and check if the result is in scope + FindTypeUsages findUsages(doc, &contextCopy); + FindTypeUsages::Result results = findUsages(name, scope); + foreach (const AST::SourceLocation &loc, results) + usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length)); + + return usages; } }; @@ -569,17 +805,6 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future, if (name.isEmpty()) return; - const ObjectValue *scope = findTarget.scope(); - if (!scope) - return; - scope->lookupMember(name, &context, &scope); - if (!scope) - return; - - // report a dummy usage to indicate the search is starting - FindReferences::Usage usage; - future.reportResult(usage); - QStringList files; foreach (const Document::Ptr &doc, snapshot) { // ### skip files that don't contain the name token @@ -588,11 +813,32 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future, future.setProgressRange(0, files.size()); - ProcessFile process(snapshot, context, name, scope); - UpdateUI reduce(&future); + if (findTarget.typeKind() == findTarget.TypeKind){ + const ObjectValue *typeValue = value_cast<const ObjectValue*>(findTarget.targetValue()); + if (!typeValue) + return; + // report a dummy usage to indicate the search is starting + future.reportResult(FindReferences::Usage()); + + SearchFileForType process(context, name, typeValue); + UpdateUI reduce(&future); - QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce); + QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce); + } else { + const ObjectValue *scope = findTarget.scope(); + if (!scope) + return; + scope->lookupMember(name, &context, &scope); + if (!scope) + return; + // report a dummy usage to indicate the search is starting + future.reportResult(FindReferences::Usage()); + ProcessFile process(snapshot, context, name, scope); + UpdateUI reduce(&future); + + QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce); + } future.setProgressValue(files.size()); } |