Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomexternalitems.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmldomtop_p.h"
10
11#include <QtQml/private/qqmljslexer_p.h>
12#include <QtQml/private/qqmljsparser_p.h>
13#include <QtQml/private/qqmljsengine_p.h>
14#include <QtQml/private/qqmljsastvisitor_p.h>
15#include <QtQml/private/qqmljsast_p.h>
16#include <QtCore/QDir>
17#include <QtCore/QScopeGuard>
18#include <QtCore/QFileInfo>
19#include <QtCore/QRegularExpressionMatch>
20
21#include <algorithm>
22
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27namespace QQmlJS {
28namespace Dom {
29
38
43
48
50{
51 return m_path;
52}
53
55{
56 return m_path;
57}
58
60{
61 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Qmldir"),
62 NewErrorGroup("Parsing") } };
63 return res;
64}
65
79
80void QmldirFile::parse()
81{
82 if (canonicalFilePath().isEmpty()) {
83 addErrorLocal(myParsingErrors().error(tr("canonicalFilePath is empty")));
84 setIsValid(false);
85 } else {
88 }
89}
90
92{
94 if (m_uri.isValid())
99 bool ok;
101 if (ok && vNr > 0) // accept 0?
104 for (auto const &el : m_qmldir.components()) {
107 if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
108 // created where we expect it)
110 Export exp;
114 exp.version =
119 exp.uri = uri().toString();
121 if (exp.version.majorVersion > 0)
123 }
124 for (auto const &el : m_qmldir.scripts()) {
127 if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
128 // created where we expect it)
130 Export exp;
132 exp.isSingleton = true;
133 exp.isInternal = false;
134 exp.version =
138 exp.uri = uri().toString();
141 if (exp.version.majorVersion > 0)
143 }
144 for (QQmlDirParser::Import const &imp : m_qmldir.imports()) {
147 Version v;
148 if (isAutoImport)
150 else {
152 : int(Version::Latest)),
154 : int(Version::Latest)));
155 }
159 }
160 for (QQmlDirParser::Import const &imp : m_qmldir.dependencies()) {
163 qCDebug(QQmlJSDomImporting) << "QmldirFile::setFromQmlDir: ignoring initial version"
164 " 'auto' in depends command, using latest version"
165 " instead.";
166 }
167 Version v = Version(
170 : int(Version::Latest)));
172 }
173 bool hasInvalidTypeinfo = false;
174 for (auto const &el : m_qmldir.typeInfos()) {
175 QString elStr = el;
177 if (elPath.isRelative())
180 if (typeInfoPath.isEmpty()) {
181 hasInvalidTypeinfo = true;
183 }
185 }
187 // add all type info files in the directory...
188 for (QFileInfo const &entry :
194 }
195 }
196 bool hasErrors = false;
197 for (auto const &el : m_qmldir.errors(uri().toString())) {
200 hasErrors = true;
202 }
203 setIsValid(!hasErrors); // consider it valid also with errors?
205}
206
211
216
218{
219 // ModuleIndex keeps the various sources of types from a given module uri import
220 // this method ensures that all major versions that are contained in this qmldir
221 // file actually have a ModuleIndex. This is required so that when importing the
222 // latest version the correct "lastest major version" is found, for example for
223 // qml only modules (qmltypes files also register their versions)
226 for (int majorV : m_majorVersions) {
229 }
230 }
231}
232
233QCborValue pluginData(const QQmlDirParser::Plugin &pl, const QStringList &cNames)
234{
235 QCborArray names;
236 for (const QString &n : cNames)
237 names.append(n);
238 return QCborMap({ { QCborValue(QStringView(Fields::name)), pl.name },
239 { QStringView(Fields::path), pl.path },
240 { QStringView(Fields::classNames), names } });
241}
242
244{
252 cont = cont && visitor(PathEls::Field(Fields::plugins), [this, &self]() {
256 [cNames](const DomItem &list, const PathEls::PathComponent &p,
257 const QQmlDirParser::Plugin &plugin) {
259 }));
260 });
261 // add qmlfiles as map because this way they are presented the same way as
262 // the qmlfiles in a directory
263 cont = cont && visitor(PathEls::Field(Fields::qmlFiles), [this, &self]() {
265 return self.subMapItem(Map(
267 [typeFileMap](const DomItem &map, const QString &typeV) {
269 if (path.isEmpty())
270 return DomItem();
271 else
272 return map.subReferencesItem(
273 PathEls::Key(typeV),
275 },
276 [typeFileMap](const DomItem &) {
278 },
279 QStringLiteral(u"QList<Reference>")));
280 });
282 return cont;
283}
284
286{
287 // add qmlfiles as map because this way they are presented the same way as
288 // the qmlfiles in a directory which gives them as fileName->list of references to files
289 // this is done only to ensure that they are loaded as dependencies
291 for (const auto &e : m_exports)
293 e.typePath[2].headName());
294 return res;
295}
296
329
331{
332 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("JsFile"),
333 NewErrorGroup("Parsing") } };
334 return res;
335}
336
347
348void JsFile::writeOut(const DomItem &self, OutWriter &ow) const
349{
351 ow.ensureNewline(2);
355 }
356}
357
365
374
376{
377 Q_UNUSED(self);
378 lw.write(u".pragma").ensureSpace().write(u"library").ensureNewline();
379}
380
381void JsFile::LegacyImport::writeOut(const DomItem &self, OutWriter &lw) const
382{
383 const auto fLoc = FileLocations::treeOf(self);
384 // either filename or module uri must be present
386
387 lw.write(u".import").ensureSpace();
388 if (!uri.isEmpty()) {
390 if (!version.isEmpty()) {
392 }
393 } else {
394 lw.write(u"\"").write(fileName).write(u"\"").ensureSpace();
395 }
397
399}
400
401/*!
402 * \internal JsFile::writeOutDirectives
403 * \brief Performs writeOut of the .js Directives (.import, .pragma)
404 *
405 * Watch out!
406 * Currently directives in .js files do not have representative AST::Node-s (see QTBUG-119770),
407 * which makes it hard to preserve attached comments during the WriteOut process,
408 * because currently they are being attached to the first AST::Node.
409 * In case when the first AST::Node is absent, they are not collected, hence lost.
410 */
411void JsFile::writeOutDirectives(const DomItem &self, OutWriter &ow) const
412{
415 }
416 for (const auto &import : m_imports) {
418 }
419}
420
422{
423 auto res = std::make_shared<QmlFile>(*this);
424 return res;
425}
426
427/*!
428 \class QmlFile
429
430 A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,
431 will be lazily constructed. That means that its member m_lazyMembers is uninitialized, and will
432 only be populated when it is accessed (through a getter, a setter or the DomItem interface).
433
434 The reason for the laziness is that the qqmljsscopes are created lazily and at the same time as
435 the Dom QmlFile representations. So instead of eagerly generating all qqmljsscopes when
436 constructing the Dom, the QmlFile itself becomes lazy and will only be populated on demand at
437 the same time as the corresponding qqmljsscopes.
438
439 The QDeferredFactory<QQmlJSScope> will, when the qqmljsscope is populated, take care of
440 populating all fields of the QmlFile.
441 Therefore, population of the QmlFile is done by populating the qqmljsscope.
442
443*/
444
467
469{
470 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("QmlFile"),
471 NewErrorGroup("Parsing") } };
472 return res;
473}
474
489
497
502
504{
506 for (const DomItem &p : self.field(Fields::pragmas).values()) {
507 p.writeOut(ow);
508 }
510
513 [](const DomItem& item1, const DomItem& item2) {
514 const auto uri1 = item1[Fields::uri].toString();
515 const auto uri2 = item2[Fields::uri].toString();
516 return uri1 < uri2;
517 });
518 }
519
520 for (const DomItem& i : imports) {
521 i.writeOut(ow);
522 }
523 ow.ensureNewline(2);
526}
527
534
540
542{
543 auto it = m_uris.begin();
544 auto end = m_uris.end();
547 while (it != end) {
548 QString uri = it.key();
549 for (int majorV : it.value()) {
553 }
554 ++it;
555 }
556 }
557}
558
560{
564 cont = cont && visitor(PathEls::Field(Fields::uris), [this, &self]() {
565 return self.subMapItem(Map::fromMapRef<QSet<int>>(
567 [](const DomItem &map, const PathEls::PathComponent &p,
568 const QSet<int> &el) {
569 QList<int> l(el.cbegin(), el.cend());
570 std::sort(l.begin(), l.end());
571 return map.subListItem(List::fromQList<int>(
573 [](const DomItem &list, const PathEls::PathComponent &p,
574 int el) { return list.subDataItem(p, el); }));
575 }));
576 });
578 return cont;
579}
580
591
593{
596 cont = cont && visitor(PathEls::Field(Fields::qmlFiles), [this, &self]() -> DomItem {
598 return self.subMapItem(Map(
600 [this, baseDir](const DomItem &map, const QString &key) -> DomItem {
601 QList<Path> res;
602 auto it = m_qmlFiles.find(key);
603 while (it != m_qmlFiles.end() && it.key() == key) {
607 ++it;
608 }
610 },
611 [this](const DomItem &) {
612 auto keys = m_qmlFiles.keys();
613 return QSet<QString>(keys.begin(), keys.end());
614 },
615 u"List<Reference>"_s));
616 });
617 return cont;
618}
619
621{
624 uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation|ui\.qml))")
625 };
627 if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
628 m_qmlFiles.insert(m.captured(u"compName"), relativePath);
629 Export e;
633 e.typeName = m.captured(u"compName");
635 e.uri = QLatin1String("file://") + canonicalFilePath();
637 return true;
638 }
639 return false;
640}
641
642} // end namespace Dom
643} // end namespace QQmlJS
644
645QT_END_NAMESPACE
QCborValue pluginData(const QQmlDirParser::Plugin &pl, const QStringList &cNames)
#define NewErrorGroup(name)