Qt 6.x
The Qt SDK
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"
6#include "qqmldomcomments_p.h"
7#include "qqmldommock_p.h"
8#include "qqmldomelements_p.h"
9
10#include <QtQml/private/qqmljslexer_p.h>
11#include <QtQml/private/qqmljsparser_p.h>
12#include <QtQml/private/qqmljsengine_p.h>
13#include <QtQml/private/qqmljsastvisitor_p.h>
14#include <QtQml/private/qqmljsast_p.h>
15#include <QtCore/QDir>
16#include <QtCore/QScopeGuard>
17#include <QtCore/QFileInfo>
18#include <QtCore/QRegularExpression>
19#include <QtCore/QRegularExpressionMatch>
20
21#include <algorithm>
22
24
25using namespace Qt::StringLiterals;
26
27namespace QQmlJS {
28namespace Dom {
29
31 int derivedFrom, QString code)
32 : OwningItem(derivedFrom, lastDataUpdateAt),
33 m_canonicalFilePath(filePath),
34 m_code(code),
35 m_path(path)
36{}
37
39{
41}
42
44{
46}
47
49{
50 return m_path;
51}
52
54{
55 return m_path;
56}
57
59{
61 NewErrorGroup("Parsing") } };
62 return res;
63}
64
65std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(QString path, QString code)
66{
68
70 auto res = std::make_shared<QmldirFile>(canonicalFilePath, code, dataUpdate);
71
72 if (canonicalFilePath.isEmpty() && !path.isEmpty())
73 res->addErrorLocal(
74 myParsingErrors().error(tr("QmldirFile started from invalid path '%1'").arg(path)));
75 res->parse();
76 return res;
77}
78
79void QmldirFile::parse()
80{
81 if (canonicalFilePath().isEmpty()) {
82 addErrorLocal(myParsingErrors().error(tr("canonicalFilePath is empty")));
83 setIsValid(false);
84 } else {
85 m_qmldir.parse(m_code);
86 setFromQmldir();
87 }
88}
89
90void QmldirFile::setFromQmldir()
91{
92 m_uri = QmlUri::fromUriString(m_qmldir.typeNamespace());
93 if (m_uri.isValid())
95 Path exportsPath = Path::Field(Fields::exports);
96 QDir baseDir = QFileInfo(canonicalFilePath()).dir();
97 int majorVersion = Version::Undefined;
98 bool ok;
99 int vNr = QFileInfo(baseDir.dirName()).suffix().toInt(&ok);
100 if (ok && vNr > 0) // accept 0?
101 majorVersion = vNr;
102 Path exportSource = canonicalPath();
103 for (auto const &el : m_qmldir.components()) {
104 QString exportFilePath = baseDir.filePath(el.fileName);
105 QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath();
106 if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
107 // created where we expect it)
108 canonicalExportFilePath = exportFilePath;
109 Export exp;
110 exp.exportSourcePath = exportSource;
111 exp.isSingleton = el.singleton;
112 exp.isInternal = el.internal;
113 exp.version =
114 Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion),
115 el.version.hasMinorVersion() ? el.version.minorVersion() : 0);
116 exp.typeName = el.typeName;
117 exp.typePath = Paths::qmlFileObjectPath(canonicalExportFilePath);
118 exp.uri = uri().toString();
119 m_exports.insert(exp.typeName, exp);
120 if (exp.version.majorVersion > 0)
121 m_majorVersions.insert(exp.version.majorVersion);
122 }
123 for (auto const &el : m_qmldir.scripts()) {
124 QString exportFilePath = baseDir.filePath(el.fileName);
125 QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath();
126 if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be
127 // created where we expect it)
128 canonicalExportFilePath = exportFilePath;
129 Export exp;
130 exp.exportSourcePath = exportSource;
131 exp.isSingleton = true;
132 exp.isInternal = false;
133 exp.version =
134 Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion),
135 el.version.hasMinorVersion() ? el.version.minorVersion() : 0);
136 exp.typePath = Paths::jsFilePath(canonicalExportFilePath).field(Fields::rootComponent);
137 exp.uri = uri().toString();
138 exp.typeName = el.nameSpace;
139 m_exports.insert(exp.typeName, exp);
140 if (exp.version.majorVersion > 0)
141 m_majorVersions.insert(exp.version.majorVersion);
142 }
143 for (QQmlDirParser::Import const &imp : m_qmldir.imports()) {
144 QString uri = imp.module;
145 bool isAutoImport = imp.flags & QQmlDirParser::Import::Auto;
146 Version v;
147 if (isAutoImport)
148 v = Version(majorVersion, int(Version::Latest));
149 else {
150 v = Version((imp.version.hasMajorVersion() ? imp.version.majorVersion()
151 : int(Version::Latest)),
152 (imp.version.hasMinorVersion() ? imp.version.minorVersion()
153 : int(Version::Latest)));
154 }
155 m_imports.append(Import(QmlUri::fromUriString(uri), v));
156 m_autoExports.append(
157 ModuleAutoExport { Import(QmlUri::fromUriString(uri), v), isAutoImport });
158 }
159 for (QQmlDirParser::Import const &imp : m_qmldir.dependencies()) {
160 QString uri = imp.module;
161 if (imp.flags & QQmlDirParser::Import::Auto)
162 qWarning() << "qmldir contains dependency with auto keyword";
163 Version v = Version(
164 (imp.version.hasMajorVersion() ? imp.version.majorVersion() : int(Version::Latest)),
165 (imp.version.hasMinorVersion() ? imp.version.minorVersion()
166 : int(Version::Latest)));
167 m_imports.append(Import(QmlUri::fromUriString(uri), v));
168 }
169 bool hasInvalidTypeinfo = false;
170 for (auto const &el : m_qmldir.typeInfos()) {
171 QString elStr = el;
172 QFileInfo elPath(elStr);
173 if (elPath.isRelative())
174 elPath = QFileInfo(baseDir.filePath(elStr));
175 QString typeInfoPath = elPath.canonicalFilePath();
176 if (typeInfoPath.isEmpty()) {
177 hasInvalidTypeinfo = true;
178 typeInfoPath = elPath.absoluteFilePath();
179 }
180 m_qmltypesFilePaths.append(Paths::qmltypesFilePath(typeInfoPath));
181 }
182 if (m_qmltypesFilePaths.isEmpty() || hasInvalidTypeinfo) {
183 // add all type info files in the directory...
184 for (QFileInfo const &entry :
185 baseDir.entryInfoList(QStringList({ QLatin1String("*.qmltypes") }),
187 Path p = Paths::qmltypesFilePath(entry.canonicalFilePath());
188 if (!m_qmltypesFilePaths.contains(p))
189 m_qmltypesFilePaths.append(p);
190 }
191 }
192 bool hasErrors = false;
193 for (auto const &el : m_qmldir.errors(uri().toString())) {
195 addErrorLocal(msg);
196 if (msg.level == ErrorLevel::Error || msg.level == ErrorLevel::Fatal)
197 hasErrors = true;
198 }
199 setIsValid(!hasErrors); // consider it valid also with errors?
200 m_plugins = m_qmldir.plugins();
201}
202
204{
205 return m_autoExports;
206}
207
209{
210 m_autoExports = autoExport;
211}
212
214{
215 // ModuleIndex keeps the various sources of types from a given module uri import
216 // this method ensures that all major versions that are contained in this qmldir
217 // file actually have a ModuleIndex. This is required so that when importing the
218 // latest version the correct "lastest major version" is found, for example for
219 // qml only modules (qmltypes files also register their versions)
220 DomItem env = self.environment();
221 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
222 for (int majorV : m_majorVersions) {
223 auto mIndex = envPtr->moduleIndexWithUri(env, uri, majorV, EnvLookup::Normal,
225 }
226 }
227}
228
230{
232 for (QString n : cNames)
233 names.append(n);
234 return QCborMap({ { QCborValue(QStringView(Fields::name)), pl.name },
235 { QStringView(Fields::path), pl.path },
236 { QStringView(Fields::classNames), names } });
237}
238
240{
241 bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
242 cont = cont && self.dvValueField(visitor, Fields::uri, uri().toString());
243 cont = cont && self.dvValueField(visitor, Fields::designerSupported, designerSupported());
244 cont = cont && self.dvReferencesField(visitor, Fields::qmltypesFiles, m_qmltypesFilePaths);
245 cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
246 cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
247 cont = cont && self.dvItemField(visitor, Fields::plugins, [this, &self]() {
248 QStringList cNames = classNames();
249 return self.subListItem(List::fromQListRef<QQmlDirParser::Plugin>(
250 self.pathFromOwner().field(Fields::plugins), m_plugins,
251 [cNames](DomItem &list, const PathEls::PathComponent &p,
252 QQmlDirParser::Plugin &plugin) {
253 return list.subDataItem(p, pluginData(plugin, cNames));
254 }));
255 });
256 // add qmlfiles as map because this way they are presented the same way as
257 // the qmlfiles in a directory
258 cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() {
259 const QMap<QString, QString> typeFileMap = qmlFiles();
260 return self.subMapItem(Map(
261 self.pathFromOwner().field(Fields::qmlFiles),
262 [typeFileMap](DomItem &map, QString typeV) {
263 QString path = typeFileMap.value(typeV);
264 if (path.isEmpty())
265 return DomItem();
266 else
267 return map.subReferencesItem(
268 PathEls::Key(typeV),
269 QList<Path>({ Paths::qmlFileObjectPath(path) }));
270 },
271 [typeFileMap](DomItem &) {
272 return QSet<QString>(typeFileMap.keyBegin(), typeFileMap.keyEnd());
273 },
274 QStringLiteral(u"QList<Reference>")));
275 });
276 cont = cont && self.dvWrapField(visitor, Fields::autoExports, m_autoExports);
277 return cont;
278}
279
280QMap<QString, QString> QmldirFile::qmlFiles() const
281{
282 // add qmlfiles as map because this way they are presented the same way as
283 // the qmlfiles in a directory which gives them as fileName->list of references to files
284 // this is done only to ensure that they are loaded as dependencies
286 for (const auto &e : m_exports)
287 res.insert(e.typeName + QStringLiteral(u"-") + e.version.stringValue(),
288 e.typePath[2].headName());
289 return res;
290}
291
292std::shared_ptr<OwningItem> QmlFile::doCopy(DomItem &) const
293{
294 auto res = std::make_shared<QmlFile>(*this);
295 return res;
296}
297
298QmlFile::QmlFile(const QmlFile &o)
300 m_engine(o.m_engine),
301 m_ast(o.m_ast),
302 m_astComments(o.m_astComments),
303 m_comments(o.m_comments),
304 m_fileLocationsTree(o.m_fileLocationsTree),
305 m_components(o.m_components),
306 m_pragmas(o.m_pragmas),
307 m_imports(o.m_imports),
308 m_importScope(o.m_importScope)
309{
310 if (m_astComments)
311 m_astComments = std::make_shared<AstComments>(*m_astComments);
312}
313
314QmlFile::QmlFile(QString filePath, QString code, QDateTime lastDataUpdateAt, int derivedFrom)
315 : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(filePath), derivedFrom,
316 code),
317 m_engine(new QQmlJS::Engine),
318 m_astComments(new AstComments(m_engine)),
319 m_fileLocationsTree(FileLocations::createTree(canonicalPath()))
320{
321 QQmlJS::Lexer lexer(m_engine.get());
322 lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/true);
323 QQmlJS::Parser parser(m_engine.get());
324 m_isValid = parser.parse();
325 for (DiagnosticMessage msg : parser.diagnosticMessages())
327 m_ast = parser.ast();
328}
329
331{
332 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("QmlFile"),
333 NewErrorGroup("Parsing") } };
334 return res;
335}
336
338{
339 bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
340 cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
341 cont = cont && self.dvWrapField(visitor, Fields::pragmas, m_pragmas);
342 cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
343 cont = cont && self.dvWrapField(visitor, Fields::importScope, m_importScope);
344 cont = cont && self.dvWrapField(visitor, Fields::fileLocationsTree, m_fileLocationsTree);
345 cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
346 cont = cont && self.dvWrapField(visitor, Fields::astComments, m_astComments);
347 return cont;
348}
349
351{
352 if (name == Fields::components)
353 return self.wrapField(Fields::components, m_components);
354 return DomBase::field(self, name);
355}
356
358{
359 self.containingObject().addError(msg);
360}
361
362void QmlFile::writeOut(DomItem &self, OutWriter &ow) const
363{
364 for (DomItem &p : self.field(Fields::pragmas).values()) {
365 p.writeOut(ow);
366 }
367 for (auto i : self.field(Fields::imports).values()) {
368 i.writeOut(ow);
369 }
370 ow.ensureNewline(2);
371 DomItem mainC = self.field(Fields::components).key(QString()).index(0);
372 mainC.writeOut(ow);
373}
374
375std::shared_ptr<OwningItem> GlobalScope::doCopy(DomItem &self) const
376{
377 auto res = std::make_shared<GlobalScope>(
379 return res;
380}
381
383{
384 bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
385 return cont;
386}
387
389{
390 auto it = m_uris.begin();
391 auto end = m_uris.end();
392 DomItem env = self.environment();
393 if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) {
394 while (it != end) {
395 QString uri = it.key();
396 for (int majorV : it.value()) {
397 auto mIndex = envPtr->moduleIndexWithUri(env, uri, majorV, EnvLookup::Normal,
399 mIndex->addQmltypeFilePath(self.canonicalPath());
400 }
401 ++it;
402 }
403 }
404}
405
407{
408 bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
409 cont = cont && self.dvWrapField(visitor, Fields::components, m_components);
410 cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
411 cont = cont && self.dvItemField(visitor, Fields::uris, [this, &self]() {
412 return self.subMapItem(Map::fromMapRef<QSet<int>>(
413 self.pathFromOwner().field(Fields::uris), m_uris,
415 QList<int> l(el.cbegin(), el.cend());
416 std::sort(l.begin(), l.end());
417 return map.subListItem(
418 List::fromQList<int>(map.pathFromOwner().appendComponent(p), l,
419 [](DomItem &list, const PathEls::PathComponent &p,
420 int &el) { return list.subDataItem(p, el); }));
421 }));
422 });
423 cont = cont && self.dvWrapField(visitor, Fields::imports, m_imports);
424 return cont;
425}
426
427QmlDirectory::QmlDirectory(QString filePath, QStringList dirList, QDateTime lastDataUpdateAt,
428 int derivedFrom)
429 : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlDirectoryPath(filePath), derivedFrom,
430 dirList.join(QLatin1Char('\n')))
431{
432 for (QString f : dirList) {
434 }
435}
436
438{
439 bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor);
440 cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
441 cont = cont && self.dvItemField(visitor, Fields::qmlFiles, [this, &self]() -> DomItem {
442 QDir baseDir(canonicalFilePath());
443 return self.subMapItem(Map(
444 self.pathFromOwner().field(Fields::qmlFiles),
445 [this, baseDir](DomItem &map, QString key) -> DomItem {
446 QList<Path> res;
447 auto it = m_qmlFiles.find(key);
448 while (it != m_qmlFiles.end() && it.key() == key) {
449 res.append(Paths::qmlFilePath(
450 QFileInfo(baseDir.filePath(it.value())).canonicalFilePath()));
451 ++it;
452 }
453 return map.subReferencesItem(PathEls::Key(key), res);
454 },
455 [this](DomItem &) {
456 auto keys = m_qmlFiles.keys();
457 return QSet<QString>(keys.begin(), keys.end());
458 },
459 u"List<Reference>"_s));
460 });
461 return cont;
462}
463
465{
467 uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation))"));
468 QRegularExpressionMatch m = qmlFileRe.match(relativePath);
469 if (m.hasMatch() && !m_qmlFiles.values(m.captured(u"compName")).contains(relativePath)) {
470 m_qmlFiles.insert(m.captured(u"compName"), relativePath);
471 Export e;
473 QFileInfo fInfo(dir.filePath(relativePath));
474 e.exportSourcePath = canonicalPath();
475 e.typeName = m.captured(u"compName");
476 e.typePath = Paths::qmlFileObjectPath(fInfo.canonicalFilePath());
477 e.uri = QLatin1String("file://") + canonicalFilePath();
478 m_exports.insert(e.typeName, e);
479 return true;
480 }
481 return false;
482}
483
484} // end namespace Dom
485} // end namespace QQmlJS
486
\inmodule QtCore\reentrant
Definition qcborarray.h:20
\inmodule QtCore\reentrant
Definition qcbormap.h:21
\inmodule QtCore\reentrant
Definition qcborvalue.h:50
\inmodule QtCore\reentrant
Definition qdatetime.h:257
static QDateTime currentDateTimeUtc()
\inmodule QtCore
Definition qdir.h:19
QString dirName() const
Returns the name of the directory; this is not the same as the path, e.g.
Definition qdir.cpp:715
QString filePath(const QString &fileName) const
Returns the path name of a file in the directory.
Definition qdir.cpp:778
@ Files
Definition qdir.h:22
@ Readable
Definition qdir.h:28
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString suffix() const
Returns the suffix (extension) of the file.
QString canonicalFilePath() const
Returns the canonical path including the file name, i.e.
QDir dir() const
Returns the path of the object's parent directory as a QDir object.
Definition qlist.h:74
Definition qmap.h:186
iterator begin()
Definition qmap.h:597
iterator end()
Definition qmap.h:601
QList< Key > keys() const
Definition qmap.h:1049
iterator insert(const Key &key, const T &value)
Definition qmap.h:1425
QList< T > values() const
Definition qmap.h:1078
bool parse(const QString &source)
url is used for generating errors.
QString typeNamespace() const
QList< Plugin > plugins() const
Associates comments with AST::Node *.
virtual DomItem field(DomItem &self, QStringView name) const
Represents a consistent set of types organized in modules, it is the top level of the DOM.
static ErrorGroup domErrorGroup
std::shared_ptr< T > ownerAs()
void writeOut(OutWriter &lw)
Represents a set of tags grouping a set of related error messages.
ErrorMessage errorMessage(Dumper msg, ErrorLevel level, Path element=Path(), QString canonicalFilePath=QString(), SourceLocation location=SourceLocation()) const
Represents an error message connected to the dom.
ErrorMessage & withPath(const Path &)
ErrorMessage & withFile(QString)
A OwningItem that refers to an external resource (file,...)
ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path pathFromTop, int derivedFrom=0, QString code=QString())
bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
Represents and maintains a mapping between elements and their location in a file.
std::shared_ptr< OwningItem > doCopy(DomItem &) const override
bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
OutWriter & ensureNewline(int nNewlines=1)
virtual QDateTime lastDataUpdateAt() const
void addErrorLocal(ErrorMessage msg)
virtual int revision() const
Path field(QString name) const
static Path Field(QStringView s=u"")
bool addQmlFilePath(QString relativePath)
bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
DomItem field(DomItem &self, QStringView name) const override
void addError(DomItem &self, ErrorMessage msg) override
void writeOut(DomItem &self, OutWriter &lw) const override
bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override
static ErrorGroups myParsingErrors()
static QmlUri fromDirectoryString(const QString &importStr)
static QmlUri fromUriString(const QString &importStr)
bool isValid() const
QString toString() const
static ErrorGroups myParsingErrors()
const QList< Import > & imports() const &
bool iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) override
QList< ModuleAutoExport > autoExports() const
void ensureInModuleIndex(DomItem &self, QString uri)
static std::shared_ptr< QmldirFile > fromPathAndCode(QString path, QString code)
QMap< QString, QString > qmlFiles() const
void setAutoExports(const QList< ModuleAutoExport > &autoExport)
void ensureInModuleIndex(DomItem &self)
bool iterateDirectSubpaths(DomItem &self, DirectVisitor) override
static constexpr qint32 Undefined
static constexpr qint32 Latest
void setCode(const QString &code, int lineno, bool qmlMode=true, CodeContinuation codeContinuation=CodeContinuation::Reset)
Represents an immutable JsonPath like path in the Qml code model (from a DomItem to another DomItem)
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
QRegularExpressionMatch match(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to match the regular expression against the given subject string, starting at the position o...
static QString anchoredPattern(const QString &expression)
Definition qset.h:18
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:660
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QMap< QString, QString > map
[6]
double e
QSet< QString >::iterator it
Path jsFilePath(QString path)
Path qmltypesFilePath(QString path)
Path qmlFileObjectPath(QString canonicalFilePath)
QCborValue pluginData(QQmlDirParser::Plugin &pl, QStringList cNames)
static QString toString(const UiQualifiedId *qualifiedId, QChar delimiter=QLatin1Char('.'))
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
#define qWarning
Definition qlogging.h:162
GLsizei const GLfloat * v
[13]
const GLfloat * m
GLuint64 key
GLint GLenum GLint components
GLuint index
[2]
GLuint GLuint end
GLfloat GLfloat f
GLuint name
GLfloat n
GLuint res
GLuint GLuint * names
GLuint entry
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define NewErrorGroup(name)
SSL_CTX int(*) void arg)
static QString canonicalPath(const QString &rootPath)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define tr(X)
QList< int > list
[14]
QStringList keys
QString dir
[11]
QStringView el
\inmodule QtCore \reentrant
Definition qchar.h:17
bool contains(const AT &t) const noexcept
Definition qlist.h:44