qmllint: Make "Did you mean" look in extension types as well
Makes sure we also look for suggestions in extension types. Adds two new methods to QQmlJSScope called properties() and methods() to easily get all of them across base types and extensions. Change-Id: I5874c0221bac6d6e317b79146227bf749100f05b Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
parent
28708434b1
commit
01cde42d5d
|
@ -148,6 +148,29 @@ bool QQmlJSScope::hasMethod(const QString &name) const
|
|||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns all methods visible from this scope including those of
|
||||
base types and extensions.
|
||||
|
||||
\note Methods that get shadowed are not included and only the
|
||||
version visible from this scope is contained. Additionally method
|
||||
overrides are not included either, only the first visible version
|
||||
of any method is included.
|
||||
*/
|
||||
QHash<QString, QQmlJSMetaMethod> QQmlJSScope::methods() const
|
||||
{
|
||||
QHash<QString, QQmlJSMetaMethod> results;
|
||||
searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
|
||||
for (auto it = scope->m_methods.constBegin(); it != scope->m_methods.constEnd(); it++) {
|
||||
if (!results.contains(it.key()))
|
||||
results.insert(it.key(), it.value());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const
|
||||
{
|
||||
QList<QQmlJSMetaMethod> results;
|
||||
|
@ -516,6 +539,27 @@ QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
|
|||
return prop;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns all properties visible from this scope including those of
|
||||
base types and extensions.
|
||||
|
||||
\note Properties that get shadowed are not included and only the
|
||||
version visible from this scope is contained.
|
||||
*/
|
||||
QHash<QString, QQmlJSMetaProperty> QQmlJSScope::properties() const
|
||||
{
|
||||
QHash<QString, QQmlJSMetaProperty> results;
|
||||
searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
|
||||
for (auto it = scope->m_properties.constBegin(); it != scope->m_properties.constEnd();
|
||||
it++) {
|
||||
if (!results.contains(it.key()))
|
||||
results.insert(it.key(), it.value());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
QQmlJSScope::ConstPtr QQmlJSScope::ownerOfProperty(const QQmlJSScope::ConstPtr &self,
|
||||
const QString &name)
|
||||
{
|
||||
|
|
|
@ -209,6 +209,7 @@ public:
|
|||
bool hasOwnMethod(const QString &name) const { return m_methods.contains(name); }
|
||||
|
||||
bool hasMethod(const QString &name) const;
|
||||
QHash<QString, QQmlJSMetaMethod> methods() const;
|
||||
QList<QQmlJSMetaMethod> methods(const QString &name) const;
|
||||
QList<QQmlJSMetaMethod> methods(const QString &name, QQmlJSMetaMethod::Type type) const;
|
||||
|
||||
|
@ -271,6 +272,7 @@ public:
|
|||
|
||||
bool hasProperty(const QString &name) const;
|
||||
QQmlJSMetaProperty property(const QString &name) const;
|
||||
QHash<QString, QQmlJSMetaProperty> properties() const;
|
||||
|
||||
void setPropertyLocallyRequired(const QString &name, bool isRequired);
|
||||
bool isPropertyRequired(const QString &name) const;
|
||||
|
|
|
@ -377,15 +377,13 @@ void QQmlJSTypePropagator::handleUnqualifiedAccess(const QString &name, bool isM
|
|||
}
|
||||
|
||||
if (!suggestion.has_value()) {
|
||||
for (QQmlJSScope::ConstPtr baseScope = m_function->qmlScope; !baseScope.isNull();
|
||||
baseScope = baseScope->baseType()) {
|
||||
if (auto didYouMean = QQmlJSUtils::didYouMean(
|
||||
name, baseScope->ownProperties().keys() + baseScope->ownMethods().keys(),
|
||||
location);
|
||||
didYouMean.has_value()) {
|
||||
suggestion = didYouMean;
|
||||
break;
|
||||
}
|
||||
if (auto didYouMean =
|
||||
QQmlJSUtils::didYouMean(name,
|
||||
m_function->qmlScope->properties().keys()
|
||||
+ m_function->qmlScope->methods().keys(),
|
||||
location);
|
||||
didYouMean.has_value()) {
|
||||
suggestion = didYouMean;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,15 +749,10 @@ void QQmlJSTypePropagator::propagatePropertyLookup(const QString &propertyName)
|
|||
|
||||
std::optional<FixSuggestion> fixSuggestion;
|
||||
|
||||
for (QQmlJSScope::ConstPtr baseScope = baseType; !baseScope.isNull();
|
||||
baseScope = baseScope->baseType()) {
|
||||
if (auto suggestion =
|
||||
QQmlJSUtils::didYouMean(propertyName, baseScope->ownProperties().keys(),
|
||||
getCurrentSourceLocation());
|
||||
suggestion.has_value()) {
|
||||
fixSuggestion = suggestion;
|
||||
break;
|
||||
}
|
||||
if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->properties().keys(),
|
||||
getCurrentSourceLocation());
|
||||
suggestion.has_value()) {
|
||||
fixSuggestion = suggestion;
|
||||
}
|
||||
|
||||
if (!fixSuggestion.has_value()
|
||||
|
@ -971,14 +964,12 @@ void QQmlJSTypePropagator::generate_CallProperty(int nameIndex, int base, int ar
|
|||
|
||||
std::optional<FixSuggestion> fixSuggestion;
|
||||
|
||||
for (QQmlJSScope::ConstPtr baseScope = m_typeResolver->containedType(callBase);
|
||||
!baseScope.isNull(); baseScope = baseScope->baseType()) {
|
||||
if (auto suggestion = QQmlJSUtils::didYouMean(
|
||||
propertyName, baseScope->ownMethods().keys(), getCurrentSourceLocation());
|
||||
suggestion.has_value()) {
|
||||
fixSuggestion = suggestion;
|
||||
break;
|
||||
}
|
||||
const auto baseType = m_typeResolver->containedType(callBase);
|
||||
|
||||
if (auto suggestion = QQmlJSUtils::didYouMean(propertyName, baseType->methods().keys(),
|
||||
getCurrentSourceLocation());
|
||||
suggestion.has_value()) {
|
||||
fixSuggestion = suggestion;
|
||||
}
|
||||
|
||||
m_logger->log(u"Property \"%1\" not found on type \"%2\""_qs.arg(
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import QtQml
|
||||
|
||||
QtObject {
|
||||
property string property_not_shadowed
|
||||
function method_not_shadowed(foo) {}
|
||||
|
||||
property string property_shadowed
|
||||
function method_shadowed(foo) {}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import QtQml
|
||||
|
||||
Shadowed {
|
||||
property int property_shadowed
|
||||
function method_shadowed() {}
|
||||
}
|
|
@ -77,18 +77,13 @@ class tst_qqmljsscope : public QQmlDataTest
|
|||
if (!error.message.isEmpty())
|
||||
return QQmlJSScope::ConstPtr();
|
||||
|
||||
const QStringList importPaths = {
|
||||
QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
|
||||
dataDirectory(),
|
||||
};
|
||||
|
||||
QQmlJSImporter importer { importPaths, /* resource file mapper */ nullptr };
|
||||
QQmlJSLogger logger;
|
||||
logger.setFileName(url);
|
||||
logger.setCode(sourceCode);
|
||||
logger.setSilent(true);
|
||||
QQmlJSImportVisitor visitor(&importer, &logger, dataDirectory());
|
||||
QQmlJSTypeResolver typeResolver { &importer };
|
||||
QQmlJSImportVisitor visitor(&m_importer, &logger, dataDirectory());
|
||||
QQmlJSTypeResolver typeResolver { &m_importer };
|
||||
typeResolver.init(&visitor, document.program);
|
||||
return visitor.result();
|
||||
}
|
||||
|
@ -99,9 +94,22 @@ private Q_SLOTS:
|
|||
void orderedBindings();
|
||||
void signalCreationDifferences();
|
||||
void allTypesAvailable();
|
||||
void shadowing();
|
||||
|
||||
public:
|
||||
tst_qqmljsscope() : QQmlDataTest(QT_QMLTEST_DATADIR) { }
|
||||
tst_qqmljsscope()
|
||||
: QQmlDataTest(QT_QMLTEST_DATADIR),
|
||||
m_importer(
|
||||
{
|
||||
QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
|
||||
dataDirectory(),
|
||||
},
|
||||
nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QQmlJSImporter m_importer;
|
||||
};
|
||||
|
||||
void tst_qqmljsscope::initTestCase()
|
||||
|
@ -178,5 +186,29 @@ void tst_qqmljsscope::allTypesAvailable()
|
|||
QCOMPARE(types[u"$internal$.QObject"_qs].scope, types[u"QtObject"_qs].scope);
|
||||
}
|
||||
|
||||
void tst_qqmljsscope::shadowing()
|
||||
{
|
||||
QQmlJSScope::ConstPtr root = run(u"shadowing.qml"_qs);
|
||||
QVERIFY(root);
|
||||
|
||||
QVERIFY(root->baseType());
|
||||
|
||||
// Check whether properties are properly shadowed
|
||||
const auto properties = root->properties();
|
||||
QVERIFY(properties.contains(u"property_not_shadowed"_qs));
|
||||
QVERIFY(properties.contains(u"property_shadowed"_qs));
|
||||
|
||||
QCOMPARE(properties[u"property_not_shadowed"_qs].typeName(), u"QString"_qs);
|
||||
QCOMPARE(properties[u"property_shadowed"_qs].typeName(), u"int"_qs);
|
||||
|
||||
// Check whether methods are properly shadowed
|
||||
const auto methods = root->methods();
|
||||
QCOMPARE(methods.count(u"method_not_shadowed"_qs), 1);
|
||||
QCOMPARE(methods.count(u"method_shadowed"_qs), 1);
|
||||
|
||||
QCOMPARE(methods[u"method_not_shadowed"_qs].parameterNames().size(), 1);
|
||||
QCOMPARE(methods[u"method_shadowed"_qs].parameterNames().size(), 0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qqmljsscope)
|
||||
#include "tst_qqmljsscope.moc"
|
||||
|
|
Loading…
Reference in New Issue