aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2021-11-17 11:25:36 +0100
committerMaximilian Goldstein <max.goldstein@qt.io>2021-11-23 18:45:06 +0100
commita98747ab6c153e1d361ec64ea616764a71465fa4 (patch)
treef622b3329bc2ef98b5300fef1a38db74533dcc4d /tools
parent608e1a805349b0c76fcd993c43f4f9d808c2d77f (diff)
qmllint: Move most code into a separate library
This is necessary step for both making qmllint viable for use in controls' tst_sanity, as well as for integration with the language server. As an additional upside it allows us to run our tests up to 10x faster. Eventually we want to integrate all of this into qmlcompiler but due to the state of some of the code we will keep qmllint in a separate library as to keep qmlcompiler tidier. Change-Id: Ic057ef0cd4424d28aa05e517d74280a442ec9c5a Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmllint/CMakeLists.txt4
-rw-r--r--tools/qmllint/codegen.cpp340
-rw-r--r--tools/qmllint/codegen.h102
-rw-r--r--tools/qmllint/codegenwarninginterface.cpp44
-rw-r--r--tools/qmllint/codegenwarninginterface.h49
-rw-r--r--tools/qmllint/findwarnings.cpp241
-rw-r--r--tools/qmllint/findwarnings.h74
-rw-r--r--tools/qmllint/main.cpp183
8 files changed, 6 insertions, 1031 deletions
diff --git a/tools/qmllint/CMakeLists.txt b/tools/qmllint/CMakeLists.txt
index 4107e10f74..f9127acaa3 100644
--- a/tools/qmllint/CMakeLists.txt
+++ b/tools/qmllint/CMakeLists.txt
@@ -9,15 +9,13 @@ qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "QML Syntax Verifier"
TOOLS_TARGET Qml # special case
SOURCES
- findwarnings.cpp findwarnings.h
- codegen.cpp codegen.h
- codegenwarninginterface.cpp codegenwarninginterface.h
main.cpp
../shared/qqmltoolingsettings.h
../shared/qqmltoolingsettings.cpp
PUBLIC_LIBRARIES
Qt::CorePrivate
Qt::QmlCompilerPrivate
+ Qt::QmlLintPrivate
)
qt_internal_return_unless_building_tools()
diff --git a/tools/qmllint/codegen.cpp b/tools/qmllint/codegen.cpp
deleted file mode 100644
index 7404ce220d..0000000000
--- a/tools/qmllint/codegen.cpp
+++ /dev/null
@@ -1,340 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 "codegen.h"
-
-#include <QtQmlCompiler/private/qqmljsimportvisitor_p.h>
-#include <QtQmlCompiler/private/qqmljsshadowcheck_p.h>
-#include <QtQmlCompiler/private/qqmljstypepropagator_p.h>
-
-#include <QFileInfo>
-
-Codegen::Codegen(QQmlJSImporter *importer, const QString &fileName,
- const QStringList &qmltypesFiles, QQmlJSLogger *logger, QQmlJSTypeInfo *typeInfo,
- const QString &code)
- : m_fileName(fileName),
- m_qmltypesFiles(qmltypesFiles),
- m_importer(importer),
- m_logger(logger),
- m_typeInfo(typeInfo),
- m_code(code)
-{
-}
-
-void Codegen::setDocument(QmlIR::JSCodeGen *codegen, QmlIR::Document *document)
-{
- Q_UNUSED(codegen);
- m_document = document;
- m_pool = document->jsParserEngine.pool();
- m_unitGenerator = &document->jsGenerator;
- m_entireSourceCodeLines = document->code.split(u'\n');
- m_typeResolver = std::make_unique<QQmlJSTypeResolver>(
- m_importer, document, QQmlJSTypeResolver::Indirect, m_logger);
-
- // Type resolving is using document parent mode here so that it produces fewer false positives
- // on the "parent" property of QQuickItem. It does produce a few false negatives this way
- // because items can be reparented. Furthermore, even if items are not reparented, the document
- // parent may indeed not be their visual parent. See QTBUG-95530. Eventually, we'll need
- // cleverer logic to deal with this.
- m_typeResolver->setParentMode(QQmlJSTypeResolver::UseDocumentParent);
-
- // TODO: using silentLogger for visitor actually hides potential issues but
- // using m_logger instead fails some tests, so for now let's leave the old
- // behavior for consistency. the proper fix is anyway to remove this visitor
- // and use FindWarningsVisitor instead.
- QQmlJSLogger silentLogger(m_fileName, document->code, /* silent */ true);
- QQmlJSImportVisitor visitor(m_importer, &silentLogger,
- QQmlJSImportVisitor::implicitImportDirectory(
- m_fileName, m_importer->resourceFileMapper()),
- m_qmltypesFiles);
- m_typeResolver->init(visitor);
-}
-
-void Codegen::setScope(const QmlIR::Object *object, const QmlIR::Object *scope)
-{
- m_currentObject = object;
- m_scopeType = m_typeResolver->scopeForLocation(scope->location);
- m_objectType = m_typeResolver->scopeForLocation(object->location);
-}
-
-std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage>
-Codegen::compileBinding(const QV4::Compiler::Context *context, const QmlIR::Binding &irBinding)
-{
- QQmlJS::SourceLocation bindingLocation;
- bindingLocation.startColumn = irBinding.location.column;
- bindingLocation.startLine = irBinding.location.line;
-
- if (irBinding.type != QmlIR::Binding::Type_Script) {
- const auto bindingName = [&]() {
- switch (irBinding.type) {
- case QmlIR::Binding::Type_Invalid:
- return "invalid";
- case QmlIR::Binding::Type_Boolean:
- return "a boolean";
- case QmlIR::Binding::Type_Number:
- return "a number";
- case QmlIR::Binding::Type_String:
- return "a string";
- case QmlIR::Binding::Type_Null:
- return "null";
- case QmlIR::Binding::Type_Translation:
- return "a translation";
- case QmlIR::Binding::Type_TranslationById:
- return "a translation by id";
- case QmlIR::Binding::Type_Script:
- return "a script";
- case QmlIR::Binding::Type_Object:
- return "an object";
- case QmlIR::Binding::Type_AttachedProperty:
- return "an attached property";
- case QmlIR::Binding::Type_GroupProperty:
- return "a grouped property";
- }
-
- return "nothing";
- };
-
- return diagnose(
- QStringLiteral("Binding is not a script binding, but %1.").arg(bindingName()),
- QtDebugMsg, bindingLocation);
- }
-
- Function function;
- function.qmlScope = m_scopeType;
-
- const QString propertyName = m_document->stringAt(irBinding.propertyNameIndex);
-
- const bool isProperty = m_objectType->hasProperty(propertyName);
- bool isSignal = false;
- if (!isProperty && QmlIR::IRBuilder::isSignalPropertyName(propertyName)) {
- const QString signalName = QmlIR::IRBuilder::signalNameFromSignalPropertyName(propertyName);
-
- if (signalName.endsWith(QStringLiteral("Changed"))
- && m_objectType->hasProperty(signalName.chopped(strlen("Changed")))) {
- isSignal = true;
- } else {
- const bool isConnections = !m_objectType->baseType().isNull()
- && m_objectType->baseType()->internalName() == u"QQmlConnections";
- const auto methods = isConnections ? m_objectType->parentScope()->methods(signalName)
- : m_objectType->methods(signalName);
- for (const auto &method : methods) {
- if (method.methodType() == QQmlJSMetaMethod::Signal) {
- isSignal = true;
- break;
- }
- }
- }
-
- if (!isSignal) {
- return diagnose(QStringLiteral("Could not compile signal handler for %1: "
- "The signal does not exist")
- .arg(signalName),
- QtWarningMsg, bindingLocation);
- }
- }
-
- if (!isSignal) {
- if (!isProperty) {
- return diagnose(QStringLiteral("Could not compile binding for %1: "
- "The property does not exist")
- .arg(propertyName),
- QtWarningMsg, bindingLocation);
- }
-
- const auto property = m_objectType->property(propertyName);
- function.returnType = property.type();
- if (!function.returnType) {
- return diagnose(QStringLiteral("Cannot resolve property type %1 for binding on %2")
- .arg(property.typeName())
- .arg(propertyName),
- QtWarningMsg, bindingLocation);
- }
-
- if (!property.bindable().isEmpty())
- function.isQPropertyBinding = true;
- }
-
- auto astNode =
- m_currentObject->functionsAndExpressions->slowAt(irBinding.value.compiledScriptIndex)
- ->node;
- auto ast = astNode->asFunctionDefinition();
- if (!ast) {
- QQmlJS::AST::Statement *stmt = astNode->statementCast();
- if (!stmt) {
- Q_ASSERT(astNode->expressionCast());
- QQmlJS::AST::ExpressionNode *expr = astNode->expressionCast();
- stmt = new (m_pool) QQmlJS::AST::ExpressionStatement(expr);
- }
- auto body = new (m_pool) QQmlJS::AST::StatementList(stmt);
- body = body->finish();
-
- QString name = "binding for "; // ####
- ast = new (m_pool) QQmlJS::AST::FunctionDeclaration(m_pool->newString(name),
- /*formals*/ nullptr, body);
- ast->lbraceToken = astNode->firstSourceLocation();
- ast->functionToken = ast->lbraceToken;
- ast->rbraceToken = astNode->lastSourceLocation();
- }
-
- QQmlJS::DiagnosticMessage error;
- if (!generateFunction(QV4::Compiler::ContextType::Binding, context, ast, &function, &error)) {
- // If it's a signal and the function just returns a closure, it's harmless.
- // Otherwise promote the message to warning level.
- return diagnose(
- QStringLiteral("Could not compile binding for %1: %2")
- .arg(propertyName, error.message),
- (isSignal && error.type == QtDebugMsg) ? QtDebugMsg : QtWarningMsg, error.loc);
- }
-
- return QQmlJSAotFunction {};
-}
-
-std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage>
-Codegen::compileFunction(const QV4::Compiler::Context *context, const QmlIR::Function &irFunction)
-{
- QQmlJS::SourceLocation functionLocation;
- functionLocation.startColumn = irFunction.location.column;
- functionLocation.startLine = irFunction.location.line;
-
- const QString functionName = m_document->stringAt(irFunction.nameIndex);
-
- Function function;
- function.qmlScope = m_scopeType;
-
- auto astNode = m_currentObject->functionsAndExpressions->slowAt(irFunction.index)->node;
-
- QQmlJS::DiagnosticMessage error;
- if (!generateFunction(QV4::Compiler::ContextType::Function, context,
- astNode->asFunctionDefinition(), &function, &error)) {
- return diagnose(QStringLiteral("Could not compile function %1: %2")
- .arg(functionName, error.message),
- QtWarningMsg, error.loc);
- }
-
- return QQmlJSAotFunction {};
-}
-
-QQmlJSAotFunction Codegen::globalCode() const
-{
- return QQmlJSAotFunction {};
-}
-
-QQmlJS::DiagnosticMessage Codegen::diagnose(const QString &message, QtMsgType type,
- const QQmlJS::SourceLocation &location)
-{
- if (!message.isEmpty()) {
- switch (type) {
- case QtDebugMsg:
- case QtInfoMsg:
- m_logger->logInfo(message, Log_Compiler, location);
- break;
- case QtWarningMsg:
- m_logger->logWarning(message, Log_Compiler, location);
- break;
- case QtCriticalMsg:
- case QtFatalMsg:
- m_logger->logCritical(message, Log_Compiler, location);
- break;
- }
- }
- return QQmlJS::DiagnosticMessage { message, type, location };
-}
-
-bool Codegen::generateFunction(
- QV4::Compiler::ContextType contextType,
- const QV4::Compiler::Context *context,
- QQmlJS::AST::FunctionExpression *ast,
- Function *function,
- QQmlJS::DiagnosticMessage *error) const
-{
- const auto fail = [&](const QString &message) {
- error->loc = ast->firstSourceLocation();
- error->message = message;
- return false;
- };
-
- QQmlJS::AST::BoundNames arguments;
- if (ast->formals)
- arguments = ast->formals->formals();
-
- if (function->argumentTypes.isEmpty()) {
- for (const QQmlJS::AST::BoundName &argument : qAsConst(arguments)) {
- if (argument.typeAnnotation) {
- const auto rawType = m_typeResolver->typeFromAST(argument.typeAnnotation->type);
- if (m_typeResolver->storedType(
- rawType, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- function->argumentTypes.append(rawType);
- continue;
- } else {
- return fail(QStringLiteral("Cannot store the argument type %1.")
- .arg(rawType ? rawType->internalName() : "<unknown>"));
- }
- } else {
- return fail(
- QStringLiteral("Functions without type annotations won't be compiled"));
- return false;
- }
- }
- }
-
- QQmlJSTypePropagator propagator(m_unitGenerator, m_typeResolver.get(), m_logger, m_typeInfo);
-
- if (!function->returnType) {
- if (ast->typeAnnotation) {
- function->returnType = m_typeResolver->typeFromAST(ast->typeAnnotation->type);
- if (!function->returnType)
- return fail(QStringLiteral("Cannot resolve return type"));
- }
- }
-
- if (function->returnType) {
- if (!m_typeResolver->storedType(
- function->returnType, QQmlJSTypeResolver::ComponentIsGeneric::Yes)) {
- return fail(QStringLiteral("Cannot store the return type %1.")
- .arg(function->returnType->internalName()));
- }
- }
-
- function->isSignalHandler = !function->returnType
- && contextType == QV4::Compiler::ContextType::Binding;
- function->addressableScopes = m_typeResolver->objectsById();
- function->code = context->code;
- function->sourceLocations = context->sourceLocationTable.get();
-
- QQmlJSCompilePass::InstructionAnnotations annotations = propagator.run(function, error);
- if (!error->isValid()) {
- QQmlJSShadowCheck shadowCheck(m_unitGenerator, m_typeResolver.get(), m_logger);
- shadowCheck.run(&annotations, function, error);
- }
- if (error->isValid()) {
- error->type = context->returnsClosure ? QtDebugMsg : QtWarningMsg;
- return false;
- }
-
- return true;
-}
diff --git a/tools/qmllint/codegen.h b/tools/qmllint/codegen.h
deleted file mode 100644
index 1a9dff3ed8..0000000000
--- a/tools/qmllint/codegen.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 CODEGEN_H
-#define CODEGEN_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 <QString>
-#include <QFile>
-#include <QList>
-
-#include <variant>
-#include <memory>
-#include <private/qqmljsdiagnosticmessage_p.h>
-#include <private/qqmlirbuilder_p.h>
-#include <private/qqmljsscope_p.h>
-#include <private/qqmljscompiler_p.h>
-
-#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
-#include <QtQmlCompiler/private/qqmljslogger_p.h>
-#include <QtQmlCompiler/private/qqmljscompilepass_p.h>
-
-class Codegen : public QQmlJSAotCompiler
-{
-public:
- Codegen(QQmlJSImporter *importer, const QString &fileName, const QStringList &qmltypesFiles,
- QQmlJSLogger *logger, QQmlJSTypeInfo *typeInfo, const QString &m_code);
-
- void setDocument(QmlIR::JSCodeGen *codegen, QmlIR::Document *document) override;
- void setScope(const QmlIR::Object *object, const QmlIR::Object *scope) override;
- std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage>
- compileBinding(const QV4::Compiler::Context *context, const QmlIR::Binding &irBinding) override;
- std::variant<QQmlJSAotFunction, QQmlJS::DiagnosticMessage>
- compileFunction(const QV4::Compiler::Context *context,
- const QmlIR::Function &irFunction) override;
-
- QQmlJSAotFunction globalCode() const override;
-
-private:
- using Function = QQmlJSCompilePass::Function;
-
- const QmlIR::Document *m_document = nullptr;
- const QString m_fileName;
- const QStringList m_resourceFiles;
- const QStringList m_qmltypesFiles;
- QQmlJSImporter *m_importer = nullptr;
-
- QQmlJS::MemoryPool *m_pool = nullptr;
- const QmlIR::Object *m_currentObject = nullptr;
- QQmlJSScope::ConstPtr m_scopeType;
- QQmlJSScope::ConstPtr m_objectType;
- QV4::Compiler::JSUnitGenerator *m_unitGenerator = nullptr;
- QStringList m_entireSourceCodeLines;
- QQmlJSLogger *m_logger;
- QQmlJSTypeInfo *m_typeInfo;
- const QString m_code;
- std::unique_ptr<QQmlJSTypeResolver> m_typeResolver;
-
- QQmlJS::DiagnosticMessage diagnose(const QString &message, QtMsgType type,
- const QQmlJS::SourceLocation &location);
- bool generateFunction(QV4::Compiler::ContextType contextType,
- const QV4::Compiler::Context *context,
- QQmlJS::AST::FunctionExpression *ast,
- Function *function,
- QQmlJS::DiagnosticMessage *error) const;
-};
-
-#endif
diff --git a/tools/qmllint/codegenwarninginterface.cpp b/tools/qmllint/codegenwarninginterface.cpp
deleted file mode 100644
index 11e39abbb0..0000000000
--- a/tools/qmllint/codegenwarninginterface.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 "codegenwarninginterface.h"
-
-#include <QtQmlCompiler/private/qqmljslogger_p.h>
-
-void CodegenWarningInterface::reportVarUsedBeforeDeclaration(
- const QString &name, const QString &fileName, QQmlJS::SourceLocation declarationLocation,
- QQmlJS::SourceLocation accessLocation)
-{
- Q_UNUSED(fileName)
- m_logger->logWarning(
- u"Variable \"%1\" is used here before its declaration. The declaration is at %2:%3."_qs
- .arg(name)
- .arg(declarationLocation.startLine)
- .arg(declarationLocation.startColumn),
- Log_Type, accessLocation);
-}
diff --git a/tools/qmllint/codegenwarninginterface.h b/tools/qmllint/codegenwarninginterface.h
deleted file mode 100644
index e0e7078629..0000000000
--- a/tools/qmllint/codegenwarninginterface.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 CODEGENWARNINGINTERFACE_H
-#define CODEGENWARNINGINTERFACE_H
-
-#include <QtQml/private/qv4codegen_p.h>
-
-QT_FORWARD_DECLARE_CLASS(QQmlJSLogger)
-
-class CodegenWarningInterface final : public QV4::Compiler::CodegenWarningInterface
-{
-public:
- CodegenWarningInterface(QQmlJSLogger *logger) : m_logger(logger) { }
-
- void reportVarUsedBeforeDeclaration(const QString &name, const QString &fileName,
- QQmlJS::SourceLocation declarationLocation,
- QQmlJS::SourceLocation accessLocation) override;
-
-private:
- QQmlJSLogger *m_logger;
-};
-
-#endif // CODEGENWARNINGINTERFACE_H
diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp
deleted file mode 100644
index 4943593236..0000000000
--- a/tools/qmllint/findwarnings.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/****************************************************************************
-**
-** 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 "findwarnings.h"
-
-#include <QtQmlCompiler/private/qqmljsscope_p.h>
-#include <QtQmlCompiler/private/qqmljstypedescriptionreader_p.h>
-#include <QtQmlCompiler/private/qqmljstypereader_p.h>
-
-#include <QtQml/private/qqmljsast_p.h>
-#include <QtQml/private/qqmljslexer_p.h>
-#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qqmlimportresolver_p.h>
-
-#include <QtCore/qfile.h>
-#include <QtCore/qdiriterator.h>
-#include <QtCore/qscopedvaluerollback.h>
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
-{
- QQmlJSImportVisitor::visit(uiod);
-
- const QString name = m_currentScope->baseTypeName();
- if (name.endsWith(u"Connections"_qs)) {
- QString target;
- auto member = uiod->initializer->members;
- while (member) {
- if (member->member->kind == QQmlJS::AST::Node::Kind_UiScriptBinding) {
- auto asBinding = static_cast<QQmlJS::AST::UiScriptBinding *>(member->member);
- if (asBinding->qualifiedId->name == u"target"_qs) {
- if (asBinding->statement->kind == QQmlJS::AST::Node::Kind_ExpressionStatement) {
- auto expr = static_cast<QQmlJS::AST::ExpressionStatement *>(
- asBinding->statement)
- ->expression;
- if (auto idexpr =
- QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(expr)) {
- target = idexpr->name.toString();
- } else {
- // more complex expressions are not supported
- }
- }
- break;
- }
- }
- member = member->next;
- }
- QQmlJSScope::ConstPtr targetScope;
- if (target.isEmpty()) {
- // no target set, connection comes from parentF
- QQmlJSScope::Ptr scope = m_currentScope;
- do {
- if (auto parentScope = scope->parentScope(); !parentScope.isNull())
- scope = parentScope;
- else
- break;
- } while (scope->scopeType() != QQmlJSScope::QMLScope);
- targetScope = m_rootScopeImports.value(scope->baseTypeName());
- } else {
- // there was a target, check if we already can find it
- auto scopeIt = m_scopesById.find(target);
- if (scopeIt != m_scopesById.end()) {
- targetScope = *scopeIt;
- } else {
- m_outstandingConnections.push_back({ target, m_currentScope, uiod });
- return false; // visit children later once target is known
- }
- }
- for (const auto scope = targetScope; targetScope; targetScope = targetScope->baseType()) {
- const auto connectionMethods = targetScope->ownMethods();
- for (const auto &method : connectionMethods)
- m_currentScope->addOwnMethod(method);
- }
- }
-
- m_objectDefinitionScopes << m_currentScope;
- return true;
-}
-
-void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *uiod)
-{
- auto childScope = m_currentScope;
- QQmlJSImportVisitor::endVisit(uiod);
-
- if (m_currentScope == m_globalScope
- || m_currentScope->baseTypeName() == QStringLiteral("Component")) {
- return;
- }
-
- QString parentPropertyName = childScope->parentPropertyName();
- if (parentPropertyName.isEmpty())
- return;
-
- auto property = childScope->property(parentPropertyName);
- property.setType(QQmlJSScope::ConstPtr(m_currentScope));
-
- if (childScope->hasOwnProperty(parentPropertyName)) {
- Q_ASSERT(childScope->ownProperty(parentPropertyName).index() >= 0);
- } else {
- // it's a new property, so must adjust the index. the index is
- // "outdated" as it's a relative index of scope, not childScope (or
- // it might even be -1 in theory but this is likely an error)
- property.setIndex(childScope->ownProperties().size());
- }
-
- // TODO: This is bad. We shouldn't add a new property but rather amend the existing one.
- childScope->addOwnProperty(property);
-}
-
-FindWarningVisitor::FindWarningVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
- QStringList qmltypesFiles,
- QList<QQmlJS::SourceLocation> comments)
- : QQmlJSImportVisitor(
- importer, logger,
- implicitImportDirectory(logger->fileName(), importer->resourceFileMapper()),
- qmltypesFiles)
-{
- parseComments(comments);
-}
-
-void FindWarningVisitor::parseComments(const QList<QQmlJS::SourceLocation> &comments)
-{
- QHash<int, QSet<QQmlJSLoggerCategory>> disablesPerLine;
- QHash<int, QSet<QQmlJSLoggerCategory>> enablesPerLine;
- QHash<int, QSet<QQmlJSLoggerCategory>> oneLineDisablesPerLine;
-
- const QString code = m_logger->code();
- const QStringList lines = code.split(u'\n');
-
- for (const auto &loc : comments) {
- const QString comment = code.mid(loc.offset, loc.length);
- if (!comment.startsWith(u" qmllint ") && !comment.startsWith(u"qmllint "))
- continue;
-
- QStringList words = comment.split(u' ');
- if (words.constFirst().isEmpty())
- words.removeFirst();
-
- const QString command = words.at(1);
-
- QSet<QQmlJSLoggerCategory> categories;
- for (qsizetype i = 2; i < words.size(); i++) {
- const QString category = words.at(i);
- const auto option = m_logger->options().constFind(category);
- if (option != m_logger->options().constEnd())
- categories << option->m_category;
- else
- m_logger->logWarning(
- u"qmllint directive on unknown category \"%1\""_qs.arg(category),
- Log_Syntax, loc);
- }
-
- if (categories.isEmpty()) {
- for (const auto &option : m_logger->options())
- categories << option.m_category;
- }
-
- if (command == u"disable"_qs) {
- const QString line = lines[loc.startLine - 1];
- const QString preComment = line.left(line.indexOf(comment) - 2);
-
- bool lineHasContent = false;
- for (qsizetype i = 0; i < preComment.size(); i++) {
- if (!preComment[i].isSpace()) {
- lineHasContent = true;
- break;
- }
- }
-
- if (lineHasContent)
- oneLineDisablesPerLine[loc.startLine] |= categories;
- else
- disablesPerLine[loc.startLine] |= categories;
- } else if (command == u"enable"_qs) {
- enablesPerLine[loc.startLine + 1] |= categories;
- } else {
- m_logger->logWarning(u"Invalid qmllint directive \"%1\" provided"_qs.arg(command),
- Log_Syntax, loc);
- }
- }
-
- if (disablesPerLine.isEmpty() && oneLineDisablesPerLine.isEmpty())
- return;
-
- QSet<QQmlJSLoggerCategory> currentlyDisabled;
- for (qsizetype i = 1; i <= lines.length(); i++) {
- currentlyDisabled.unite(disablesPerLine[i]).subtract(enablesPerLine[i]);
-
- currentlyDisabled.unite(oneLineDisablesPerLine[i]);
-
- if (!currentlyDisabled.isEmpty())
- m_logger->ignoreWarnings(i, currentlyDisabled);
-
- currentlyDisabled.subtract(oneLineDisablesPerLine[i]);
- }
-}
-
-bool FindWarningVisitor::check()
-{
- // now that all ids are known, revisit any Connections whose target were perviously unknown
- for (auto const &outstandingConnection: m_outstandingConnections) {
- auto targetScope = m_scopesById[outstandingConnection.targetName];
- if (outstandingConnection.scope) {
- for (const auto scope = targetScope; targetScope;
- targetScope = targetScope->baseType()) {
- const auto connectionMethods = targetScope->ownMethods();
- for (const auto &method : connectionMethods)
- outstandingConnection.scope->addOwnMethod(method);
- }
- }
- QScopedValueRollback<QQmlJSScope::Ptr> rollback(m_currentScope,
- outstandingConnection.scope);
- outstandingConnection.uiod->initializer->accept(this);
- }
-
- return !m_logger->hasWarnings() && !m_logger->hasErrors();
-}
diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h
deleted file mode 100644
index 8d9475f467..0000000000
--- a/tools/qmllint/findwarnings.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************************
-**
-** 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 FINDUNQUALIFIED_H
-#define FINDUNQUALIFIED_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 <QtQmlCompiler/private/qcoloroutput_p.h>
-#include <QtQmlCompiler/private/qqmljstypedescriptionreader_p.h>
-#include <QtQmlCompiler/private/qqmljsscope_p.h>
-#include <QtQmlCompiler/private/qqmljsimporter_p.h>
-#include <QtQmlCompiler/private/qqmljsimportvisitor_p.h>
-
-#include <QtQml/private/qqmldirparser_p.h>
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-#include <QtCore/qscopedpointer.h>
-
-class FindWarningVisitor : public QQmlJSImportVisitor
-{
- Q_DISABLE_COPY_MOVE(FindWarningVisitor)
-public:
- explicit FindWarningVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
- QStringList qmltypesFiles, QList<QQmlJS::SourceLocation> comments);
- ~FindWarningVisitor() override = default;
- bool check();
-
-private:
- void parseComments(const QList<QQmlJS::SourceLocation> &comments);
-
- // work around compiler error in clang11
- using QQmlJSImportVisitor::visit;
- using QQmlJSImportVisitor::endVisit;
-
- bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override;
- void endVisit(QQmlJS::AST::UiObjectDefinition *uiod) override;
-};
-
-#endif // FINDUNQUALIFIED_H
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 3df510ed79..b7b64f0b2b 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -26,20 +26,13 @@
**
****************************************************************************/
-#include "findwarnings.h"
-#include "codegen.h"
-#include "codegenwarninginterface.h"
#include "../shared/qqmltoolingsettings.h"
+#include <QtQmlLint/private/qqmllinter_p.h>
+
#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
#include <QtQmlCompiler/private/qqmljscompiler_p.h>
-#include <QtQml/private/qqmljslexer_p.h>
-#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qqmljsengine_p.h>
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
@@ -48,7 +41,6 @@
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
-#include <QtCore/qloggingcategory.h>
#include <QtCore/qscopeguard.h>
#if QT_CONFIG(commandlineparser)
@@ -61,171 +53,6 @@
constexpr int JSON_LOGGING_FORMAT_REVISION = 1;
-static bool lint_file(const QString &filename, const bool silent, const bool useAbsolutePath,
- QJsonArray *json, const QStringList &qmlImportPaths,
- const QStringList &qmltypesFiles, const QStringList &resourceFiles,
- const QMap<QString, QQmlJSLogger::Option> &options, QQmlJSImporter &importer)
-{
- QJsonArray warnings;
- QJsonObject result;
-
- bool success = true;
-
- QScopeGuard jsonOutput([&] {
- if (!json)
- return;
-
- result[u"filename"_qs] = QFileInfo(filename).absoluteFilePath();
- result[u"warnings"] = warnings;
- result[u"success"] = success;
-
- json->append(result);
- });
-
- auto addJsonWarning = [&](const QQmlJS::DiagnosticMessage &message) {
- QJsonObject jsonMessage;
-
- QString type;
- switch (message.type) {
- case QtDebugMsg:
- type = "debug";
- break;
- case QtWarningMsg:
- type = "warning";
- break;
- case QtCriticalMsg:
- type = "critical";
- break;
- case QtFatalMsg:
- type = "fatal";
- break;
- case QtInfoMsg:
- type = "info";
- break;
- default:
- type = "unknown";
- break;
- }
-
- jsonMessage[u"type"_qs] = type;
-
- if (message.loc.isValid()) {
- jsonMessage[u"line"_qs] = static_cast<int>(message.loc.startLine);
- jsonMessage[u"column"_qs] = static_cast<int>(message.loc.startColumn);
- jsonMessage[u"charOffset"_qs] = static_cast<int>(message.loc.offset);
- jsonMessage[u"length"_qs] = static_cast<int>(message.loc.length);
- }
-
- jsonMessage[u"message"_qs] = message.message;
-
- warnings << jsonMessage;
- };
-
- QFile file(filename);
- if (!file.open(QFile::ReadOnly)) {
- if (json) {
- result[u"openFailed"] = true;
- success = false;
- } else if (!silent) {
- qWarning() << "Failed to open file" << filename << file.error();
- }
- return false;
- }
-
- QString code = QString::fromUtf8(file.readAll());
- file.close();
-
- QQmlJS::Engine engine;
- QQmlJS::Lexer lexer(&engine);
-
- QFileInfo info(filename);
- const QString lowerSuffix = info.suffix().toLower();
- const bool isESModule = lowerSuffix == QLatin1String("mjs");
- const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
-
- lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/ !isJavaScript);
- QQmlJS::Parser parser(&engine);
-
- success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram())
- : parser.parse();
-
- if (!success && !silent) {
- const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- if (json) {
- addJsonWarning(m);
- } else {
- qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
- .arg(filename)
- .arg(m.loc.startLine)
- .arg(m.message);
- }
- }
- }
-
- if (success && !isJavaScript) {
- const auto check = [&](QQmlJSResourceFileMapper *mapper) {
- if (importer.importPaths() != qmlImportPaths)
- importer.setImportPaths(qmlImportPaths);
-
- importer.setResourceFileMapper(mapper);
-
- QQmlJSLogger logger(useAbsolutePath ? info.absoluteFilePath() : filename, code,
- silent || json);
- FindWarningVisitor v {
- &importer,
- &logger,
- qmltypesFiles,
- engine.comments(),
- };
-
- for (auto it = options.cbegin(); it != options.cend(); ++it) {
- logger.setCategoryError(it.value().m_category, it.value().m_error);
- logger.setCategoryLevel(it.value().m_category, it.value().m_level);
- }
-
- parser.rootNode()->accept(&v);
- success = v.check();
-
- if (logger.hasErrors())
- return;
-
- QQmlJSTypeInfo typeInfo;
- Codegen codegen { &importer, filename, qmltypesFiles, &logger, &typeInfo, code };
- QQmlJSSaveFunction saveFunction = [](const QV4::CompiledData::SaveableUnitPointer &,
- const QQmlJSAotFunctionMap &,
- QString *) { return true; };
-
- QQmlJSCompileError error;
-
- QLoggingCategory::setFilterRules(u"qt.qml.compiler=false"_qs);
-
- CodegenWarningInterface interface(&logger);
- qCompileQmlFile(filename, saveFunction, &codegen, &error, true, &interface);
-
- success &= !logger.hasWarnings() && !logger.hasErrors();
-
- if (json) {
- for (const auto &error : logger.errors())
- addJsonWarning(error);
- for (const auto &warning : logger.warnings())
- addJsonWarning(warning);
- for (const auto &info : logger.infos())
- addJsonWarning(info);
- }
- };
-
- if (resourceFiles.isEmpty()) {
- check(nullptr);
- } else {
- QQmlJSResourceFileMapper mapper(resourceFiles);
- check(&mapper);
- }
- }
-
- return success;
-}
-
int main(int argv, char *argc[])
{
qSetGlobalQHashSeed(0);
@@ -394,7 +221,7 @@ All warnings can be set to three levels:
QStringList resourceFiles {};
#endif
bool success = true;
- QQmlJSImporter importer(qmlImportPaths, nullptr);
+ QQmlLinter linter(qmlImportPaths, useAbsolutePath);
QJsonArray jsonFiles;
@@ -439,8 +266,8 @@ All warnings can be set to three levels:
const auto arguments = app.arguments();
for (const QString &filename : arguments) {
#endif
- success &= lint_file(filename, silent, useAbsolutePath, useJson ? &jsonFiles : nullptr,
- qmlImportPaths, qmltypesFiles, resourceFiles, options, importer);
+ success &= linter.lintFile(filename, silent, useJson ? &jsonFiles : nullptr, qmlImportPaths,
+ qmltypesFiles, resourceFiles, options);
}
if (useJson) {