QmlJs: search for version in parent modules.

Modules used to be searched by name with optionally an attached
version.  Now, if a module with version is not found, the version is
attached to the parent module.

Task-number: QTCREATORBUG-16145
Change-Id: Ie0f30d4df64d13b3ec4c5ee38e9ad9215ae56420
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@theqtcompany.com>
This commit is contained in:
Marco Benelli
2016-04-29 16:25:17 +02:00
committed by Marco Benelli
parent b92ff237a9
commit 1556a707c3
3 changed files with 70 additions and 32 deletions

View File

@@ -377,38 +377,8 @@ Import LinkPrivate::importNonFile(Document::Ptr doc, const ImportInfo &importInf
const QString packageName = importInfo.name();
const ComponentVersion version = importInfo.version();
bool importFound = false;
const QString &packagePath = importInfo.path();
// check the filesystem with full version
foreach (const QString &importPath, importPaths) {
QString libraryPath = QString::fromLatin1("%1/%2.%3").arg(importPath, packagePath, version.toString());
if (importLibrary(doc, libraryPath, &import, importPath)) {
importFound = true;
break;
}
}
if (!importFound) {
// check the filesystem with major version
foreach (const QString &importPath, importPaths) {
QString libraryPath = QString::fromLatin1("%1/%2.%3").arg(importPath, packagePath,
QString::number(version.majorVersion()));
if (importLibrary(doc, libraryPath, &import, importPath)) {
importFound = true;
break;
}
}
}
if (!importFound) {
// check the filesystem with no version
foreach (const QString &importPath, importPaths) {
QString libraryPath = QString::fromLatin1("%1/%2").arg(importPath, packagePath);
if (importLibrary(doc, libraryPath, &import, importPath)) {
importFound = true;
break;
}
}
}
QString libraryPath = modulePath(packageName, version.toString(), importPaths);
bool importFound = importLibrary(doc, libraryPath, &import);
// if there are cpp-based types for this package, use them too
if (valueOwner->cppQmlTypes().hasModule(packageName)) {

View File

@@ -28,6 +28,8 @@
#include "parser/qmljsast_p.h"
#include <QColor>
#include <QDir>
#include <QRegularExpression>
using namespace QmlJS;
using namespace QmlJS::AST;
@@ -195,6 +197,67 @@ DiagnosticMessage QmlJS::errorMessage(const AST::SourceLocation &loc, const QStr
return DiagnosticMessage(Severity::Error, loc, message);
}
/*!
* \brief Permissive validation of a string representing a module version.
* \param version
* \return True if \p version is a valid version format (<digit(s)>.<digit(s)>) or if it is empty.
* False otherwise.
*/
bool QmlJS::maybeModuleVersion(const QString &version) {
QRegularExpression re(QLatin1String("^\\d+\\.\\d+$"));
return version.isEmpty() || re.match(version).hasMatch();
}
/*!
* \brief Get the path of a module
* \param name
* \param version
* \param importPaths
*
* Given the qualified \p name and \p version of a module, look for a valid path in \p importPaths.
* Most specific version are searched first, the version is searched also in parent modules.
* For example, given the \p name QtQml.Models and \p version 2.0, the following directories are
* searched in every element of \p importPath:
*
* - QtQml/Models.2.0
* - QtQml.2.0/Models
* - QtQml/Models.2
* - QtQml.2/Models
* - QtQml/Models
*
* \return The module paths if found, an empty string otherwise
* \see qmlimportscanner in qtdeclarative/tools
*/
QString QmlJS::modulePath(const QString &name, const QString &version,
const QStringList &importPaths)
{
Q_ASSERT(maybeModuleVersion(version));
const QStringList parts = name.split(QLatin1Char('.'), QString::SkipEmptyParts);
auto mkpath = [] (const QStringList &xs) -> QString { return xs.join(QLatin1Char('/')); };
QString candidate;
for (QString ver = version; ; ver.remove(QRegularExpression(QLatin1String("\\.?\\d+$")))) {
for (const QString &path: importPaths) {
if (ver.isEmpty()) {
candidate = QDir::cleanPath(QString::fromLatin1("%1/%2").arg(path, mkpath(parts)));
return QDir(candidate).exists() ? candidate : QString();
} else {
for (int i = parts.count() - 1; i >= 0; --i) {
candidate = QDir::cleanPath(
QString::fromLatin1("%1/%2.%3/%4").arg(path,
mkpath(parts.mid(0, i + 1)),
ver,
mkpath(parts.mid(i + 1))));
if (QDir(candidate).exists())
return candidate;
}
}
}
}
}
bool QmlJS::isValidBuiltinPropertyType(const QString &name)
{
return sharedData()->validBuiltinPropertyNames.contains(name);

View File

@@ -54,6 +54,11 @@ QMLJS_EXPORT bool isValidBuiltinPropertyType(const QString &name);
QMLJS_EXPORT DiagnosticMessage errorMessage(const AST::SourceLocation &loc,
const QString &message);
QMLJS_EXPORT bool maybeModuleVersion(const QString &version);
QMLJS_EXPORT QString modulePath(const QString &moduleImportName, const QString &version,
const QStringList &importPaths);
template <class T>
AST::SourceLocation locationFromRange(const T *node)
{