aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmllint
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2021-06-07 14:25:01 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-06-09 12:47:20 +0000
commit4489266724b98e9777ab0509a57aac9f032a3b30 (patch)
tree3bb8bee97bd03dd2909e39e012fd000dfa4c7193 /tools/qmllint
parentb5b0a39a2aa3ec98e221bc2ef0080d9b879304d5 (diff)
qmllint: Move a lot of warning logic to qmlcompiler
Moves the majority of qmllint warning logic to qmlcompiler, making them available to all tools using the library. The end goal is to get rid off the additional AST visitor in qmllint altogether (We can't quite yet until we have ported over type interference). This also prepares qmlcompiler to move to a two pass approach which isn't fully implemented in here yet due to the size of the change. Change-Id: Id2e108340d26a75085ce6ed97d56dec03ea3a12d Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 3c95bf5b6df7cfd12af3755d11b34db721e23f19) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'tools/qmllint')
-rw-r--r--tools/qmllint/checkidentifiers.cpp8
-rw-r--r--tools/qmllint/checkidentifiers.h14
-rw-r--r--tools/qmllint/findwarnings.cpp612
-rw-r--r--tools/qmllint/findwarnings.h44
4 files changed, 82 insertions, 596 deletions
diff --git a/tools/qmllint/checkidentifiers.cpp b/tools/qmllint/checkidentifiers.cpp
index 72e0d6fd7d..93edb08ba0 100644
--- a/tools/qmllint/checkidentifiers.cpp
+++ b/tools/qmllint/checkidentifiers.cpp
@@ -250,9 +250,9 @@ void CheckIdentifiers::checkMemberAccess(const QVector<FieldMember> &members,
void CheckIdentifiers::operator()(
const QHash<QString, QQmlJSScope::ConstPtr> &qmlIDs,
- const QHash<QQmlJS::SourceLocation, SignalHandler> &signalHandlers,
- const MemberAccessChains &memberAccessChains,
- const QQmlJSScope::ConstPtr &root, const QString &rootId) const
+ const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers,
+ const MemberAccessChains &memberAccessChains, const QQmlJSScope::ConstPtr &root,
+ const QString &rootId) const
{
// revisit all scopes
QQueue<QQmlJSScope::ConstPtr> workQueue;
@@ -442,7 +442,7 @@ void CheckIdentifiers::operator()(
const auto handler = signalHandlers[id.location];
colorOut.write(QLatin1String(handler.isMultiline ? "function(" : "("), QtInfoMsg);
- const auto parameters = handler.signal.parameterNames();
+ const auto parameters = handler.signalParameters;
for (int numParams = parameters.size(); numParams > 0; --numParams) {
colorOut.write(parameters.at(parameters.size() - numParams), QtInfoMsg);
if (numParams > 1)
diff --git a/tools/qmllint/checkidentifiers.h b/tools/qmllint/checkidentifiers.h
index c23a64f9bd..81f86f7aeb 100644
--- a/tools/qmllint/checkidentifiers.h
+++ b/tools/qmllint/checkidentifiers.h
@@ -32,14 +32,10 @@
#include <QtQmlCompiler/private/qqmljslogger_p.h>
#include <QtQmlCompiler/private/qqmljsscope_p.h>
#include <QtQmlCompiler/private/qqmljsimporter_p.h>
+#include <QtQmlCompiler/private/qqmljsmetatypes_p.h>
class QColorOutput;
-struct SignalHandler {
- QQmlJSMetaMethod signal;
- bool isMultiline;
-};
-
struct FieldMember
{
QString m_name;
@@ -57,10 +53,10 @@ public:
m_logger(logger), m_code(code), m_types(types), m_fileName(fileName)
{}
- void operator ()(const QHash<QString, QQmlJSScope::ConstPtr> &qmlIDs,
- const QHash<QQmlJS::SourceLocation, SignalHandler> &signalHandlers,
- const MemberAccessChains &memberAccessChains,
- const QQmlJSScope::ConstPtr &root, const QString &rootId) const;
+ void operator()(const QHash<QString, QQmlJSScope::ConstPtr> &qmlIDs,
+ const QHash<QQmlJS::SourceLocation, QQmlJSMetaSignalHandler> &signalHandlers,
+ const MemberAccessChains &memberAccessChains, const QQmlJSScope::ConstPtr &root,
+ const QString &rootId) const;
private:
void checkMemberAccess(const QVector<FieldMember> &members,
diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp
index 916962a12d..c677e112dd 100644
--- a/tools/qmllint/findwarnings.cpp
+++ b/tools/qmllint/findwarnings.cpp
@@ -36,376 +36,105 @@
#include <QtQml/private/qqmljsast_p.h>
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qv4codegen_p.h>
#include <QtQml/private/qqmlimportresolver_p.h>
#include <QtCore/qfile.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qscopedvaluerollback.h>
-void FindWarningVisitor::checkInheritanceCycle(QQmlJSScope::ConstPtr scope)
+bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
{
- QQmlJSScope::ConstPtr originalScope = scope;
- QList<QQmlJSScope::ConstPtr> scopes;
- while (!scope.isNull()) {
-
- for (const QQmlJSAnnotation &annotation : scope->annotations()) {
- if (annotation.isDeprecation()) {
- QQQmlJSDeprecation deprecation = annotation.deprecation();
-
- QString message = QStringLiteral("Type \"%1\" is deprecated")
- .arg(scope->internalName());
+ QQmlJSImportVisitor::visit(uiod);
- if (!deprecation.reason.isEmpty())
- message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
-
- m_logger.log(message, Log_Deprecation, originalScope->sourceLocation()
- );
- }
- }
+ const QString name = m_currentScope->baseTypeName();
+ if (name.isEmpty() || name.front().isLower())
+ return false; // Ignore grouped properties for now
- if (scopes.contains(scope)) {
- QString inheritenceCycle;
- for (const auto &seen: qAsConst(scopes)) {
- if (!inheritenceCycle.isEmpty())
- inheritenceCycle.append(QLatin1String(" -> "));
- inheritenceCycle.append(seen->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;
+ }
}
-
- m_logger.log(QStringLiteral("%1 is part of an inheritance cycle: %2")
- .arg(scope->internalName())
- .arg(inheritenceCycle),
- Log_InheritanceCycle);
-
- m_unknownImports.insert(scope->internalName());
- break;
+ member = member->next;
}
-
- scopes.append(scope);
-
- if (scope->baseTypeName().isEmpty()) {
- break;
- } else if (auto newScope = scope->baseType()) {
- scope = newScope;
+ QQmlJSScope::ConstPtr targetScope;
+ if (target.isEmpty()) {
+ // no target set, connection comes from parentF
+ QQmlJSScope::Ptr scope = m_currentScope;
+ do {
+ scope = scope->parentScope(); // TODO: rename method
+ } while (scope->scopeType() != QQmlJSScope::QMLScope);
+ targetScope = m_rootScopeImports.value(scope->baseTypeName());
} else {
- m_logger.log(scope->baseTypeName()
- + QStringLiteral(" was not found. Did you add all import paths?"),
- Log_Import);
- m_unknownImports.insert(scope->baseTypeName());
- break;
- }
- }
-}
-
-void FindWarningVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
-{
- // These warnings do not apply for custom parsers and their children and need to be handled on a
- // case by case basis
- if (scope->isInCustomParserParent())
- return;
-
- auto children = scope->childScopes();
- while (!children.isEmpty()) {
- auto childScope = children.takeFirst();
- const auto type = childScope->scopeType();
- switch (type) {
- case QQmlJSScope::GroupedPropertyScope:
- case QQmlJSScope::AttachedPropertyScope:
- if (!childScope->baseType()) {
- m_logger.log(
- QStringLiteral("unknown %1 property scope %2.")
- .arg(type == QQmlJSScope::GroupedPropertyScope
- ? QStringLiteral("grouped")
- : QStringLiteral("attached"),
- childScope->internalName()),
- Log_UnqualifiedAccess,
- childScope->sourceLocation()
- );
+ // 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
}
- children.append(childScope->childScopes());
- default:
- break;
- }
- }
-}
-
-void FindWarningVisitor::flushPendingSignalParameters()
-{
- const SignalHandler handler = m_signalHandlers[m_pendingSingalHandler];
- for (const QString &parameter : handler.signal.parameterNames()) {
- m_currentScope->insertJSIdentifier(
- parameter, {
- QQmlJSScope::JavaScriptIdentifier::Injected,
- m_pendingSingalHandler
- });
- }
- m_pendingSingalHandler = QQmlJS::SourceLocation();
-}
-
-void FindWarningVisitor::checkDefaultProperty(const QQmlJSScope::ConstPtr &scope)
-{
- if (scope == m_exportedRootScope || scope->isArrayScope()
- || scope->isInlineComponent()) // inapplicable
- return;
-
- // These warnings do not apply for custom parsers and their children and need to be handled on a
- // case by case basis
- if (scope->isInCustomParserParent())
- return;
-
- const QQmlJSScope *scopeOfDefaultProperty = nullptr;
- QString defaultPropertyName;
- // NB: start looking for default property in parent scope (because this
- // scope is not suitable), but progress through baseType()
- for (auto s = scope->parentScope(); s; s = s->baseType()) {
- defaultPropertyName = s->defaultPropertyName();
- if (!defaultPropertyName.isEmpty()) {
- scopeOfDefaultProperty = s.data();
- break;
}
-
- // If the parent scope is based on Component it can have any child element
- if (s->internalName() == QStringLiteral("QQmlComponent"))
- return;
- }
- if (defaultPropertyName.isEmpty()) {
- if (scope->parentScope()->isFullyResolved()) {
- m_logger.log(QStringLiteral("Cannot assign to non-existent default property"),
- Log_Property, scope->sourceLocation());
+ for (const auto scope = targetScope; targetScope; targetScope = targetScope->baseType()) {
+ const auto connectionMethods = targetScope->ownMethods();
+ for (const auto &method : connectionMethods)
+ m_currentScope->addOwnMethod(method);
}
- return;
- }
-
- Q_ASSERT(scopeOfDefaultProperty);
- Q_ASSERT(scope->parentScope());
- QQmlJSMetaProperty defaultProp = scopeOfDefaultProperty->property(defaultPropertyName);
-
- const QQmlJSScope *parentScope = scope->parentScope().get();
-
- // abuse QHash feature to construct default value through
- // operator[]. default bool is false, which is what's needed
- if (m_scopeHasDefaultPropertyAssignment[parentScope] && !defaultProp.isList()) {
- // already has some object assigned to a default property and
- // this default property is not a list property
- m_logger.log(QStringLiteral("Cannot assign multiple objects to a default non-list property"),
- Log_Property, scope->sourceLocation());
}
- m_scopeHasDefaultPropertyAssignment[parentScope] = true;
- auto propType = defaultProp.type();
- if (propType.isNull() || !propType->isFullyResolved()
- || !scope->isFullyResolved()) // should be an error somewhere else
- return;
-
- // Assigning any element to a QQmlComponent property implicitly wraps it into a Component
- // Check whether the property can be assigned the scope
- if (propType->canAssign(scope))
- return;
-
- m_logger.log(QStringLiteral("Cannot assign to default property of incompatible type"),
- Log_Property, scope->sourceLocation());
-}
-
-void FindWarningVisitor::throwRecursionDepthError()
-{
- QQmlJSImportVisitor::throwRecursionDepthError();
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
-{
- if (m_pendingSingalHandler.isValid()) {
- enterEnvironment(QQmlJSScope::JSFunctionScope, "signalhandler", ast->firstSourceLocation());
- flushPendingSignalParameters();
- }
- return true;
-}
-
-void FindWarningVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
-{
- if (m_currentScope->scopeType() == QQmlJSScope::JSFunctionScope
- && m_currentScope->baseTypeName() == "signalhandler") {
- leaveEnvironment();
- }
-}
+ addDefaultProperties();
+ m_objectDefinitionScopes << m_currentScope;
-bool FindWarningVisitor::visit(QQmlJS::AST::Block *block)
-{
- if (!QQmlJSImportVisitor::visit(block))
- return false;
- if (m_pendingSingalHandler.isValid())
- flushPendingSignalParameters();
return true;
}
-bool FindWarningVisitor::visit(QQmlJS::AST::WithStatement *withStatement)
-{
- m_logger.log(QStringLiteral("with statements are strongly discouraged in QML "
- "and might cause false positives when analysing unqualified "
- "identifiers"),
- Log_WithStatement, withStatement->firstSourceLocation());
-
- return QQmlJSImportVisitor::visit(withStatement);
-}
-
-static QString signalName(QStringView handlerName)
-{
- if (handlerName.startsWith(u"on") && handlerName.size() > 2) {
- QString signal = handlerName.mid(2).toString();
- for (int i = 0; i < signal.length(); ++i) {
- QChar &ch = signal[i];
- if (ch.isLower())
- return QString();
- if (ch.isUpper()) {
- ch = ch.toLower();
- return signal;
- }
- }
- }
- return QString();
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
+void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *uiod)
{
- using namespace QQmlJS::AST;
-
- const auto qmlScope = m_currentScope;
- if (!QQmlJSImportVisitor::visit(uisb))
- return false;
-
- auto name = uisb->qualifiedId->name;
- if (name == QLatin1String("id")) {
- // Figure out whether the current scope is the root scope.
- if (auto parentScope = qmlScope->parentScope()) {
- if (!parentScope->parentScope()) {
- const auto expstat = cast<ExpressionStatement *>(uisb->statement);
- const auto identexp = cast<IdentifierExpression *>(expstat->expression);
- m_rootId = identexp->name.toString();
- }
- }
- return true;
- }
-
- if (!qmlScope->isFullyResolved())
- return true;
-
- const QString signal = signalName(name);
- if (signal.isEmpty()) {
- for (const auto &childScope : qmlScope->childScopes()) {
- if ((childScope->scopeType() == QQmlJSScope::AttachedPropertyScope
- || childScope->scopeType() == QQmlJSScope::GroupedPropertyScope)
- && childScope->internalName() == name) {
- return true;
- }
- }
-
- if (!qmlScope->hasProperty(name.toString())) {
- // These warnings do not apply for custom parsers and their children and need to be
- // handled on a case by case basis
-
- if (qmlScope->isInCustomParserParent())
- return true;
-
- // TODO: Can this be in a better suited category?
- m_logger.log(QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
- "exists in the current element.")
- .arg(name),
- Log_Type, uisb->firstSourceLocation());
- return true;
- }
-
- const auto property = qmlScope->property(name.toString());
- if (!property.type()) {
- m_logger.log(QStringLiteral("No type found for property \"%1\". This may be due "
- "to a missing import statement or incomplete "
- "qmltypes files.")
- .arg(name),
- Log_Type, uisb->firstSourceLocation());
- }
-
- const auto &annotations = property.annotations();
-
- const auto deprecationAnn = std::find_if(annotations.cbegin(), annotations.cend(), [](const QQmlJSAnnotation &ann) { return ann.isDeprecation(); });
-
- if (deprecationAnn != annotations.cend()) {
- const auto deprecation = deprecationAnn->deprecation();
-
- QString message = QStringLiteral("Binding on deprecated property \"%1\"")
- .arg(property.propertyName());
-
- if (!deprecation.reason.isEmpty())
- message.append(QStringLiteral(" (Reason: %1)").arg(deprecation.reason));
-
- m_logger.log(message, Log_Deprecation, uisb->firstSourceLocation());
- }
-
+ auto childScope = m_currentScope;
+ QQmlJSImportVisitor::endVisit(uiod);
- return true;
+ if (m_currentScope == m_globalScope
+ || m_currentScope->baseTypeName() == QStringLiteral("Component")) {
+ return;
}
+ QString parentPropertyName;
+ for (QQmlJSScope::ConstPtr scope = childScope; scope; scope = scope->baseType()) {
+ parentPropertyName = scope->parentPropertyName();
+ if (parentPropertyName.isEmpty())
+ continue;
- if (!qmlScope->hasMethod(signal)) {
- m_logger.log(
- QStringLiteral("no matching signal found for handler \"%1\"").arg(name.toString()),
- Log_UnqualifiedAccess, uisb->firstSourceLocation());
- return true;
- }
+ auto property = scope->property(parentPropertyName);
+ property.setType(QQmlJSScope::ConstPtr(m_currentScope));
- QQmlJSMetaMethod scopeSignal;
- for (QQmlJSScope::ConstPtr scope = qmlScope; scope; scope = scope->baseType()) {
- const auto methods = scope->ownMethods();
- const auto methodsRange = methods.equal_range(signal);
- for (auto method = methodsRange.first; method != methodsRange.second; ++method) {
- if (method->methodType() != QQmlJSMetaMethod::Signal)
- continue;
- scopeSignal = *method;
- break;
+ 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());
}
- }
-
- const auto statement = uisb->statement;
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
- if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
- // functions are already handled
- // they do not get names inserted according to the signal, but access their formal
- // parameters. Let's still check if the names match, though.
- const QStringList signalParameters = scopeSignal.parameterNames();
- qsizetype i = 0, end = signalParameters.length();
- for (FormalParameterList *formal = func->formals;
- formal; ++i, formal = formal->next) {
- if (i == end) {
- m_logger.log(
- QStringLiteral("Signal handler for \"%2\" has more formal"
- " parameters than the signal it handles.")
- .arg(name),
- Log_Signal,
- uisb->firstSourceLocation()
- );
- }
- const QStringView handlerParameter = formal->element->bindingIdentifier;
- const qsizetype j = signalParameters.indexOf(handlerParameter);
- if (j == i || j < 0)
- continue;
-
- m_logger.log(QStringLiteral("Parameter %1 to signal handler for \"%2\""
- " is called \"%3\". The signal has a parameter"
- " of the same name in position %4.")
- .arg(i + 1)
- .arg(name, handlerParameter)
- .arg(j + 1),
- Log_Signal, uisb->firstSourceLocation());
- }
-
- return true;
- }
+ // TODO: This is bad. We shouldn't add a new property but rather amend the existing one.
+ childScope->addOwnProperty(property);
}
-
- const auto firstSourceLocation = statement->firstSourceLocation();
- bool hasMultilineStatementBody
- = statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
- m_pendingSingalHandler = firstSourceLocation;
- m_signalHandlers.insert(firstSourceLocation, {scopeSignal, hasMultilineStatementBody});
- return true;
}
bool FindWarningVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
@@ -426,38 +155,8 @@ FindWarningVisitor::FindWarningVisitor(QQmlJSImporter *importer, QStringList qml
QString fileName, bool silent)
: QQmlJSImportVisitor(importer,
implicitImportDirectory(fileName, importer->resourceFileMapper()),
- qmltypesFiles, fileName, code, silent),
- m_code(code),
- m_rootId(QLatin1String("<id>")),
- m_filePath(fileName)
+ qmltypesFiles, fileName, code, silent)
{
- m_currentScope->setInternalName("global");
-
- QLatin1String jsGlobVars[] = {
- /* Not listed on the MDN page; browser and QML extensions: */
- // console/debug api
- QLatin1String("console"), QLatin1String("print"),
- // garbage collector
- QLatin1String("gc"),
- // i18n
- QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"),
- QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"),
- // XMLHttpRequest
- QLatin1String("XMLHttpRequest")
- };
-
- QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
- QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- QQmlJS::SourceLocation()
- };
- for (const char **globalName = QV4::Compiler::Codegen::s_globalNames;
- *globalName != nullptr;
- ++globalName) {
- m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), globalJavaScript);
- }
- for (const auto& jsGlobVar: jsGlobVars)
- m_currentScope->insertJSIdentifier(jsGlobVar, globalJavaScript);
-
parseComments(comments);
}
@@ -549,106 +248,17 @@ bool FindWarningVisitor::check()
outstandingConnection.scope->addOwnMethod(method);
}
}
- QScopedValueRollback<QQmlJSScope::Ptr> rollback(m_currentScope, outstandingConnection.scope);
+ QScopedValueRollback<QQmlJSScope::Ptr> rollback(m_currentScope,
+ outstandingConnection.scope);
outstandingConnection.uiod->initializer->accept(this);
}
- auto unusedImports = m_importLocations;
- for (const QString &type : m_usedTypes) {
- for (const auto &importLocation : m_importTypeLocationMap.values(type))
- unusedImports.remove(importLocation);
-
- // If there are no more unused imports left we can abort early
- if (unusedImports.isEmpty())
- break;
- }
-
- for (const auto &import : unusedImports) {
- m_logger.log(QString::fromLatin1("Unused import at %1:%2:%3")
- .arg(m_filePath)
- .arg(import.startLine)
- .arg(import.startColumn),
- Log_UnusedImport, import);
- }
-
CheckIdentifiers check(&m_logger, m_code, m_rootScopeImports, m_filePath);
check(m_scopesById, m_signalHandlers, m_memberAccessChains, m_globalScope, m_rootId);
return !m_logger.hasWarnings() && !m_logger.hasErrors();
}
-bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
-{
- if (!QQmlJSImportVisitor::visit(uiob))
- return false;
-
- checkInheritanceCycle(m_currentScope);
- return true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
-{
- using namespace QQmlJS::AST;
-
- if (!QQmlJSImportVisitor::visit(uiod))
- return false;
-
- const QString name = m_currentScope->baseTypeName();
- if (name.isEmpty() || name.front().isLower())
- return false; // Ignore grouped properties for now
-
- checkInheritanceCycle(m_currentScope);
-
- if (name.endsWith("Connections")) {
- 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 == QLatin1String("target")) {
- 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 {
- scope = scope->parentScope(); // TODO: rename method
- } 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);
- }
- }
-
- checkDefaultProperty(m_currentScope);
-
- return true;
-}
-
bool FindWarningVisitor::visit(QQmlJS::AST::PatternElement *element)
{
if (element->isVariableDeclaration()) {
@@ -668,46 +278,6 @@ bool FindWarningVisitor::visit(QQmlJS::AST::PatternElement *element)
return true;
}
-void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *uiod)
-{
- auto childScope = m_currentScope;
- QQmlJSImportVisitor::endVisit(uiod);
-
- checkGroupedAndAttachedScopes(childScope);
-
- if (m_currentScope == m_globalScope
- || m_currentScope->baseTypeName() == QStringLiteral("Component")) {
- return;
- }
-
- QString parentPropertyName;
- for (QQmlJSScope::ConstPtr scope = childScope; scope; scope = scope->baseType()) {
- parentPropertyName = scope->parentPropertyName();
- if (parentPropertyName.isEmpty())
- continue;
-
- auto property = scope->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);
- }
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::FieldMemberExpression *)
-{
- return true;
-}
-
void FindWarningVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
{
using namespace QQmlJS::AST;
@@ -745,11 +315,6 @@ void FindWarningVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMembe
}
}
-bool FindWarningVisitor::visit(QQmlJS::AST::BinaryExpression *)
-{
- return true;
-}
-
void FindWarningVisitor::endVisit(QQmlJS::AST::BinaryExpression *binExp)
{
if (binExp->op == QSOperator::As
@@ -759,36 +324,3 @@ void FindWarningVisitor::endVisit(QQmlJS::AST::BinaryExpression *binExp)
m_fieldMemberBase = nullptr;
}
}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiPublicMember *uipb)
-{
- QQmlJSImportVisitor::visit(uipb);
- if (uipb->type == QQmlJS::AST::UiPublicMember::Property && uipb->memberType != nullptr
- && !uipb->memberType->name.isEmpty() && uipb->memberType->name != QLatin1String("alias")) {
- const auto name = uipb->memberType->name.toString();
- if (m_rootScopeImports.contains(name) && !m_rootScopeImports[name].isNull()) {
- if (m_importTypeLocationMap.contains(name))
- m_usedTypes.insert(name);
- } else {
- m_logger.log(name + QStringLiteral(" was not found. Did you add all import paths?"),
- Log_Import);
- }
- }
-
- return true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::StringLiteral *sl)
-{
- const QString s = m_code.mid(sl->literalToken.begin(), sl->literalToken.length);
-
- if (s.contains(QLatin1Char('\r')) || s.contains(QLatin1Char('\n')) || s.contains(QChar(0x2028u))
- || s.contains(QChar(0x2029u))) {
- m_logger.log(QStringLiteral("String contains unescaped line terminator which is "
- "deprecated. Use a template "
- "literal instead."),
- Log_MultilineString, sl->literalToken);
- }
-
- return true;
-}
diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h
index e2538fe5b8..b558bcb2c2 100644
--- a/tools/qmllint/findwarnings.h
+++ b/tools/qmllint/findwarnings.h
@@ -64,68 +64,26 @@ public:
bool check();
private:
- QHash<QQmlJS::SourceLocation, SignalHandler> m_signalHandlers;
- QQmlJS::SourceLocation m_pendingSingalHandler;
-
MemberAccessChains m_memberAccessChains;
QQmlJS::AST::ExpressionNode *m_fieldMemberBase = nullptr;
- QString m_code;
- QString m_rootId;
- QString m_filePath;
- QSet<QString> m_unknownImports;
-
- struct OutstandingConnection
- {
- QString targetName;
- QQmlJSScope::Ptr scope;
- QQmlJS::AST::UiObjectDefinition *uiod;
- };
-
- QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
-
- // records of whether a default property has object assigned to it. for
- // correctness, the scope that defines the default property acts as a key
- QHash<const QQmlJSScope *, bool> m_scopeHasDefaultPropertyAssignment;
void parseComments(const QList<QQmlJS::SourceLocation> &comments);
- void checkInheritanceCycle(QQmlJSScope::ConstPtr scope);
- void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
- void flushPendingSignalParameters();
- void checkDefaultProperty(const QQmlJSScope::ConstPtr &scope);
-
- void throwRecursionDepthError() override;
-
// work around compiler error in clang11
using QQmlJSImportVisitor::visit;
using QQmlJSImportVisitor::endVisit;
- // start block/scope handling
- bool visit(QQmlJS::AST::ExpressionStatement *ast) override;
- void endVisit(QQmlJS::AST::ExpressionStatement *ast) override;
- bool visit(QQmlJS::AST::Block *ast) override;
- bool visit(QQmlJS::AST::WithStatement *withStatement) override;
-
- /* --- end block handling --- */
-
- bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override;
- void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
- bool visit(QQmlJS::AST::UiScriptBinding *uisb) override;
- bool visit(QQmlJS::AST::UiPublicMember *uipb) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *uiod) override;
// expression handling
bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
bool visit(QQmlJS::AST::PatternElement *) override;
- bool visit(QQmlJS::AST::FieldMemberExpression *idprop) override;
void endVisit(QQmlJS::AST::FieldMemberExpression *) override;
- bool visit(QQmlJS::AST::BinaryExpression *) override;
void endVisit(QQmlJS::AST::BinaryExpression *) override;
-
- bool visit(QQmlJS::AST::StringLiteral *) override;
};
#endif // FINDUNQUALIFIED_H