diff options
Diffstat (limited to 'src/qmlcompiler')
| -rw-r--r-- | src/qmlcompiler/qqmljslinter.cpp | 2 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljslintervisitor.cpp | 65 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljslintervisitor_p.h | 11 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljslogger.cpp | 4 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljsloggingutils.h | 1 |
5 files changed, 80 insertions, 3 deletions
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp index 595a512992..13f5a0a362 100644 --- a/src/qmlcompiler/qqmljslinter.cpp +++ b/src/qmlcompiler/qqmljslinter.cpp @@ -608,7 +608,7 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename, QQmlJS::LinterVisitor v{ target, &m_importer, m_logger.get(), QQmlJSImportVisitor::implicitImportDirectory( m_logger->filePath(), m_importer.resourceFileMapper()), - qmldirFiles }; + qmldirFiles, &engine }; if (m_enablePlugins) { for (const Plugin &plugin : m_plugins) { diff --git a/src/qmlcompiler/qqmljslintervisitor.cpp b/src/qmlcompiler/qqmljslintervisitor.cpp index e08567a26f..2705fc945e 100644 --- a/src/qmlcompiler/qqmljslintervisitor.cpp +++ b/src/qmlcompiler/qqmljslintervisitor.cpp @@ -17,6 +17,15 @@ namespace QQmlJS { are purely syntactic checks, or style-checks warnings that don't make sense during compilation. */ +LinterVisitor::LinterVisitor( + const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger, + const QString &implicitImportDirectory, const QStringList &qmldirFiles, + QQmlJS::Engine *engine) + : QQmlJSImportVisitor(target, importer, logger, implicitImportDirectory, qmldirFiles) + , m_engine(engine) +{ +} + bool LinterVisitor::visit(StringLiteral *sl) { QQmlJSImportVisitor::visit(sl); @@ -267,6 +276,62 @@ bool LinterVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied) return true; } +void LinterVisitor::checkCaseFallthrough(StatementList *statements, SourceLocation errorLoc, + SourceLocation nextLoc) +{ + if (!statements || !nextLoc.isValid()) + return; + + quint32 afterLastStatement = 0; + for (StatementList *it = statements; it; it = it->next) { + if (!it->next) { + Node::Kind kind = (Node::Kind) it->statement->kind; + if (kind == Node::Kind_BreakStatement || kind == Node::Kind_ReturnStatement + || kind == Node::Kind_ThrowStatement) { + return; + } + afterLastStatement = it->statement->lastSourceLocation().end(); + } + } + + const auto &comments = m_engine->comments(); + auto it = std::find_if(comments.cbegin(), comments.cend(), + [&](auto c) { return afterLastStatement < c.offset; }); + auto end = std::find_if(it, comments.cend(), + [&](auto c) { return c.offset >= nextLoc.offset; }); + + for (; it != end; ++it) { + const QString &commentText = m_engine->code().mid(it->offset, it->length); + if (commentText.contains("fall through"_L1) + || commentText.contains("fall-through"_L1) + || commentText.contains("fallthrough"_L1)) { + return; + } + } + + m_logger->log("Unterminated non-empty case block"_L1, qmlUnterminatedCase, errorLoc); +} + +bool LinterVisitor::visit(QQmlJS::AST::CaseBlock *block) +{ + QQmlJSImportVisitor::visit(block); + + std::vector<std::pair<SourceLocation, StatementList *>> clauses; + for (CaseClauses *it = block->clauses; it; it = it->next) + clauses.push_back({ it->clause->caseToken, it->clause->statements }); + if (block->defaultClause) + clauses.push_back({ block->defaultClause->defaultToken, block->defaultClause->statements }); + for (CaseClauses *it = block->moreClauses; it; it = it->next) + clauses.push_back({ it->clause->caseToken, it->clause->statements }); + + // check all but the last clause for fallthrough + for (size_t i = 0; i < clauses.size() - 1; ++i) { + const SourceLocation nextToken = clauses[i + 1].first; + checkCaseFallthrough(clauses[i].second, clauses[i].first, nextToken); + } + return true; +} + } // namespace QQmlJS QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljslintervisitor_p.h b/src/qmlcompiler/qqmljslintervisitor_p.h index c485428af4..48e9372887 100644 --- a/src/qmlcompiler/qqmljslintervisitor_p.h +++ b/src/qmlcompiler/qqmljslintervisitor_p.h @@ -16,6 +16,8 @@ #include <private/qqmljsimportvisitor_p.h> +#include <private/qqmljsengine_p.h> + QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -29,7 +31,9 @@ namespace QQmlJS { class LinterVisitor final : public QQmlJSImportVisitor { public: - using QQmlJSImportVisitor::QQmlJSImportVisitor; + LinterVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger, + const QString &implicitImportDirectory, + const QStringList &qmldirFiles = QStringList(), QQmlJS::Engine *engine = nullptr); protected: using QQmlJSImportVisitor::endVisit; @@ -46,6 +50,7 @@ protected: bool visit(QQmlJS::AST::BinaryExpression *) override; bool visit(QQmlJS::AST::UiImport *import) override; bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override; + bool visit(QQmlJS::AST::CaseBlock *) override; private: struct SeenImport @@ -75,11 +80,15 @@ private: return qHashMulti(seed, i.filename, i.uri, i.version, i.id); } }; + QQmlJS::Engine *m_engine = nullptr; QSet<SeenImport> m_seenImports; std::vector<QQmlJS::AST::Node *> m_ancestryIncludingCurrentNode; void handleDuplicateEnums(QQmlJS::AST::UiEnumMemberList *members, QStringView key, const QQmlJS::SourceLocation &location); + void warnCaseNoFlowControl(QQmlJS::SourceLocation caseToken) const; + void checkCaseFallthrough(QQmlJS::AST::StatementList *statements, SourceLocation errorLoc, + SourceLocation nextLoc); }; } // namespace QQmlJS diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp index 77d9bfc1e9..c01ae212f0 100644 --- a/src/qmlcompiler/qqmljslogger.cpp +++ b/src/qmlcompiler/qqmljslogger.cpp @@ -88,6 +88,9 @@ using namespace Qt::StringLiterals; "Warn about non-list properties", QtWarningMsg, false, false) \ X(qmlNonRootEnums, "non-root-enum", "NonRootEnum", \ "Warn about enums defined outside the root component", QtWarningMsg, false, false) \ + X(qmlUnterminatedCase, "unterminated-case", "UnterminatedCase", "Warn about non-empty case " \ + "blocks that are not terminated by control flow or by a fallthrough comment", \ + QtWarningMsg, false, false) \ X(qmlPlugin, "plugin", "LintPluginWarnings", "Warn if a qmllint plugin finds an issue", \ QtWarningMsg, true, false) \ X(qmlPrefixedImportType, "prefixed-import-type", "PrefixedImportType", \ @@ -139,7 +142,6 @@ using namespace Qt::StringLiterals; "positives when checking for unqualified access", \ QtWarningMsg, false, false) - #define X(category, name, setting, description, level, ignored, isDefault) \ const QQmlSA::LoggerWarningId category{ name }; QMLLINT_DEFAULT_CATEGORIES diff --git a/src/qmlcompiler/qqmljsloggingutils.h b/src/qmlcompiler/qqmljsloggingutils.h index fe3a76c742..f87f76d414 100644 --- a/src/qmlcompiler/qqmljsloggingutils.h +++ b/src/qmlcompiler/qqmljsloggingutils.h @@ -88,6 +88,7 @@ extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnqualified; extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnreachableCode; extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnresolvedAlias; extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnresolvedType; +extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnterminatedCase; extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUnusedImports; extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlUseProperFunction; extern const Q_QMLCOMPILER_EXPORT QQmlSA::LoggerWarningId qmlVarUsedBeforeDeclaration; |
