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{
251 cont = cont && self.dvItemField(visitor, Fields::plugins, [this, &self]() {
255 [cNames](const DomItem &list, const PathEls::PathComponent &p,
256 const QQmlDirParser::Plugin &plugin) {
258 }));
259 });
260 // add qmlfiles as map because this way they are presented the same way as
261 // the qmlfiles in a directory
262 cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() {
264 return self.subMapItem(Map(
266 [typeFileMap](const DomItem &map, const QString &typeV) {
268 if (path.isEmpty())
269 return DomItem();
270 else
271 return map.subReferencesItem(
272 PathEls::Key(typeV),
274 },
275 [typeFileMap](const DomItem &) {
277 },
278 QStringLiteral(u"QList<Reference>")));
279 });
281 return cont;
282}
283
285{
286 // add qmlfiles as map because this way they are presented the same way as
287 // the qmlfiles in a directory which gives them as fileName->list of references to files
288 // this is done only to ensure that they are loaded as dependencies
290 for (const auto &e : m_exports)
292 e.typePath[2].headName());
293 return res;
294}
295
328
330{
331 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("JsFile"),
332 NewErrorGroup("Parsing") } };
333 return res;
334}
335
346
347void JsFile::writeOut(const DomItem &self, OutWriter &ow) const
348{
350 ow.ensureNewline(2);
354 }
355}
356
364
373
375{
376 Q_UNUSED(self);
377 lw.write(u".pragma").ensureSpace().write(u"library").ensureNewline();
378}
379
380void JsFile::LegacyImport::writeOut(const DomItem &self, OutWriter &lw) const
381{
382 const auto fLoc = FileLocations::treeOf(self);
383 // either filename or module uri must be present
385
386 lw.write(u".import").ensureSpace();
387 if (!uri.isEmpty()) {
389 if (!version.isEmpty()) {
391 }
392 } else {
393 lw.write(u"\"").write(fileName).write(u"\"").ensureSpace();
394 }
396
398}
399
400/*!
401 * \internal JsFile::writeOutDirectives
402 * \brief Performs writeOut of the .js Directives (.import, .pragma)
403 *
404 * Watch out!
405 * Currently directives in .js files do not have representative AST::Node-s (see QTBUG-119770),
406 * which makes it hard to preserve attached comments during the WriteOut process,
407 * because currently they are being attached to the first AST::Node.
408 * In case when the first AST::Node is absent, they are not collected, hence lost.
409 */
410void JsFile::writeOutDirectives(const DomItem &self, OutWriter &ow) const
411{
414 }
415 for (const auto &import : m_imports) {
417 }
418}
419
421{
422 auto res = std::make_shared<QmlFile>(*this);
423 return res;
424}
425
426/*!
427 \class QmlFile
428
429 A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,
430 will be lazily constructed. That means that its member m_lazyMembers is uninitialized, and will
431 only be populated when it is accessed (through a getter, a setter or the DomItem interface).
432
433 The reason for the laziness is that the qqmljsscopes are created lazily and at the same time as
434 the Dom QmlFile representations. So instead of eagerly generating all qqmljsscopes when
435 constructing the Dom, the QmlFile itself becomes lazy and will only be populated on demand at
436 the same time as the corresponding qqmljsscopes.
437
438 The QDeferredFactory<QQmlJSScope> will, when the qqmljsscope is populated, take care of
439 populating all fields of the QmlFile.
440 Therefore, population of the QmlFile is done by populating the qqmljsscope.
441
442*/
443
466
468{
469 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("QmlFile"),
470 NewErrorGroup("Parsing") } };
471 return res;
472}
473
488
496
501
503{
505 for (const DomItem &p : self.field(Fields::pragmas).values()) {
506 p.writeOut(ow);
507 }
509
512 [](const DomItem& item1, const DomItem& item2) {
513 const auto uri1 = item1[Fields::uri].toString();
514 const auto uri2 = item2[Fields::uri].toString();
515 return uri1 < uri2;
516 });
517 }
518
519 for (const DomItem& i : imports) {
520 i.writeOut(ow);
521 }
522 ow.ensureNewline(2);
525}
526
533
539
541{
542 auto it = m_uris.begin();
543 auto end = m_uris.end();
546 while (it != end) {
547 QString uri = it.key();
548 for (int majorV : it.value()) {
552 }
553 ++it;
554 }
555 }
556}
557
559{
563 cont = cont && self.dvItemField(visitor, Fields::uris, [this, &self]() {
564 return self.subMapItem(Map::fromMapRef<QSet<int>>(
566 [](const DomItem &map, const PathEls::PathComponent &p, const QSet<int> &el) {
567 QList<int> l(el.cbegin(), el.cend());
568 std::sort(l.begin(), l.end());
569 return map.subListItem(
571 [](const DomItem &list, const PathEls::PathComponent &p,
572 int el) { return list.subDataItem(p, el); }));
573 }));
574 });
576 return cont;
577}
578
589
591{
594 cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() -> DomItem {
596 return self.subMapItem(Map(
598 [this, baseDir](const DomItem &map, const QString &key) -> DomItem {
599 QList<Path> res;
600 auto it = m_qmlFiles.find(key);
601 while (it != m_qmlFiles.end() && it.key() == key) {
604 ++it;
605 }
607 },
608 [this](const DomItem &) {
609 auto keys = m_qmlFiles.keys();
610 return QSet<QString>(keys.begin(), keys.end());
611 },
612 u"List<Reference>"_s));
613 });
614 return cont;
615}
616
618{
621 uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation|ui\.qml))")
622 };
624 if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
625 m_qmlFiles.insert(m.captured(u"compName"), relativePath);
626 Export e;
630 e.typeName = m.captured(u"compName");
632 e.uri = QLatin1String("file://") + canonicalFilePath();
634 return true;
635 }
636 return false;
637}
638
639} // end namespace Dom
640} // end namespace QQmlJS
641
642QT_END_NAMESPACE
QCborValue pluginData(const QQmlDirParser::Plugin &pl, const QStringList &cNames)
#define NewErrorGroup(name)