2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2010-09-24 14:05:34 +02:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2010-09-24 14:05:34 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2010-09-24 14:05:34 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:57:40 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2010-09-24 14:05:34 +02:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2010-12-17 16:01:08 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
#include "qmljsfindreferences.h"
|
|
|
|
|
|
|
|
|
|
#include <texteditor/basefilefind.h>
|
2014-01-13 16:17:34 +01:00
|
|
|
#include <coreplugin/find/searchresultwindow.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
#include <utils/filesearch.h>
|
|
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
|
|
|
|
#include <coreplugin/progressmanager/futureprogress.h>
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
|
|
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
|
|
|
|
#include <qmljs/qmljsbind.h>
|
|
|
|
|
#include <qmljs/qmljslink.h>
|
|
|
|
|
#include <qmljs/qmljsevaluate.h>
|
|
|
|
|
#include <qmljs/qmljsscopebuilder.h>
|
2011-05-04 11:12:45 +02:00
|
|
|
#include <qmljs/qmljsscopeastpath.h>
|
2011-07-01 13:51:53 +02:00
|
|
|
#include <qmljs/qmljscontext.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
#include <qmljs/parser/qmljsastvisitor_p.h>
|
|
|
|
|
#include <qmljs/parser/qmljsast_p.h>
|
2011-09-09 10:55:11 +02:00
|
|
|
#include <qmljstools/qmljsmodelmanager.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
#include "qmljseditorconstants.h"
|
|
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QTime>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
#include <QtConcurrentRun>
|
|
|
|
|
#include <QtConcurrentMap>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QLabel>
|
2012-02-09 09:35:03 +01:00
|
|
|
#include <utils/runextensions.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
2013-08-30 09:22:42 +02:00
|
|
|
using namespace Core;
|
2010-09-24 14:05:34 +02:00
|
|
|
using namespace QmlJS;
|
|
|
|
|
using namespace QmlJS::AST;
|
|
|
|
|
using namespace QmlJSEditor;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// ### These visitors could be useful in general
|
|
|
|
|
|
|
|
|
|
class FindUsages: protected Visitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2018-11-24 02:45:30 +01:00
|
|
|
using Result = QList<AST::SourceLocation>;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
FindUsages(Document::Ptr doc, const ContextPtr &context)
|
2010-09-24 14:05:34 +02:00
|
|
|
: _doc(doc)
|
2011-07-12 14:55:27 +02:00
|
|
|
, _scopeChain(doc, context)
|
|
|
|
|
, _builder(&_scopeChain)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result operator()(const QString &name, const ObjectValue *scope)
|
|
|
|
|
{
|
|
|
|
|
_name = name;
|
|
|
|
|
_scope = scope;
|
|
|
|
|
_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;
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiPublicMember *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name == _name
|
2011-07-12 14:55:27 +02:00
|
|
|
&& _scopeChain.qmlScopeObjects().contains(_scope)) {
|
2010-09-24 14:05:34 +02:00
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
}
|
2011-06-06 14:13:50 +02:00
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectDefinition *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (node->qualifiedId
|
|
|
|
|
&& !node->qualifiedId->next
|
2011-09-13 09:57:24 +02:00
|
|
|
&& node->qualifiedId->name == _name
|
2010-09-24 14:05:34 +02:00
|
|
|
&& checkQmlScope()) {
|
|
|
|
|
_usages.append(node->qualifiedId->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiScriptBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (node->qualifiedId
|
|
|
|
|
&& !node->qualifiedId->next
|
2011-09-13 09:57:24 +02:00
|
|
|
&& node->qualifiedId->name == _name
|
2010-09-24 14:05:34 +02:00
|
|
|
&& checkQmlScope()) {
|
|
|
|
|
_usages.append(node->qualifiedId->identifierToken);
|
|
|
|
|
}
|
2011-06-06 14:13:50 +02:00
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
Node::accept(node->qualifiedId, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiArrayBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (node->qualifiedId
|
|
|
|
|
&& !node->qualifiedId->next
|
2011-09-13 09:57:24 +02:00
|
|
|
&& node->qualifiedId->name == _name
|
2010-09-24 14:05:34 +02:00
|
|
|
&& checkQmlScope()) {
|
|
|
|
|
_usages.append(node->qualifiedId->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::IdentifierExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name.isEmpty() || node->name != _name)
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const ObjectValue *scope;
|
2011-07-12 14:55:27 +02:00
|
|
|
_scopeChain.lookup(_name, &scope);
|
2010-09-24 14:05:34 +02:00
|
|
|
if (!scope)
|
|
|
|
|
return false;
|
|
|
|
|
if (check(scope)) {
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the order of scopes in 'instantiatingComponents' is undefined,
|
|
|
|
|
// so it might still be a use - we just found a different value in a different scope first
|
|
|
|
|
|
|
|
|
|
// if scope is one of these, our match wasn't inside the instantiating components list
|
2011-07-12 14:55:27 +02:00
|
|
|
const ScopeChain &chain = _scopeChain;
|
|
|
|
|
if (chain.jsScopes().contains(scope)
|
|
|
|
|
|| chain.qmlScopeObjects().contains(scope)
|
|
|
|
|
|| chain.qmlTypes() == scope
|
|
|
|
|
|| chain.globalScope() == scope)
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
if (contains(chain.qmlComponentChain().data()))
|
2010-09-24 14:05:34 +02:00
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FieldMemberExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name != _name)
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(&_scopeChain);
|
2010-09-24 14:05:34 +02:00
|
|
|
const Value *lhsValue = evaluate(node->base);
|
|
|
|
|
if (!lhsValue)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (check(lhsValue->asObjectValue())) // passing null is ok
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionDeclaration *node) override
|
2010-11-24 14:42:10 +01:00
|
|
|
{
|
|
|
|
|
return visit(static_cast<FunctionExpression *>(node));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name == _name) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (checkLookup())
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
Node::accept(node->formals, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->body, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
bool visit(AST::PatternElement *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->isVariableDeclaration() && node->bindingIdentifier == _name) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (checkLookup())
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2011-07-12 14:55:27 +02:00
|
|
|
bool contains(const QmlComponentChain *chain)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-11-29 12:53:06 +01:00
|
|
|
if (!chain || !chain->document() || !chain->document()->bind())
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
2011-11-29 12:53:06 +01:00
|
|
|
const ObjectValue *idEnv = chain->document()->bind()->idEnvironment();
|
|
|
|
|
if (idEnv && idEnv->lookupMember(_name, _scopeChain.context()))
|
|
|
|
|
return idEnv == _scope;
|
2011-07-12 14:55:27 +02:00
|
|
|
const ObjectValue *root = chain->document()->bind()->rootObjectValue();
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (root && root->lookupMember(_name, _scopeChain.context()))
|
2010-09-24 14:05:34 +02:00
|
|
|
return check(root);
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
foreach (const QmlComponentChain *parent, chain->instantiatingComponents()) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (contains(parent))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool check(const ObjectValue *s)
|
|
|
|
|
{
|
|
|
|
|
if (!s)
|
|
|
|
|
return false;
|
2010-11-17 14:20:06 +01:00
|
|
|
const ObjectValue *definingObject;
|
2011-07-12 14:55:27 +02:00
|
|
|
s->lookupMember(_name, _scopeChain.context(), &definingObject);
|
2010-11-17 14:20:06 +01:00
|
|
|
return definingObject == _scope;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkQmlScope()
|
|
|
|
|
{
|
2011-07-12 14:55:27 +02:00
|
|
|
foreach (const ObjectValue *s, _scopeChain.qmlScopeObjects()) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (check(s))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkLookup()
|
|
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *scope = nullptr;
|
2011-07-12 14:55:27 +02:00
|
|
|
_scopeChain.lookup(_name, &scope);
|
2010-09-24 14:05:34 +02:00
|
|
|
return check(scope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result _usages;
|
|
|
|
|
|
|
|
|
|
Document::Ptr _doc;
|
2011-07-12 14:55:27 +02:00
|
|
|
ScopeChain _scopeChain;
|
2010-09-24 14:05:34 +02:00
|
|
|
ScopeBuilder _builder;
|
|
|
|
|
|
|
|
|
|
QString _name;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *_scope = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
};
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
class FindTypeUsages: protected Visitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2018-11-24 02:45:30 +01:00
|
|
|
using Result = QList<AST::SourceLocation>;
|
2011-06-06 11:17:12 +02:00
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
FindTypeUsages(Document::Ptr doc, const ContextPtr &context)
|
2011-06-06 11:17:12 +02:00
|
|
|
: _doc(doc)
|
|
|
|
|
, _context(context)
|
2011-07-12 14:55:27 +02:00
|
|
|
, _scopeChain(doc, context)
|
|
|
|
|
, _builder(&_scopeChain)
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiPublicMember *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2019-02-13 15:38:58 +01:00
|
|
|
if (UiQualifiedId *memberType = node->memberType) {
|
|
|
|
|
if (memberType->name == _name) {
|
|
|
|
|
const ObjectValue * tVal = _context->lookupType(_doc.data(), QStringList(_name));
|
|
|
|
|
if (tVal == _typeValue)
|
|
|
|
|
_usages.append(node->typeToken);
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
}
|
2011-06-06 14:13:50 +02:00
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectDefinition *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
checkTypeName(node->qualifiedTypeNameId);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectBinding *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
checkTypeName(node->qualifiedTypeNameId);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiScriptBinding *node) override
|
2011-06-06 14:13:50 +02:00
|
|
|
{
|
|
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
Node::accept(node->qualifiedId, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::IdentifierExpression *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name != _name)
|
2011-06-06 11:17:12 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const ObjectValue *scope;
|
2011-07-12 14:55:27 +02:00
|
|
|
const Value *objV = _scopeChain.lookup(_name, &scope);
|
2011-06-06 11:17:12 +02:00
|
|
|
if (objV == _typeValue)
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FieldMemberExpression *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name != _name)
|
2011-06-06 11:17:12 +02:00
|
|
|
return true;
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(&_scopeChain);
|
2011-06-06 11:17:12 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionDeclaration *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
return visit(static_cast<FunctionExpression *>(node));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionExpression *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
Node::accept(node->formals, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->body, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
bool visit(AST::PatternElement *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->isVariableDeclaration())
|
|
|
|
|
Node::accept(node->initializer, this);
|
2011-06-06 11:17:12 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiImport *ast) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (ast && ast->importId == _name) {
|
2011-06-06 11:17:12 +02:00
|
|
|
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){
|
2011-09-13 09:57:24 +02:00
|
|
|
if (att->name == _name) {
|
2011-06-06 11:17:12 +02:00
|
|
|
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;
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr _context;
|
2011-07-12 14:55:27 +02:00
|
|
|
ScopeChain _scopeChain;
|
2011-06-06 11:17:12 +02:00
|
|
|
ScopeBuilder _builder;
|
|
|
|
|
|
|
|
|
|
QString _name;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *_typeValue = nullptr;
|
2011-06-06 11:17:12 +02:00
|
|
|
};
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
class FindTargetExpression: protected Visitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2011-06-06 11:17:12 +02:00
|
|
|
enum Kind {
|
|
|
|
|
ExpKind,
|
|
|
|
|
TypeKind
|
|
|
|
|
};
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
FindTargetExpression(Document::Ptr doc, const ScopeChain *scopeChain)
|
|
|
|
|
: _doc(doc), _scopeChain(scopeChain)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-17 14:43:08 +01:00
|
|
|
void operator()(quint32 offset)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-04-19 15:42:14 +02:00
|
|
|
_name.clear();
|
2018-11-24 02:45:30 +01:00
|
|
|
_scope = nullptr;
|
|
|
|
|
_objectNode = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
_offset = offset;
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = ExpKind;
|
2010-09-24 14:05:34 +02:00
|
|
|
if (_doc)
|
|
|
|
|
Node::accept(_doc->ast(), this);
|
2011-03-17 14:43:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString name() const
|
|
|
|
|
{ return _name; }
|
|
|
|
|
|
|
|
|
|
const ObjectValue *scope()
|
|
|
|
|
{
|
|
|
|
|
if (!_scope)
|
2011-07-12 14:55:27 +02:00
|
|
|
_scopeChain->lookup(_name, &_scope);
|
2011-03-17 14:43:08 +01:00
|
|
|
return _scope;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
Kind typeKind(){
|
|
|
|
|
return _typeKind;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Value *targetValue(){
|
|
|
|
|
return _targetValue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
protected:
|
|
|
|
|
void accept(AST::Node *node)
|
|
|
|
|
{ AST::Node::acceptChild(node, this); }
|
|
|
|
|
|
|
|
|
|
using Visitor::visit;
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool preVisit(Node *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (Statement *stmt = node->statementCast())
|
2010-09-24 14:05:34 +02:00
|
|
|
return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation());
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (ExpressionNode *exp = node->expressionCast())
|
2010-09-24 14:05:34 +02:00
|
|
|
return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation());
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (UiObjectMember *ui = node->uiObjectMemberCast())
|
2010-09-24 14:05:34 +02:00
|
|
|
return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(IdentifierExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if (containsOffset(node->identifierToken)) {
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2011-06-06 11:17:12 +02:00
|
|
|
if ((!_name.isEmpty()) && _name.at(0).isUpper()) {
|
|
|
|
|
// a possible type
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = _scopeChain->lookup(_name, &_scope);
|
2011-10-10 10:55:37 +02:00
|
|
|
if (value_cast<ObjectValue>(_targetValue))
|
2011-06-06 14:13:50 +02:00
|
|
|
_typeKind = TypeKind;
|
2011-06-06 11:17:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(FieldMemberExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (containsOffset(node->identifierToken)) {
|
2011-03-17 14:43:08 +01:00
|
|
|
setScope(node->base);
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2011-06-06 11:17:12 +02:00
|
|
|
if ((!_name.isEmpty()) && _name.at(0).isUpper()) {
|
|
|
|
|
// a possible type
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(_scopeChain);
|
2011-06-06 11:17:12 +02:00
|
|
|
const Value *lhsValue = evaluate(node->base);
|
|
|
|
|
if (!lhsValue)
|
|
|
|
|
return true;
|
|
|
|
|
const ObjectValue *lhsObj = lhsValue->asObjectValue();
|
|
|
|
|
if (lhsObj) {
|
|
|
|
|
_scope = lhsObj;
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = lhsObj->lookupMember(_name, _scopeChain->context());
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = TypeKind;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiScriptBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
return !checkBindingName(node->qualifiedId);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiArrayBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
return !checkBindingName(node->qualifiedId);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiObjectBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if ((!checkTypeName(node->qualifiedTypeNameId)) &&
|
|
|
|
|
(!checkBindingName(node->qualifiedId))) {
|
2011-03-17 14:43:08 +01:00
|
|
|
Node *oldObjectNode = _objectNode;
|
|
|
|
|
_objectNode = node;
|
|
|
|
|
accept(node->initializer);
|
|
|
|
|
_objectNode = oldObjectNode;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiObjectDefinition *node) override
|
2011-03-17 14:43:08 +01:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!checkTypeName(node->qualifiedTypeNameId)) {
|
|
|
|
|
Node *oldObjectNode = _objectNode;
|
|
|
|
|
_objectNode = node;
|
|
|
|
|
accept(node->initializer);
|
|
|
|
|
_objectNode = oldObjectNode;
|
|
|
|
|
}
|
2011-03-17 14:43:08 +01:00
|
|
|
return false;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiPublicMember *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if (containsOffset(node->typeToken)){
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->defaultToken.isValid()) {
|
|
|
|
|
_name = node->memberType->name.toString();
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = _scopeChain->context()->lookupType(_doc.data(), QStringList(_name));
|
2018-11-24 02:45:30 +01:00
|
|
|
_scope = nullptr;
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = TypeKind;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
} else if (containsOffset(node->identifierToken)) {
|
2011-03-17 14:43:08 +01:00
|
|
|
_scope = _doc->bind()->findQmlObject(_objectNode);
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(FunctionDeclaration *node) override
|
2010-11-24 14:42:10 +01:00
|
|
|
{
|
|
|
|
|
return visit(static_cast<FunctionExpression *>(node));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(FunctionExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (containsOffset(node->identifierToken)) {
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
bool visit(PatternElement *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->isVariableDeclaration() && containsOffset(node->identifierToken)) {
|
|
|
|
|
_name = node->bindingIdentifier.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool containsOffset(SourceLocation start, SourceLocation end)
|
|
|
|
|
{
|
|
|
|
|
return _offset >= start.begin() && _offset <= end.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool containsOffset(SourceLocation loc)
|
|
|
|
|
{
|
|
|
|
|
return _offset >= loc.begin() && _offset <= loc.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkBindingName(UiQualifiedId *id)
|
|
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (id && !id->name.isEmpty() && !id->next && containsOffset(id->identifierToken)) {
|
2011-03-17 14:43:08 +01:00
|
|
|
_scope = _doc->bind()->findQmlObject(_objectNode);
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = id->name.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
bool checkTypeName(UiQualifiedId *id)
|
|
|
|
|
{
|
|
|
|
|
for (UiQualifiedId *att = id; att; att = att->next) {
|
2011-09-13 09:57:24 +02:00
|
|
|
if (!att->name.isEmpty() && containsOffset(att->identifierToken)) {
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = _scopeChain->context()->lookupType(_doc.data(), id, att->next);
|
2018-11-24 02:45:30 +01:00
|
|
|
_scope = nullptr;
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = att->name.toString();
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = TypeKind;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-17 14:43:08 +01:00
|
|
|
void setScope(Node *node)
|
|
|
|
|
{
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(_scopeChain);
|
2011-03-17 14:43:08 +01:00
|
|
|
const Value *v = evaluate(node);
|
|
|
|
|
if (v)
|
|
|
|
|
_scope = v->asObjectValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString _name;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *_scope = nullptr;
|
|
|
|
|
const Value *_targetValue = nullptr;
|
|
|
|
|
Node *_objectNode = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
Document::Ptr _doc;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ScopeChain *_scopeChain = nullptr;
|
|
|
|
|
quint32 _offset = 0;
|
|
|
|
|
Kind _typeKind = ExpKind;
|
2010-09-24 14:05:34 +02:00
|
|
|
};
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-31 11:31:59 +02:00
|
|
|
class ProcessFile
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr context;
|
2018-11-24 02:45:30 +01:00
|
|
|
using Usage = FindReferences::Usage;
|
|
|
|
|
const QString name;
|
2010-09-24 14:05:34 +02:00
|
|
|
const ObjectValue *scope;
|
2012-05-25 16:45:18 +02:00
|
|
|
QFutureInterface<Usage> *future;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
public:
|
2018-05-31 11:31:59 +02:00
|
|
|
// needed by QtConcurrent
|
|
|
|
|
using argument_type = const QString &;
|
|
|
|
|
using result_type = QList<Usage>;
|
|
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
ProcessFile(const ContextPtr &context,
|
2018-11-24 02:45:30 +01:00
|
|
|
const QString &name,
|
2012-05-25 16:45:18 +02:00
|
|
|
const ObjectValue *scope,
|
|
|
|
|
QFutureInterface<Usage> *future)
|
|
|
|
|
: context(context), name(name), scope(scope), future(future)
|
2010-09-24 14:05:34 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
QList<Usage> operator()(const QString &fileName)
|
|
|
|
|
{
|
|
|
|
|
QList<Usage> usages;
|
2012-05-25 16:45:18 +02:00
|
|
|
if (future->isPaused())
|
|
|
|
|
future->waitForResume();
|
|
|
|
|
if (future->isCanceled())
|
|
|
|
|
return usages;
|
2011-07-13 15:04:27 +02:00
|
|
|
Document::Ptr doc = context->snapshot().document(fileName);
|
2010-09-24 14:05:34 +02:00
|
|
|
if (!doc)
|
|
|
|
|
return usages;
|
|
|
|
|
|
|
|
|
|
// find all idenfifier expressions, try to resolve them and check if the result is in scope
|
2011-07-13 15:04:27 +02:00
|
|
|
FindUsages findUsages(doc, context);
|
2010-09-24 14:05:34 +02:00
|
|
|
FindUsages::Result results = findUsages(name, scope);
|
2011-04-19 15:42:14 +02:00
|
|
|
foreach (const AST::SourceLocation &loc, results)
|
2010-09-24 14:05:34 +02:00
|
|
|
usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
|
2012-05-25 16:45:18 +02:00
|
|
|
if (future->isPaused())
|
|
|
|
|
future->waitForResume();
|
2010-09-24 14:05:34 +02:00
|
|
|
return usages;
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
};
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2018-05-31 11:31:59 +02:00
|
|
|
class SearchFileForType
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr context;
|
2018-11-24 02:45:30 +01:00
|
|
|
using Usage = FindReferences::Usage;
|
|
|
|
|
const QString name;
|
2011-06-06 11:17:12 +02:00
|
|
|
const ObjectValue *scope;
|
2012-05-25 16:45:18 +02:00
|
|
|
QFutureInterface<Usage> *future;
|
2011-06-06 11:17:12 +02:00
|
|
|
|
|
|
|
|
public:
|
2018-05-31 11:31:59 +02:00
|
|
|
// needed by QtConcurrent
|
|
|
|
|
using argument_type = const QString &;
|
|
|
|
|
using result_type = QList<Usage>;
|
|
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
SearchFileForType(const ContextPtr &context,
|
2018-11-24 02:45:30 +01:00
|
|
|
const QString &name,
|
2012-05-25 16:45:18 +02:00
|
|
|
const ObjectValue *scope,
|
|
|
|
|
QFutureInterface<Usage> *future)
|
|
|
|
|
: context(context), name(name), scope(scope), future(future)
|
2011-06-06 11:17:12 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
QList<Usage> operator()(const QString &fileName)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
QList<Usage> usages;
|
2012-05-25 16:45:18 +02:00
|
|
|
if (future->isPaused())
|
|
|
|
|
future->waitForResume();
|
|
|
|
|
if (future->isCanceled())
|
|
|
|
|
return usages;
|
2011-07-13 15:04:27 +02:00
|
|
|
Document::Ptr doc = context->snapshot().document(fileName);
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!doc)
|
|
|
|
|
return usages;
|
|
|
|
|
|
|
|
|
|
// find all idenfifier expressions, try to resolve them and check if the result is in scope
|
2011-07-13 15:04:27 +02:00
|
|
|
FindTypeUsages findUsages(doc, context);
|
2011-06-06 11:17:12 +02:00
|
|
|
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));
|
2012-05-25 16:45:18 +02:00
|
|
|
if (future->isPaused())
|
|
|
|
|
future->waitForResume();
|
2011-06-06 11:17:12 +02:00
|
|
|
return usages;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-31 11:31:59 +02:00
|
|
|
class UpdateUI
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
using Usage = FindReferences::Usage;
|
2010-09-24 14:05:34 +02:00
|
|
|
QFutureInterface<Usage> *future;
|
|
|
|
|
|
|
|
|
|
public:
|
2018-05-31 11:31:59 +02:00
|
|
|
// needed by QtConcurrent
|
|
|
|
|
using first_argument_type = QList<Usage> &;
|
|
|
|
|
using second_argument_type = const QList<Usage> &;
|
|
|
|
|
using result_type = void;
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
UpdateUI(QFutureInterface<Usage> *future): future(future) {}
|
|
|
|
|
|
|
|
|
|
void operator()(QList<Usage> &, const QList<Usage> &usages)
|
|
|
|
|
{
|
|
|
|
|
foreach (const Usage &u, usages)
|
|
|
|
|
future->reportResult(u);
|
|
|
|
|
|
|
|
|
|
future->setProgressValue(future->progressValue() + 1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
|
|
|
|
FindReferences::FindReferences(QObject *parent)
|
2011-09-09 16:10:57 +02:00
|
|
|
: QObject(parent)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
m_watcher.setPendingResultsLimit(1);
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(&m_watcher, &QFutureWatcherBase::resultsReadyAt, this, &FindReferences::displayResults);
|
|
|
|
|
connect(&m_watcher, &QFutureWatcherBase::finished, this, &FindReferences::searchFinished);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
FindReferences::~FindReferences() = default;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
static void find_helper(QFutureInterface<FindReferences::Usage> &future,
|
2018-11-24 02:45:30 +01:00
|
|
|
const ModelManagerInterface::WorkingCopy &workingCopy,
|
2010-09-24 14:05:34 +02:00
|
|
|
Snapshot snapshot,
|
2018-11-24 02:45:30 +01:00
|
|
|
const QString &fileName,
|
2011-07-11 12:53:05 +02:00
|
|
|
quint32 offset,
|
|
|
|
|
QString replacement)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
// update snapshot from workingCopy to make sure it's up to date
|
|
|
|
|
// ### remove?
|
|
|
|
|
// ### this is a great candidate for map-reduce
|
2019-07-24 13:43:54 +02:00
|
|
|
const ModelManagerInterface::WorkingCopy::Table &all = workingCopy.all();
|
|
|
|
|
for (auto it = all.cbegin(), end = all.cend(); it != end; ++it) {
|
2011-09-09 10:55:11 +02:00
|
|
|
const QString fileName = it.key();
|
|
|
|
|
Document::Ptr oldDoc = snapshot.document(fileName);
|
2010-09-24 14:05:34 +02:00
|
|
|
if (oldDoc && oldDoc->editorRevision() == it.value().second)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-07-22 19:06:44 +02:00
|
|
|
Dialect language;
|
2011-09-09 10:55:11 +02:00
|
|
|
if (oldDoc)
|
|
|
|
|
language = oldDoc->language();
|
|
|
|
|
else
|
2014-01-22 18:38:45 +01:00
|
|
|
language = ModelManagerInterface::guessLanguageOfFile(fileName);
|
2014-07-22 19:06:44 +02:00
|
|
|
if (language == Dialect::NoLanguage) {
|
2014-06-02 19:36:32 +02:00
|
|
|
qCDebug(qmljsLog) << "NoLanguage in qmljsfindreferences.cpp find_helper for " << fileName;
|
2014-07-22 19:06:44 +02:00
|
|
|
language = Dialect::AnyLanguage;
|
2014-06-02 19:36:32 +02:00
|
|
|
}
|
2011-09-09 10:55:11 +02:00
|
|
|
|
2011-11-03 13:47:03 +01:00
|
|
|
Document::MutablePtr newDoc = snapshot.documentFromSource(
|
|
|
|
|
it.value().first, fileName, language);
|
2010-09-24 14:05:34 +02:00
|
|
|
newDoc->parse();
|
|
|
|
|
snapshot.insert(newDoc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the scope for the name we're searching
|
|
|
|
|
Document::Ptr doc = snapshot.document(fileName);
|
|
|
|
|
if (!doc)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
2011-06-28 12:01:56 +02:00
|
|
|
|
2014-04-11 23:07:52 +02:00
|
|
|
Link link(snapshot, modelManager->defaultVContext(doc->language(), doc), modelManager->builtins(doc));
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr context = link();
|
2011-05-06 13:31:30 +02:00
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
ScopeChain scopeChain(doc, context);
|
2011-07-12 14:55:27 +02:00
|
|
|
ScopeBuilder builder(&scopeChain);
|
2010-09-24 14:05:34 +02:00
|
|
|
ScopeAstPath astPath(doc);
|
|
|
|
|
builder.push(astPath(offset));
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
FindTargetExpression findTarget(doc, &scopeChain);
|
2011-03-17 14:43:08 +01:00
|
|
|
findTarget(offset);
|
|
|
|
|
const QString &name = findTarget.name();
|
2010-09-24 14:05:34 +02:00
|
|
|
if (name.isEmpty())
|
|
|
|
|
return;
|
2011-07-11 12:53:05 +02:00
|
|
|
if (!replacement.isNull() && replacement.isEmpty())
|
|
|
|
|
replacement = name;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
QStringList files;
|
|
|
|
|
foreach (const Document::Ptr &doc, snapshot) {
|
|
|
|
|
// ### skip files that don't contain the name token
|
|
|
|
|
files.append(doc->fileName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
future.setProgressRange(0, files.size());
|
|
|
|
|
|
2011-07-11 12:53:05 +02:00
|
|
|
// report a dummy usage to indicate the search is starting
|
2011-09-07 20:28:04 +02:00
|
|
|
FindReferences::Usage searchStarting(replacement, name, 0, 0, 0);
|
2011-07-11 12:53:05 +02:00
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
if (findTarget.typeKind() == findTarget.TypeKind){
|
2011-10-10 10:55:37 +02:00
|
|
|
const ObjectValue *typeValue = value_cast<ObjectValue>(findTarget.targetValue());
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!typeValue)
|
|
|
|
|
return;
|
2011-07-11 12:53:05 +02:00
|
|
|
future.reportResult(searchStarting);
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2012-05-25 16:45:18 +02:00
|
|
|
SearchFileForType process(context, name, typeValue, &future);
|
2011-06-06 11:17:12 +02:00
|
|
|
UpdateUI reduce(&future);
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
|
|
|
|
|
} else {
|
|
|
|
|
const ObjectValue *scope = findTarget.scope();
|
|
|
|
|
if (!scope)
|
|
|
|
|
return;
|
2011-07-13 15:04:27 +02:00
|
|
|
scope->lookupMember(name, context, &scope);
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!scope)
|
|
|
|
|
return;
|
2011-09-07 20:28:04 +02:00
|
|
|
if (!scope->className().isEmpty())
|
|
|
|
|
searchStarting.lineText.prepend(scope->className() + QLatin1Char('.'));
|
2011-07-11 12:53:05 +02:00
|
|
|
future.reportResult(searchStarting);
|
2011-06-06 11:17:12 +02:00
|
|
|
|
2012-05-25 16:45:18 +02:00
|
|
|
ProcessFile process(context, name, scope, &future);
|
2011-06-06 11:17:12 +02:00
|
|
|
UpdateUI reduce(&future);
|
|
|
|
|
|
|
|
|
|
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
future.setProgressValue(files.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FindReferences::findUsages(const QString &fileName, quint32 offset)
|
|
|
|
|
{
|
2011-07-11 12:53:05 +02:00
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
|
|
|
|
|
2016-02-08 16:26:19 +01:00
|
|
|
QFuture<Usage> result = Utils::runAsync(&find_helper, modelManager->workingCopy(),
|
|
|
|
|
modelManager->snapshot(), fileName, offset, QString());
|
2011-07-11 12:53:05 +02:00
|
|
|
m_watcher.setFuture(result);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2011-07-11 12:53:05 +02:00
|
|
|
void FindReferences::renameUsages(const QString &fileName, quint32 offset,
|
|
|
|
|
const QString &replacement)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
|
|
|
|
|
2011-07-11 12:53:05 +02:00
|
|
|
// an empty non-null string asks the future to use the current name as base
|
|
|
|
|
QString newName = replacement;
|
|
|
|
|
if (newName.isNull())
|
|
|
|
|
newName = QLatin1String("");
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2016-02-08 16:26:19 +01:00
|
|
|
QFuture<Usage> result = Utils::runAsync(&find_helper, modelManager->workingCopy(),
|
|
|
|
|
modelManager->snapshot(), fileName, offset, newName);
|
2010-09-24 14:05:34 +02:00
|
|
|
m_watcher.setFuture(result);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
QList<FindReferences::Usage> FindReferences::findUsageOfType(const QString &fileName, const QString &typeName)
|
2015-02-17 12:10:04 +01:00
|
|
|
{
|
|
|
|
|
QList<Usage> usages;
|
|
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
|
|
|
|
|
|
|
|
|
Document::Ptr doc = modelManager->snapshot().document(fileName);
|
|
|
|
|
if (!doc)
|
|
|
|
|
return usages;
|
|
|
|
|
|
|
|
|
|
Link link(modelManager->snapshot(), modelManager->defaultVContext(doc->language(), doc), modelManager->builtins(doc));
|
|
|
|
|
ContextPtr context = link();
|
|
|
|
|
ScopeChain scopeChain(doc, context);
|
|
|
|
|
|
|
|
|
|
const ObjectValue *targetValue = scopeChain.context()->lookupType(doc.data(), QStringList(typeName));
|
|
|
|
|
|
|
|
|
|
QmlJS::Snapshot snapshot = modelManager->snapshot();
|
|
|
|
|
|
|
|
|
|
foreach (const QmlJS::Document::Ptr &doc, snapshot) {
|
|
|
|
|
FindTypeUsages findUsages(doc, context);
|
|
|
|
|
FindTypeUsages::Result results = findUsages(typeName, targetValue);
|
|
|
|
|
foreach (const AST::SourceLocation &loc, results) {
|
|
|
|
|
usages.append(Usage(doc->fileName(), matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return usages;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
void FindReferences::displayResults(int first, int last)
|
|
|
|
|
{
|
2011-03-17 15:08:25 +01:00
|
|
|
// the first usage is always a dummy to indicate we now start searching
|
|
|
|
|
if (first == 0) {
|
2011-07-11 12:53:05 +02:00
|
|
|
Usage dummy = m_watcher.future().resultAt(0);
|
2011-09-09 16:10:57 +02:00
|
|
|
const QString replacement = dummy.path;
|
|
|
|
|
const QString symbolName = dummy.lineText;
|
|
|
|
|
const QString label = tr("QML/JS Usages:");
|
2011-07-11 12:53:05 +02:00
|
|
|
|
|
|
|
|
if (replacement.isEmpty()) {
|
2015-02-03 23:48:57 +02:00
|
|
|
m_currentSearch = SearchResultWindow::instance()->startNewSearch(
|
|
|
|
|
label, QString(), symbolName, SearchResultWindow::SearchOnly);
|
2011-07-11 12:53:05 +02:00
|
|
|
} else {
|
2015-02-03 23:48:57 +02:00
|
|
|
m_currentSearch = SearchResultWindow::instance()->startNewSearch(
|
|
|
|
|
label, QString(), symbolName, SearchResultWindow::SearchAndReplace,
|
|
|
|
|
SearchResultWindow::PreserveCaseDisabled);
|
2011-09-02 11:51:31 +02:00
|
|
|
m_currentSearch->setTextToReplace(replacement);
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::replaceButtonClicked,
|
|
|
|
|
this, &FindReferences::onReplaceButtonClicked);
|
2011-07-11 12:53:05 +02:00
|
|
|
}
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::activated,
|
2017-09-22 16:36:26 +02:00
|
|
|
[](const Core::SearchResultItem& item) {
|
|
|
|
|
Core::EditorManager::openEditorAtSearchResult(item);
|
|
|
|
|
});
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::cancelled, this, &FindReferences::cancel);
|
|
|
|
|
connect(m_currentSearch.data(), &SearchResult::paused, this, &FindReferences::setPaused);
|
2015-02-03 23:48:57 +02:00
|
|
|
SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch | IOutputPane::WithFocus));
|
2011-03-17 15:08:25 +01:00
|
|
|
|
2019-07-18 14:17:25 +02:00
|
|
|
FutureProgress *progress = ProgressManager::addTask(m_watcher.future(),
|
|
|
|
|
tr("Searching for Usages"),
|
|
|
|
|
"QmlJSEditor.TaskSearch");
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(progress, &FutureProgress::clicked, m_currentSearch.data(), &SearchResult::popup);
|
2011-03-17 15:08:25 +01:00
|
|
|
|
|
|
|
|
++first;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-09 16:10:57 +02:00
|
|
|
if (!m_currentSearch) {
|
|
|
|
|
m_watcher.cancel();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
for (int index = first; index != last; ++index) {
|
|
|
|
|
Usage result = m_watcher.future().resultAt(index);
|
2011-09-02 11:51:31 +02:00
|
|
|
m_currentSearch->addResult(result.path,
|
2010-09-24 14:05:34 +02:00
|
|
|
result.line,
|
|
|
|
|
result.lineText,
|
|
|
|
|
result.col,
|
|
|
|
|
result.len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FindReferences::searchFinished()
|
|
|
|
|
{
|
2011-09-09 16:10:57 +02:00
|
|
|
if (m_currentSearch)
|
2012-05-16 15:59:16 +02:00
|
|
|
m_currentSearch->finishSearch(m_watcher.isCanceled());
|
2018-11-24 02:45:30 +01:00
|
|
|
m_currentSearch = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
emit changed();
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-09 08:46:51 +02:00
|
|
|
void FindReferences::cancel()
|
|
|
|
|
{
|
|
|
|
|
m_watcher.cancel();
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-25 16:45:18 +02:00
|
|
|
void FindReferences::setPaused(bool paused)
|
|
|
|
|
{
|
|
|
|
|
if (!paused || m_watcher.isRunning()) // guard against pausing when the search is finished
|
|
|
|
|
m_watcher.setPaused(paused);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
void FindReferences::onReplaceButtonClicked(const QString &text, const QList<SearchResultItem> &items, bool preserveCase)
|
2011-07-11 12:53:05 +02:00
|
|
|
{
|
2012-11-30 16:15:07 +01:00
|
|
|
const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase);
|
2011-07-11 12:53:05 +02:00
|
|
|
|
|
|
|
|
// files that are opened in an editor are changed, but not saved
|
|
|
|
|
QStringList changedOnDisk;
|
|
|
|
|
QStringList changedUnsavedEditors;
|
|
|
|
|
foreach (const QString &fileName, fileNames) {
|
2014-05-07 16:25:04 +02:00
|
|
|
if (DocumentModel::documentForFilePath(fileName))
|
2011-07-11 12:53:05 +02:00
|
|
|
changedOnDisk += fileName;
|
|
|
|
|
else
|
|
|
|
|
changedUnsavedEditors += fileName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!changedOnDisk.isEmpty())
|
2015-02-03 23:48:57 +02:00
|
|
|
ModelManagerInterface::instance()->updateSourceFiles(changedOnDisk, true);
|
2011-07-11 12:53:05 +02:00
|
|
|
if (!changedUnsavedEditors.isEmpty())
|
2015-02-03 23:48:57 +02:00
|
|
|
ModelManagerInterface::instance()->updateSourceFiles(changedUnsavedEditors, false);
|
2011-07-11 12:53:05 +02:00
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
SearchResultWindow::instance()->hide();
|
2011-07-11 12:53:05 +02:00
|
|
|
}
|