aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2019-11-11 17:35:09 +0100
committerUlf Hermann <ulf.hermann@qt.io>2020-01-13 16:54:13 +0100
commit6c68a39a56299c72da30a4bac3ca7fe8420687a4 (patch)
treeb88b33e57c71f131dec7adfd4cf42b87766f1382 /tools
parentd0b2a3b5eb4021fb19b634e550cbc6f6664ad775 (diff)
qmllint: Resolve aliases
Change-Id: Ida53af9774dc72559395064169113d0ee1f47f24 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmllint/findunqualified.cpp159
-rw-r--r--tools/qmllint/findunqualified.h3
-rw-r--r--tools/qmllint/importedmembersvisitor.cpp155
-rw-r--r--tools/qmllint/importedmembersvisitor.h73
-rw-r--r--tools/qmllint/metatypes.h6
-rw-r--r--tools/qmllint/qmllint.pro2
-rw-r--r--tools/qmllint/typedescriptionreader.cpp2
7 files changed, 272 insertions, 128 deletions
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp
index ea28884f7b..359510f5f7 100644
--- a/tools/qmllint/findunqualified.cpp
+++ b/tools/qmllint/findunqualified.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include "findunqualified.h"
+#include "importedmembersvisitor.h"
#include "scopetree.h"
#include "typedescriptionreader.h"
@@ -99,121 +100,21 @@ void FindUnqualifiedIDVisitor::parseHeaders(QQmlJS::AST::UiHeaderItemList *heade
}
}
-void FindUnqualifiedIDVisitor::parseMembers(QQmlJS::AST::UiObjectMemberList *member,
- ScopeTree *scope)
-{
- using namespace QQmlJS::AST;
-
- // member should be the sole element
- Q_ASSERT(!member->next);
- Q_ASSERT(member && member->member->kind == UiObjectMember::Kind_UiObjectDefinition);
- auto definition = cast<UiObjectDefinition *>(member->member);
- auto qualifiedId = definition->qualifiedTypeNameId;
- while (qualifiedId && qualifiedId->next) {
- qualifiedId = qualifiedId->next;
- }
- scope->setSuperclassName(qualifiedId->name.toString());
- UiObjectMemberList *initMembers = definition->initializer->members;
- while (initMembers) {
- switch (initMembers->member->kind) {
- case UiObjectMember::Kind_UiArrayBinding: {
- // nothing to do
- break;
- }
- case UiObjectMember::Kind_UiEnumDeclaration: {
- // nothing to do
- break;
- }
- case UiObjectMember::Kind_UiObjectBinding: {
- // nothing to do
- break;
- }
- case UiObjectMember::Kind_UiObjectDefinition: {
- // creates nothing accessible
- break;
- }
- case UiObjectMember::Kind_UiPublicMember: {
- auto publicMember = cast<UiPublicMember *>(initMembers->member);
- switch (publicMember->type) {
- case UiPublicMember::Signal: {
- UiParameterList *param = publicMember->parameters;
- MetaMethod method;
- method.setMethodType(MetaMethod::Signal);
- method.setMethodName(publicMember->name.toString());
- while (param) {
- method.addParameter(param->name.toString(), param->type->name.toString());
- param = param->next;
- }
- scope->addMethod(method);
- break;
- }
- case UiPublicMember::Property: {
- MetaProperty prop {
- publicMember->name.toString(),
- publicMember->memberType->name.toString(),
- false,
- false,
- false,
- 0
- };
- scope->addProperty(prop);
- break;
- }
- }
- break;
- }
- case UiObjectMember::Kind_UiScriptBinding: {
- // does not create anything new, ignore
- break;
- }
- case UiObjectMember::Kind_UiSourceElement: {
- auto sourceElement = cast<UiSourceElement *>(initMembers->member);
- if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
- MetaMethod method;
- method.setMethodName(fexpr->name.toString());
- method.setMethodType(MetaMethod::Method);
- FormalParameterList *parameters = fexpr->formals;
- while (parameters) {
- method.addParameter(parameters->element->bindingIdentifier.toString(), "");
- parameters = parameters->next;
- }
- scope->addMethod(method);
- } else if (ClassExpression *clexpr =
- sourceElement->sourceElement->asClassDefinition()) {
- const MetaProperty prop { clexpr->name.toString(), "", false, false, false, 1 };
- scope->addProperty(prop);
- } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
- // nothing to do
- } else {
- const auto loc = sourceElement->firstSourceLocation();
- m_colorOut.writeUncolored(
- "unsupportedd sourceElement at "
- + QString::fromLatin1("%1:%2: ").arg(loc.startLine).arg(loc.startColumn)
- + QString::number(sourceElement->sourceElement->kind));
- }
- break;
- }
- default: {
- m_colorOut.writeUncolored("unsupported element of kind "
- + QString::number(initMembers->member->kind));
- }
- }
- initMembers = initMembers->next;
- }
-}
-
-void FindUnqualifiedIDVisitor::parseProgram(QQmlJS::AST::Program *program, ScopeTree *scope)
+ScopeTree *FindUnqualifiedIDVisitor::parseProgram(QQmlJS::AST::Program *program,
+ const QString &name)
{
using namespace QQmlJS::AST;
+ ScopeTree *result = new ScopeTree(ScopeType::JSLexicalScope, name);
for (auto *statement = program->statements; statement; statement = statement->next) {
if (auto *function = cast<FunctionDeclaration *>(statement->statement)) {
MetaMethod method(function->name.toString());
method.setMethodType(MetaMethod::Method);
for (auto *parameters = function->formals; parameters; parameters = parameters->next)
method.addParameter(parameters->element->bindingIdentifier.toString(), "");
- scope->addMethod(method);
+ result->addMethod(method);
}
}
+ return result;
}
enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath };
@@ -401,42 +302,47 @@ void FindUnqualifiedIDVisitor::importHelper(const QString &module, const QString
ScopeTree *FindUnqualifiedIDVisitor::localFile2ScopeTree(const QString &filePath)
{
using namespace QQmlJS::AST;
- auto scope = new ScopeTree(ScopeType::QMLScope);
const QFileInfo info { filePath };
QString baseName = info.baseName();
- scope->setClassName(baseName.endsWith(".ui") ? baseName.chopped(3) : baseName);
- QFile file(filePath);
- if (!file.open(QFile::ReadOnly))
- return scope;
-
- QString code = file.readAll();
- file.close();
+ const QString scopeName = baseName.endsWith(".ui") ? baseName.chopped(3) : baseName;
QQmlJS::Engine engine;
QQmlJS::Lexer lexer(&engine);
const QString lowerSuffix = info.suffix().toLower();
- const bool isJavaScript = (lowerSuffix == QLatin1String("js") || lowerSuffix == QLatin1String("mjs"));
const bool isESModule = lowerSuffix == QLatin1String("mjs");
+ const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
+
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly)) {
+ return new ScopeTree(isJavaScript ? ScopeType::JSLexicalScope : ScopeType::QMLScope,
+ scopeName);
+ }
+
+ QString code = file.readAll();
+ file.close();
+
lexer.setCode(code, /*line = */ 1, /*qmlMode=*/ !isJavaScript);
QQmlJS::Parser parser(&engine);
const bool success = isJavaScript ? (isESModule ? parser.parseModule()
: parser.parseProgram())
: parser.parse();
- if (!success)
- return scope;
+ if (!success) {
+ return new ScopeTree(isJavaScript ? ScopeType::JSLexicalScope : ScopeType::QMLScope,
+ scopeName);
+ }
if (!isJavaScript) {
QQmlJS::AST::UiProgram *program = parser.ast();
parseHeaders(program->headers);
- parseMembers(program->members, scope);
- } else {
- // TODO: Anything special to do with ES modules here?
- parseProgram(QQmlJS::AST::cast<QQmlJS::AST::Program *>(parser.rootNode()), scope);
+ ImportedMembersVisitor membersVisitor(&m_colorOut);
+ program->members->accept(&membersVisitor);
+ return membersVisitor.result(scopeName);
}
- return scope;
+ // TODO: Anything special to do with ES modules here?
+ return parseProgram(QQmlJS::AST::cast<QQmlJS::AST::Program *>(parser.rootNode()), scopeName);
}
void FindUnqualifiedIDVisitor::importFileOrDirectory(const QString &fileOrDirectory,
@@ -705,7 +611,9 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiPublicMember *uipm)
uipm->memberType ? uipm->memberType->name.toString() : QString(),
uipm->typeModifier == QLatin1String("list"),
!uipm->isReadonlyMember,
- false, 0);
+ false,
+ uipm->memberType ? (uipm->memberType->name == QLatin1String("alias")) : false,
+ 0);
property.setType(m_exportedName2Scope.value(property.typeName()).get());
m_currentScope->insertPropertyIdentifier(property);
return true;
@@ -882,7 +790,8 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
}
name.chop(1);
- MetaProperty prop(uiob->qualifiedId->name.toString(), name, false, true, true, 0);
+ MetaProperty prop(uiob->qualifiedId->name.toString(), name, false, true, true,
+ name == QLatin1String("alias"), 0);
prop.setType(m_exportedName2Scope.value(uiob->qualifiedTypeNameId->name.toString()).get());
m_currentScope->addProperty(prop);
@@ -897,7 +806,9 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
leaveEnvironment();
MetaProperty property(uiob->qualifiedId->name.toString(),
uiob->qualifiedTypeNameId->name.toString(),
- false, true, true, 0);
+ false, true, true,
+ uiob->qualifiedTypeNameId->name == QLatin1String("alias"),
+ 0);
property.setType(childScope);
m_currentScope->addProperty(property);
}
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
index 37ba259638..f5955a3a74 100644
--- a/tools/qmllint/findunqualified.h
+++ b/tools/qmllint/findunqualified.h
@@ -103,8 +103,7 @@ private:
void importExportedNames(const QStringRef &prefix, QString name);
void parseHeaders(QQmlJS::AST::UiHeaderItemList *headers);
- void parseMembers(QQmlJS::AST::UiObjectMemberList *members, ScopeTree *scope);
- void parseProgram(QQmlJS::AST::Program *program, ScopeTree *scope);
+ ScopeTree *parseProgram(QQmlJS::AST::Program *program, const QString &name);
void throwRecursionDepthError() override;
diff --git a/tools/qmllint/importedmembersvisitor.cpp b/tools/qmllint/importedmembersvisitor.cpp
new file mode 100644
index 0000000000..676903d135
--- /dev/null
+++ b/tools/qmllint/importedmembersvisitor.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** 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.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "importedmembersvisitor.h"
+#include "scopetree.h"
+
+using namespace QQmlJS::AST;
+
+ScopeTree *ImportedMembersVisitor::result(const QString &scopeName) const
+{
+ ScopeTree *result = new ScopeTree(ScopeType::QMLScope);
+ result->setClassName(scopeName);
+ result->setSuperclassName(m_rootObject->superclassName());
+ const auto properties = m_rootObject->properties();
+ for (auto property : properties) {
+ if (property.isAlias()) {
+ const auto it = m_objects.find(property.typeName());
+ if (it != m_objects.end())
+ property.setType(it->get());
+ result->addProperty(property);
+ } else {
+ result->addProperty(property);
+ }
+ }
+
+ for (const auto &method : m_rootObject->methods())
+ result->addMethod(method);
+
+ return result;
+}
+
+bool ImportedMembersVisitor::visit(UiObjectDefinition *definition)
+{
+ ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope));
+ auto qualifiedId = definition->qualifiedTypeNameId;
+ while (qualifiedId && qualifiedId->next)
+ qualifiedId = qualifiedId->next;
+ scope->setSuperclassName(qualifiedId->name.toString());
+ if (!m_rootObject)
+ m_rootObject = scope;
+ m_currentObjects.append(scope);
+ return true;
+}
+
+void ImportedMembersVisitor::endVisit(UiObjectDefinition *)
+{
+ m_currentObjects.pop_back();
+}
+
+bool ImportedMembersVisitor::visit(UiPublicMember *publicMember)
+{
+ switch (publicMember->type) {
+ case UiPublicMember::Signal: {
+ UiParameterList *param = publicMember->parameters;
+ MetaMethod method;
+ method.setMethodType(MetaMethod::Signal);
+ method.setMethodName(publicMember->name.toString());
+ while (param) {
+ method.addParameter(param->name.toString(), param->type->name.toString());
+ param = param->next;
+ }
+ currentObject()->addMethod(method);
+ break;
+ }
+ case UiPublicMember::Property: {
+ auto typeName = publicMember->memberType->name;
+ const bool isAlias = (typeName == QLatin1String("alias"));
+ if (isAlias) {
+ const auto expression = cast<ExpressionStatement *>(publicMember->statement);
+ if (const auto idExpression = cast<IdentifierExpression *>(expression->expression))
+ typeName = idExpression->name;
+ }
+ MetaProperty prop {
+ publicMember->name.toString(),
+ typeName.toString(),
+ false,
+ false,
+ false,
+ isAlias,
+ 0
+ };
+ currentObject()->addProperty(prop);
+ break;
+ }
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(UiSourceElement *sourceElement)
+{
+ if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
+ MetaMethod method;
+ method.setMethodName(fexpr->name.toString());
+ method.setMethodType(MetaMethod::Method);
+ FormalParameterList *parameters = fexpr->formals;
+ while (parameters) {
+ method.addParameter(parameters->element->bindingIdentifier.toString(), "");
+ parameters = parameters->next;
+ }
+ currentObject()->addMethod(method);
+ } else if (ClassExpression *clexpr = sourceElement->sourceElement->asClassDefinition()) {
+ MetaProperty prop { clexpr->name.toString(), "", false, false, false, false, 1 };
+ currentObject()->addProperty(prop);
+ } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
+ // nothing to do
+ } else {
+ const auto loc = sourceElement->firstSourceLocation();
+ m_colorOut->writeUncolored(
+ "unsupportedd sourceElement at "
+ + QString::fromLatin1("%1:%2: ").arg(loc.startLine).arg(loc.startColumn)
+ + QString::number(sourceElement->sourceElement->kind));
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(UiScriptBinding *scriptBinding)
+{
+ if (scriptBinding->qualifiedId->name == QLatin1String("id")) {
+ const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
+ const auto *idExprension = cast<IdentifierExpression *>(statement->expression);
+ m_objects.insert(idExprension->name.toString(), currentObject());
+ }
+ return true;
+}
+
+void ImportedMembersVisitor::throwRecursionDepthError()
+{
+ m_colorOut->write(QStringLiteral("Error"), Error);
+ m_colorOut->write(QStringLiteral("Maximum statement or expression depth exceeded"), Error);
+}
diff --git a/tools/qmllint/importedmembersvisitor.h b/tools/qmllint/importedmembersvisitor.h
new file mode 100644
index 0000000000..793ff18dcc
--- /dev/null
+++ b/tools/qmllint/importedmembersvisitor.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** 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.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IMPORTEDMEMBERSVISITOR_H
+#define IMPORTEDMEMBERSVISITOR_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include "scopetree.h"
+#include "qcoloroutput.h"
+
+#include <private/qqmljsast_p.h>
+
+class ImportedMembersVisitor : public QQmlJS::AST::Visitor
+{
+public:
+ ImportedMembersVisitor(ColorOutput *colorOut) :
+ m_colorOut(colorOut)
+ {}
+
+ ScopeTree *result(const QString &scopeName) const;
+
+private:
+ bool visit(QQmlJS::AST::UiObjectDefinition *) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
+ bool visit(QQmlJS::AST::UiPublicMember *) override;
+ bool visit(QQmlJS::AST::UiSourceElement *) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *) override;
+ void throwRecursionDepthError() override;
+
+ ScopeTree::Ptr currentObject() const { return m_currentObjects.back(); }
+
+ QVector<ScopeTree::Ptr> m_currentObjects;
+ ScopeTree::ConstPtr m_rootObject;
+ QHash<QString, ScopeTree::Ptr> m_objects;
+
+ ColorOutput *m_colorOut = nullptr;
+};
+
+#endif // IMPORTEDMEMBERSVISITOR_H
diff --git a/tools/qmllint/metatypes.h b/tools/qmllint/metatypes.h
index 4710ac1613..d67de2edcd 100644
--- a/tools/qmllint/metatypes.h
+++ b/tools/qmllint/metatypes.h
@@ -123,16 +123,19 @@ class MetaProperty
bool m_isList;
bool m_isWritable;
bool m_isPointer;
+ bool m_isAlias;
int m_revision;
public:
MetaProperty(QString propertyName, QString typeName,
- bool isList, bool isWritable, bool isPointer, int revision)
+ bool isList, bool isWritable, bool isPointer, bool isAlias,
+ int revision)
: m_propertyName(std::move(propertyName))
, m_typeName(std::move(typeName))
, m_isList(isList)
, m_isWritable(isWritable)
, m_isPointer(isPointer)
+ , m_isAlias(isAlias)
, m_revision(revision)
{}
@@ -145,6 +148,7 @@ public:
bool isList() const { return m_isList; }
bool isWritable() const { return m_isWritable; }
bool isPointer() const { return m_isPointer; }
+ bool isAlias() const { return m_isAlias; }
int revision() const { return m_revision; }
};
diff --git a/tools/qmllint/qmllint.pro b/tools/qmllint/qmllint.pro
index 8ab31bc83c..4b7ca947cf 100644
--- a/tools/qmllint/qmllint.pro
+++ b/tools/qmllint/qmllint.pro
@@ -5,6 +5,7 @@ QT = core-private qmldevtools-private
SOURCES += main.cpp \
componentversion.cpp \
findunqualified.cpp \
+ importedmembersvisitor.cpp \
qcoloroutput.cpp \
scopetree.cpp \
typedescriptionreader.cpp
@@ -16,6 +17,7 @@ load(qt_tool)
HEADERS += \
componentversion.h \
findunqualified.h \
+ importedmembersvisitor.h \
metatypes.h \
qcoloroutput.h \
scopetree.h \
diff --git a/tools/qmllint/typedescriptionreader.cpp b/tools/qmllint/typedescriptionreader.cpp
index 4f0cc88800..3dc87ffc8d 100644
--- a/tools/qmllint/typedescriptionreader.cpp
+++ b/tools/qmllint/typedescriptionreader.cpp
@@ -380,7 +380,7 @@ void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, const ScopeTre
return;
}
- scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, revision));
+ scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, false, revision));
}
void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)