aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmllint
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmllint')
-rw-r--r--tools/qmllint/fakemetaobject.cpp14
-rw-r--r--tools/qmllint/fakemetaobject.h3
-rw-r--r--tools/qmllint/findunqualified.cpp205
-rw-r--r--tools/qmllint/findunqualified.h7
-rw-r--r--tools/qmllint/main.cpp9
-rw-r--r--tools/qmllint/qcoloroutput.cpp11
-rw-r--r--tools/qmllint/qcoloroutput_p.h2
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.cpp67
-rw-r--r--tools/qmllint/scopetree.cpp44
-rw-r--r--tools/qmllint/scopetree.h5
10 files changed, 238 insertions, 129 deletions
diff --git a/tools/qmllint/fakemetaobject.cpp b/tools/qmllint/fakemetaobject.cpp
index 514bb2fe42..8319ae6713 100644
--- a/tools/qmllint/fakemetaobject.cpp
+++ b/tools/qmllint/fakemetaobject.cpp
@@ -46,8 +46,8 @@ QString FakeMetaEnum::name() const
void FakeMetaEnum::setName(const QString &name)
{ m_name = name; }
-void FakeMetaEnum::addKey(const QString &key, int value)
-{ m_keys.append(key); m_values.append(value); }
+void FakeMetaEnum::addKey(const QString &key)
+{ m_keys.append(key); }
QString FakeMetaEnum::key(int index) const
{ return m_keys.at(index); }
@@ -73,10 +73,6 @@ void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
}
- len = m_values.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- foreach (int value, m_values)
- hash.addData(reinterpret_cast<const char *>(&value), sizeof(value));
}
QString FakeMetaEnum::describe(int baseIndent) const
@@ -84,16 +80,14 @@ QString FakeMetaEnum::describe(int baseIndent) const
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Enum ");
res += name();
- res += QLatin1String(":{");
+ res += QLatin1String(": [");
for (int i = 0; i < keyCount(); ++i) {
res += newLine;
res += QLatin1String(" ");
res += key(i);
- res += QLatin1String(": ");
- res += QString::number(m_values.value(i, -1));
}
res += newLine;
- res += QLatin1Char('}');
+ res += QLatin1Char(']');
return res;
}
diff --git a/tools/qmllint/fakemetaobject.h b/tools/qmllint/fakemetaobject.h
index 4e0ea1f8b3..ae76596343 100644
--- a/tools/qmllint/fakemetaobject.h
+++ b/tools/qmllint/fakemetaobject.h
@@ -46,7 +46,6 @@ namespace LanguageUtils {
class FakeMetaEnum {
QString m_name;
QStringList m_keys;
- QList<int> m_values;
public:
FakeMetaEnum();
@@ -57,7 +56,7 @@ public:
QString name() const;
void setName(const QString &name);
- void addKey(const QString &key, int value);
+ void addKey(const QString &key);
QString key(int index) const;
int keyCount() const;
QStringList keys() const;
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp
index 27939608d7..ee2a0c38c1 100644
--- a/tools/qmllint/findunqualified.cpp
+++ b/tools/qmllint/findunqualified.cpp
@@ -39,10 +39,18 @@
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qv4codegen_p.h>
+#include <private/qqmldirparser_p.h>
-QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc);
+static QQmlDirParser createQmldirParserForFile(const QString &filename)
+{
+ QFile f(filename);
+ f.open(QFile::ReadOnly);
+ QQmlDirParser parser;
+ parser.parse(f.readAll());
+ return parser;
+}
-static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename)
+static QQmlJS::TypeDescriptionReader createQmltypesReaderForFile(QString const &filename)
{
QFile f(filename);
f.open(QFile::ReadOnly);
@@ -60,13 +68,12 @@ void FindUnqualifiedIDVisitor::leaveEnvironment()
m_currentScope = m_currentScope->parentScope();
}
-enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned };
+enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned, BasePath };
-QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin)
+QStringList completeImportPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin)
{
static const QLatin1Char Slash('/');
static const QLatin1Char Backslash('\\');
- static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes");
const QVector<QStringRef> parts = uri.splitRef(QLatin1Char('.'), QString::SkipEmptyParts);
@@ -96,7 +103,7 @@ QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePat
return str;
};
- for (int version = FullyVersioned; version <= Unversioned; ++version) {
+ for (int version = FullyVersioned; version <= BasePath; ++version) {
const QString ver = versionString(vmaj, vmin, static_cast<ImportVersion>(version));
for (const QString &path : basePaths) {
@@ -104,20 +111,23 @@ QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePat
if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
dir += Slash;
- // append to the end
- qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver + SlashPluginsDotQmltypes;
+ if (version == BasePath) {
+ qmlDirPathsPaths += dir;
+ } else {
+ // append to the end
+ qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver;
+ }
- if (version != Unversioned) {
+ if (version < Unversioned) {
// insert in the middle
for (int index = parts.count() - 2; index >= 0; --index) {
qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash)
+ ver + Slash
- + joinStringRefs(parts.mid(index + 1), Slash) + SlashPluginsDotQmltypes;
+ + joinStringRefs(parts.mid(index + 1), Slash);
}
}
}
}
-
return qmlDirPathsPaths;
}
@@ -130,19 +140,50 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo
m_alreadySeenImports.insert(importId);
}
id = id.replace(QLatin1String("/"), QLatin1String("."));
- auto qmltypesPaths = completeQmltypesPaths(id, m_qmltypeDirs, major, minor);
+ auto qmltypesPaths = completeImportPaths(id, m_qmltypeDirs, major, minor);
QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects;
QList<QQmlJS::ModuleApiInfo> moduleApis;
QStringList dependencies;
+ static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes");
+ static const QLatin1String SlashQmldir("/qmldir");
for (auto const &qmltypesPath : qmltypesPaths) {
- if (QFile::exists(qmltypesPath)) {
- auto reader = createReaderForFile(qmltypesPath);
- auto succ = reader(&objects, &moduleApis, &dependencies);
- if (!succ) {
- qDebug() << reader.errorMessage();
+ if (QFile::exists(qmltypesPath + SlashQmldir)) {
+ auto reader = createQmldirParserForFile(qmltypesPath + SlashQmldir);
+ const auto imports = reader.imports();
+ for (const QString &import : imports)
+ importHelper(import, prefix, major, minor);
+
+ QHash<QString, LanguageUtils::FakeMetaObject *> qmlComponents;
+ const auto components = reader.components();
+ for (auto it = components.begin(), end = components.end(); it != end; ++it) {
+ const QString filePath = qmltypesPath + QLatin1Char('/') + it->fileName;
+ if (!QFile::exists(filePath)) {
+ m_colorOut.write(QLatin1String("warning: "), Warning);
+ m_colorOut.write(it->fileName + QLatin1String(" is listed as component in ")
+ + qmltypesPath + SlashQmldir
+ + QLatin1String(" but does not exist.\n"));
+ continue;
+ }
+
+ auto mo = qmlComponents.find(it.key());
+ if (mo == qmlComponents.end())
+ mo = qmlComponents.insert(it.key(), localQmlFile2FakeMetaObject(filePath));
+
+ (*mo)->addExport(
+ it.key(), reader.typeNamespace(),
+ LanguageUtils::ComponentVersion(it->majorVersion, it->minorVersion));
}
- break;
+ for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it) {
+ objects.insert(it.key(),
+ QSharedPointer<const LanguageUtils::FakeMetaObject>(it.value()));
+ }
+ }
+ if (QFile::exists(qmltypesPath + SlashPluginsDotQmltypes)) {
+ auto reader = createQmltypesReaderForFile(qmltypesPath + SlashPluginsDotQmltypes);
+ auto succ = reader(&objects, &moduleApis, &dependencies);
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
}
}
for (auto const &dependency : qAsConst(dependencies)) {
@@ -170,7 +211,8 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
{
using namespace QQmlJS::AST;
auto fake = new LanguageUtils::FakeMetaObject;
- fake->setClassName(QFileInfo { filePath }.baseName());
+ QString baseName = QFileInfo { filePath }.baseName();
+ fake->setClassName(baseName.endsWith(".ui") ? baseName.chopped(3) : baseName);
QFile file(filePath);
if (!file.open(QFile::ReadOnly)) {
return fake;
@@ -273,6 +315,7 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
auto sourceElement = static_cast<UiSourceElement *>(initMembers->member);
if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
LanguageUtils::FakeMetaMethod method;
+ method.setMethodName(fexpr->name.toString());
method.setMethodType(LanguageUtils::FakeMetaMethod::Method);
FormalParameterList *parameters = fexpr->formals;
while (parameters) {
@@ -290,13 +333,17 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
} else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
// nothing to do
} else {
- qDebug() << "unsupportedd sourceElement at" << sourceElement->firstSourceLocation()
- << sourceElement->sourceElement->kind;
+ 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: {
- qDebug() << "unsupported element of kind" << initMembers->member->kind;
+ m_colorOut.writeUncolored("unsupported element of kind "
+ + QString::number(initMembers->member->kind));
}
}
initMembers = initMembers->next;
@@ -304,6 +351,22 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
return fake;
}
+void FindUnqualifiedIDVisitor::importDirectory(const QString &directory, const QString &prefix)
+{
+ QString dirname = directory;
+ QFileInfo info { dirname };
+ if (info.isRelative())
+ dirname = QDir(QFileInfo { m_filePath }.path()).filePath(dirname);
+
+ QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
+ while (it.hasNext()) {
+ LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next());
+ m_exportedName2MetaObject.insert(
+ prefix + fake->className(),
+ QSharedPointer<const LanguageUtils::FakeMetaObject>(fake));
+ }
+}
+
void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString name)
{
for (;;) {
@@ -348,11 +411,10 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter,
QDirIterator::Subdirectories };
while (it.hasNext()) {
- auto reader = createReaderForFile(it.next());
+ auto reader = createQmltypesReaderForFile(it.next());
auto succ = reader(&objects, &moduleApis, &dependencies);
- if (!succ) {
- qDebug() << reader.errorMessage();
- }
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
}
}
// add builtins
@@ -377,6 +439,8 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
meta->addProperty(LanguageUtils::FakeMetaProperty {"ignoreUnknownSignals", "bool", false, false, false, 0});
meta->addProperty(LanguageUtils::FakeMetaProperty {"target", "QObject", false, false, false, 0});
m_exportedName2MetaObject["Connections"] = LanguageUtils::FakeMetaObject::ConstPtr { meta };
+
+ importDirectory(".", QString());
return true;
}
@@ -476,6 +540,23 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::WithStatement *)
leaveEnvironment();
}
+static QString signalName(const QStringRef &handlerName)
+{
+ if (handlerName.startsWith("on") && handlerName.size() > 2) {
+ QString signal = handlerName.mid(2).toString();
+ for (int i = 0; i < signal.length(); ++i) {
+ QCharRef ch = signal[i];
+ if (ch.isLower())
+ return QString();
+ if (ch.isUpper()) {
+ ch = ch.toLower();
+ return signal;
+ }
+ }
+ }
+ return QString();
+}
+
bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
{
using namespace QQmlJS::AST;
@@ -489,8 +570,17 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
if (m_currentScope->isVisualRootScope()) {
m_rootId = identexp->name.toString();
}
- } else if (name.startsWith("on") && name.size() > 2 && name.at(2).isUpper()) {
- auto statement = uisb->statement;
+ } else {
+ const QString signal = signalName(name);
+ if (signal.isEmpty())
+ return true;
+
+ if (!m_currentScope->methods().contains(signal)) {
+ m_currentScope->addUnmatchedSignalHandler(name.toString(), uisb->firstSourceLocation());
+ return true;
+ }
+
+ const auto statement = uisb->statement;
if (statement->kind == Node::Kind::Kind_ExpressionStatement) {
if (static_cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) {
// functions are already handled
@@ -499,17 +589,14 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
return true;
}
}
- QString signal = name.mid(2).toString();
- signal[0] = signal[0].toLower();
- if (!m_currentScope->methods().contains(signal)) {
- qDebug() << "Info file does not contain signal" << signal;
- } else {
- auto method = m_currentScope->methods()[signal];
- for (auto const &param : method.parameterNames()) {
- auto firstSourceLocation = uisb->statement->firstSourceLocation();
- bool hasMultilineStatementBody = uisb->statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
- m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, hasMultilineStatementBody);
- }
+
+ auto method = m_currentScope->methods()[signal];
+ for (auto const &param : method.parameterNames()) {
+ const auto firstSourceLocation = statement->firstSourceLocation();
+ bool hasMultilineStatementBody
+ = statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
+ m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation,
+ hasMultilineStatementBody);
}
return true;
}
@@ -535,13 +622,15 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
}
FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs,
- const QString &code, const QString &fileName)
+ const QString &code, const QString &fileName,
+ bool silent)
: m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }),
m_currentScope(m_rootScope.get()),
m_qmltypeDirs(qmltypeDirs),
m_code(code),
m_rootId(QLatin1String("<id>")),
- m_filePath(fileName)
+ m_filePath(fileName),
+ m_colorOut(silent)
{
// setup color output
m_colorOut.insertColorMapping(Error, ColorOutput::RedForeground);
@@ -649,18 +738,9 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
prefix += import->importId + QLatin1Char('.');
}
auto dirname = import->fileName.toString();
- if (!dirname.isEmpty()) {
- QFileInfo info { dirname };
- if (info.isRelative()) {
- dirname = QDir(QFileInfo { m_filePath }.path()).filePath(dirname);
- }
- QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
- while (it.hasNext()) {
- LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next());
- m_exportedName2MetaObject.insert(
- fake->className(), QSharedPointer<const LanguageUtils::FakeMetaObject>(fake));
- }
- }
+ if (!dirname.isEmpty())
+ importDirectory(dirname, prefix);
+
QString path {};
if (!import->importId.isEmpty()) {
m_qmlid2meta.insert(import->importId.toString(), {}); // TODO: do not put imported ids into the same space as qml IDs
@@ -768,16 +848,19 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
return true;
}
-void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::PatternElement *element)
{
- leaveEnvironment();
+ if (element->isVariableDeclaration()) {
+ QQmlJS::AST::BoundNames names;
+ element->boundNames(&names);
+ for (const auto &name : names)
+ m_currentScope->insertJSIdentifier(name.id, element->scope);
+ }
+
+ return true;
}
-QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc)
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
{
- QDebugStateSaver saver(dbg);
- dbg.nospace() << loc.startLine;
- dbg.nospace() << ":";
- dbg.nospace() << loc.startColumn;
- return dbg.maybeSpace();
+ leaveEnvironment();
}
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
index 181f42f265..80413bd402 100644
--- a/tools/qmllint/findunqualified.h
+++ b/tools/qmllint/findunqualified.h
@@ -43,7 +43,8 @@ enum class ScopeType;
class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor {
public:
- explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, const QString& fileName);
+ explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code,
+ const QString& fileName, bool silent);
~FindUnqualifiedIDVisitor() override;
bool check();
@@ -70,7 +71,7 @@ private:
void importHelper(QString id, QString prefix, int major, int minor);
LanguageUtils::FakeMetaObject* localQmlFile2FakeMetaObject(QString filePath);
-
+ void importDirectory(const QString &directory, const QString &prefix);
void importExportedNames(QStringRef prefix, QString name);
void throwRecursionDepthError() override;
@@ -125,6 +126,8 @@ private:
// expression handling
bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
+
+ bool visit(QQmlJS::AST::PatternElement *) override;
};
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 235ec16c6e..56f72dd020 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -50,7 +50,8 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
- qWarning() << "Failed to open file" << filename << file.error();
+ if (!silent)
+ qWarning() << "Failed to open file" << filename << file.error();
return false;
}
@@ -76,7 +77,7 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
if (success && !isJavaScript && warnUnqualied) {
auto root = parser.rootNode();
- FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename};
+ FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename, silent };
root->accept(&v);
success = v.check();
}
@@ -122,9 +123,9 @@ int main(int argv, char *argc[])
// use host qml import path as a sane default if nothing else has been provided
QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption) ? parser.values(qmltypesDirsOption)
#ifndef QT_BOOTSTRAPPED
- : QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)};
+ : QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath), QLatin1String(".")};
#else
- : QStringList{};
+ : QStringList{QLatin1String(".")};
#endif
#else
bool silent = false;
diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp
index d2e723700a..eb4c721663 100644
--- a/tools/qmllint/qcoloroutput.cpp
+++ b/tools/qmllint/qcoloroutput.cpp
@@ -39,7 +39,7 @@
class ColorOutputPrivate
{
public:
- ColorOutputPrivate() : currentColorID(-1)
+ ColorOutputPrivate(bool silent) : currentColorID(-1), silent(silent)
{
/* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance
@@ -53,6 +53,7 @@ public:
ColorOutput::ColorMapping colorMapping;
int currentColorID;
bool coloringEnabled;
+ bool silent;
static const char *const foregrounds[];
static const char *const backgrounds[];
@@ -238,7 +239,7 @@ ColorOutput::ColorMapping ColorOutput::colorMapping() const
/*!
Constructs a ColorOutput instance, ready for use.
*/
-ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
+ColorOutput::ColorOutput(bool silent) : d(new ColorOutputPrivate(silent))
{
}
@@ -258,7 +259,8 @@ ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer ha
*/
void ColorOutput::write(const QString &message, int colorID)
{
- d->write(colorify(message, colorID));
+ if (!d->silent)
+ d->write(colorify(message, colorID));
}
/*!
@@ -269,7 +271,8 @@ void ColorOutput::write(const QString &message, int colorID)
*/
void ColorOutput::writeUncolored(const QString &message)
{
- d->write(message + QLatin1Char('\n'));
+ if (!d->silent)
+ d->write(message + QLatin1Char('\n'));
}
/*!
diff --git a/tools/qmllint/qcoloroutput_p.h b/tools/qmllint/qcoloroutput_p.h
index 710bf5db74..aefa765a87 100644
--- a/tools/qmllint/qcoloroutput_p.h
+++ b/tools/qmllint/qcoloroutput_p.h
@@ -89,7 +89,7 @@ public:
typedef QFlags<ColorCodeComponent> ColorCode;
typedef QHash<int, ColorCode> ColorMapping;
- ColorOutput();
+ ColorOutput(bool silent);
~ColorOutput();
void setColorMapping(const ColorMapping &cMapping);
diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/qmljstypedescriptionreader.cpp
index 542cdf99eb..b8aecdddb1 100644
--- a/tools/qmllint/qmljstypedescriptionreader.cpp
+++ b/tools/qmllint/qmljstypedescriptionreader.cpp
@@ -123,15 +123,13 @@ void TypeDescriptionReader::readDocument(UiProgram *ast)
return;
}
- ComponentVersion version;
- const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
- const int dotIdx = versionString.indexOf(QLatin1Char('.'));
- if (dotIdx != -1) {
- version = ComponentVersion(versionString.leftRef(dotIdx).toInt(),
- versionString.midRef(dotIdx + 1).toInt());
- }
- if (version.majorVersion() != 1) {
- addError(import->versionToken, tr("Major version different from 1 not supported."));
+ if (!import->version) {
+ addError(import->firstSourceLocation(), tr("Import statement without version."));
+ return;
+ }
+
+ if (import->version->majorVersion != 1) {
+ addError(import->version->firstSourceLocation(), tr("Major version different from 1 not supported."));
return;
}
@@ -666,39 +664,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt
return;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
- addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
+ addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
return;
}
- ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
- if (!objectLit) {
- addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
- return;
- }
-
- for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
- PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
- if (assignement) {
- StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
- NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
- UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
- if (minus)
- value = AST::cast<NumericLiteral *>(minus->expression);
- if (!propName || !value) {
- addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
- continue;
+ if (auto *objectLit = AST::cast<ObjectPattern *>(expStmt->expression)) {
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ if (PatternProperty *assignement = it->property) {
+ if (auto *name = AST::cast<StringLiteralPropertyName *>(assignement->name)) {
+ fme->addKey(name->id.toString());
+ continue;
+ }
}
-
- double v = value->value;
- if (minus)
- v = -v;
- fme->addKey(propName->id.toString(), v);
- continue;
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
+ }
+ } else if (auto *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression)) {
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ if (PatternElement *element = it->element) {
+ if (auto *name = AST::cast<StringLiteral *>(element->initializer)) {
+ fme->addKey(name->value.toString());
+ continue;
+ }
+ }
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
}
- PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
- if (getterSetter)
- addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
+ } else {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected either array or object literal as enum definition."));
}
}
diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp
index 2eff3fa319..1e873cca8f 100644
--- a/tools/qmllint/scopetree.cpp
+++ b/tools/qmllint/scopetree.cpp
@@ -81,6 +81,12 @@ void ScopeTree::insertPropertyIdentifier(QString id)
this->addMethod(method);
}
+void ScopeTree::addUnmatchedSignalHandler(const QString &handler,
+ const QQmlJS::AST::SourceLocation &location)
+{
+ m_unmatchedSignalHandlers.append(qMakePair(handler, location));
+}
+
bool ScopeTree::isIdInCurrentScope(const QString &id) const
{
return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
@@ -132,6 +138,15 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
workQueue.enqueue(this);
while (!workQueue.empty()) {
const ScopeTree* currentScope = workQueue.dequeue();
+ for (const auto &handler : currentScope->m_unmatchedSignalHandlers) {
+ colorOut.write("Warning: ", Warning);
+ colorOut.write(QString::fromLatin1(
+ "no matching signal found for handler \"%1\" at %2:%3\n")
+ .arg(handler.first).arg(handler.second.startLine)
+ .arg(handler.second.startColumn), Normal);
+ printContext(colorOut, code, handler.second);
+ }
+
for (auto idLocationPair : currentScope->m_accessedIdentifiers) {
if (qmlIDs.contains(idLocationPair.first))
continue;
@@ -141,13 +156,11 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
noUnqualifiedIdentifier = false;
colorOut.write("Warning: ", Warning);
auto location = idLocationPair.second;
- colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine, location.startColumn), Normal);
- IssueLocationWithContext issueLocationWithContext {code, location};
- colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
- colorOut.write(issueLocationWithContext.issueText.toString(), Error);
- colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal);
- int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t'));
- colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount) + QString("\t").repeated(tabCount) + QString("^").repeated(location.length) + QLatin1Char('\n'), Normal);
+ colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine,
+ location.startColumn), Normal);
+
+ printContext(colorOut, code, location);
+
// root(JS) --> program(qml) --> (first element)
if (root->m_childScopes[0]->m_childScopes[0]->m_currentScopeQMLIdentifiers.contains(idLocationPair.first)) {
ScopeTree *parentScope = currentScope->m_parentScope;
@@ -161,6 +174,7 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
colorOut.write("Note: ", Warning);
colorOut.write(("You first have to give the root element an id\n"));
}
+ IssueLocationWithContext issueLocationWithContext {code, location};
colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
colorOut.write(rootId + QLatin1Char('.'), Hint);
colorOut.write(issueLocationWithContext.issueText.toString(), Normal);
@@ -212,7 +226,7 @@ QMap<QString, LanguageUtils::FakeMetaMethod>const &ScopeTree::methods() const
bool ScopeTree::isIdInCurrentQMlScopes(QString id) const
{
auto qmlScope = getCurrentQMLScope();
- return qmlScope->m_currentScopeQMLIdentifiers.contains(id);
+ return qmlScope->m_currentScopeQMLIdentifiers.contains(id) || qmlScope->m_methods.contains(id);
}
bool ScopeTree::isIdInCurrentJSScopes(QString id) const
@@ -250,6 +264,20 @@ ScopeTree *ScopeTree::getCurrentQMLScope()
return qmlScope;
}
+void ScopeTree::printContext(ColorOutput &colorOut, const QString &code,
+ const QQmlJS::AST::SourceLocation &location) const
+{
+ IssueLocationWithContext issueLocationWithContext {code, location};
+ colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
+ colorOut.write(issueLocationWithContext.issueText.toString(), Error);
+ colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal);
+ int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t'));
+ colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount)
+ + QString("\t").repeated(tabCount)
+ + QString("^").repeated(location.length)
+ + QLatin1Char('\n'), Normal);
+}
+
ScopeType ScopeTree::scopeType() {return m_scopeType;}
void ScopeTree::addMethod(LanguageUtils::FakeMetaMethod method)
diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h
index 872a509123..52cdc45e96 100644
--- a/tools/qmllint/scopetree.h
+++ b/tools/qmllint/scopetree.h
@@ -73,6 +73,8 @@ public:
void insertQMLIdentifier(QString id);
void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody);
void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding
+ void addUnmatchedSignalHandler(const QString &handler,
+ const QQmlJS::AST::SourceLocation &location);
bool isIdInCurrentScope(QString const &id) const;
void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports);
@@ -96,11 +98,14 @@ private:
ScopeTree *m_parentScope;
QString m_name;
ScopeType m_scopeType;
+ QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_unmatchedSignalHandlers;
bool isIdInCurrentQMlScopes(QString id) const;
bool isIdInCurrentJSScopes(QString id) const;
bool isIdInjectedFromSignal(QString id) const;
const ScopeTree* getCurrentQMLScope() const;
ScopeTree* getCurrentQMLScope();
+ void printContext(ColorOutput& colorOut, const QString &code,
+ const QQmlJS::AST::SourceLocation &location) const;
};
#endif // SCOPETREE_H