1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <private/qqmljsengine_p.h>
30#include <private/qqmljsparser_p.h>
31#include <private/qqmljslexer_p.h>
32#include <private/qqmljsastvisitor_p.h>
33#include <private/qqmljsast_p.h>
34
35#include <qtest.h>
36#include <QDir>
37#include <QDebug>
38#include <cstdlib>
39
40class tst_qqmlparser : public QObject
41{
42 Q_OBJECT
43public:
44 tst_qqmlparser();
45
46private slots:
47 void initTestCase();
48#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
49 void qmlParser_data();
50 void qmlParser();
51#endif
52 void invalidEscapeSequence();
53 void stringLiteral();
54 void noSubstitutionTemplateLiteral();
55 void templateLiteral();
56
57private:
58 QStringList excludedDirs;
59
60 QStringList findFiles(const QDir &);
61};
62
63namespace check {
64
65using namespace QQmlJS;
66
67class Check: public AST::Visitor
68{
69 QList<AST::Node *> nodeStack;
70
71public:
72 void operator()(AST::Node *node)
73 {
74 AST::Node::accept(node, visitor: this);
75 }
76
77 void checkNode(AST::Node *node)
78 {
79 if (! nodeStack.isEmpty()) {
80 AST::Node *parent = nodeStack.last();
81 const quint32 parentBegin = parent->firstSourceLocation().begin();
82 const quint32 parentEnd = parent->lastSourceLocation().end();
83
84 if (node->firstSourceLocation().begin() < parentBegin)
85 qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn
86 << "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn;
87 if (node->lastSourceLocation().end() > parentEnd)
88 qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn
89 << "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn;
90
91 QVERIFY(node->firstSourceLocation().begin() >= parentBegin);
92 QVERIFY(node->lastSourceLocation().end() <= parentEnd);
93 }
94 }
95
96 virtual bool preVisit(AST::Node *node)
97 {
98 checkNode(node);
99 nodeStack.append(t: node);
100 return true;
101 }
102
103 virtual void postVisit(AST::Node *)
104 {
105 nodeStack.removeLast();
106 }
107
108 void throwRecursionDepthError()
109 {
110 QFAIL("Maximum statement or expression depth exceeded");
111 }
112};
113
114}
115
116tst_qqmlparser::tst_qqmlparser()
117{
118}
119
120void tst_qqmlparser::initTestCase()
121{
122 // Add directories you want excluded here:
123 // excludedDirs << "exclude/this/dir";
124}
125
126QStringList tst_qqmlparser::findFiles(const QDir &d)
127{
128 for (int ii = 0; ii < excludedDirs.count(); ++ii) {
129 QString s = excludedDirs.at(i: ii);
130 if (d.absolutePath().endsWith(s))
131 return QStringList();
132 }
133
134 QStringList rv;
135
136 QStringList files = d.entryList(nameFilters: QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"),
137 filters: QDir::Files);
138 foreach (const QString &file, files) {
139 rv << d.absoluteFilePath(fileName: file);
140 }
141
142 QStringList dirs = d.entryList(filters: QDir::Dirs | QDir::NoDotAndDotDot |
143 QDir::NoSymLinks);
144 foreach (const QString &dir, dirs) {
145 QDir sub = d;
146 sub.cd(dirName: dir);
147 rv << findFiles(d: sub);
148 }
149
150 return rv;
151}
152
153/*
154This test checks all the qml and js files in the QtQml UI source tree
155and ensures that the subnode's source locations are inside parent node's source locations
156*/
157
158#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
159void tst_qqmlparser::qmlParser_data()
160{
161 QTest::addColumn<QString>(name: "file");
162
163 QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
164 QString tests = QLatin1String(SRCDIR) + "/../../../../tests/";
165
166 QStringList files;
167 files << findFiles(d: QDir(examples));
168 files << findFiles(d: QDir(tests));
169
170 foreach (const QString &file, files)
171 QTest::newRow(qPrintable(file)) << file;
172}
173#endif
174
175#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
176void tst_qqmlparser::qmlParser()
177{
178 QFETCH(QString, file);
179
180 using namespace QQmlJS;
181
182 QString code;
183
184 QFile f(file);
185 if (f.open(flags: QFile::ReadOnly))
186 code = QString::fromUtf8(str: f.readAll());
187
188 const bool qmlMode = file.endsWith(s: QLatin1String(".qml"));
189
190 Engine engine;
191 Lexer lexer(&engine);
192 lexer.setCode(code, lineno: 1, qmlMode);
193 Parser parser(&engine);
194 bool ok = qmlMode ? parser.parse() : parser.parseProgram();
195
196 if (ok) {
197 check::Check chk;
198 chk(parser.rootNode());
199 }
200}
201#endif
202
203void tst_qqmlparser::invalidEscapeSequence()
204{
205 using namespace QQmlJS;
206
207 Engine engine;
208 Lexer lexer(&engine);
209 lexer.setCode(code: QLatin1String("\"\\"), lineno: 1);
210 Parser parser(&engine);
211 parser.parse();
212}
213
214void tst_qqmlparser::stringLiteral()
215{
216 using namespace QQmlJS;
217
218 Engine engine;
219 Lexer lexer(&engine);
220 QLatin1String code("'hello string'");
221 lexer.setCode(code , lineno: 1);
222 Parser parser(&engine);
223 QVERIFY(parser.parseExpression());
224 AST::ExpressionNode *expression = parser.expression();
225 QVERIFY(expression);
226 auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(ast: expression);
227 QVERIFY(literal);
228 QCOMPARE(literal->value, "hello string");
229 QCOMPARE(literal->firstSourceLocation().begin(), 0);
230 QCOMPARE(literal->lastSourceLocation().end(), code.size());
231}
232
233void tst_qqmlparser::noSubstitutionTemplateLiteral()
234{
235 using namespace QQmlJS;
236
237 Engine engine;
238 Lexer lexer(&engine);
239 QLatin1String code("`hello template`");
240 lexer.setCode(code, lineno: 1);
241 Parser parser(&engine);
242 QVERIFY(parser.parseExpression());
243 AST::ExpressionNode *expression = parser.expression();
244 QVERIFY(expression);
245
246 auto *literal = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(ast: expression);
247 QVERIFY(literal);
248
249 QCOMPARE(literal->value, "hello template");
250 QCOMPARE(literal->firstSourceLocation().begin(), 0);
251 QCOMPARE(literal->lastSourceLocation().end(), code.size());
252}
253
254void tst_qqmlparser::templateLiteral()
255{
256 using namespace QQmlJS;
257
258 Engine engine;
259 Lexer lexer(&engine);
260 QLatin1String code("`one plus one equals ${1+1}!`");
261 lexer.setCode(code, lineno: 1);
262 Parser parser(&engine);
263 QVERIFY(parser.parseExpression());
264 AST::ExpressionNode *expression = parser.expression();
265 QVERIFY(expression);
266
267 auto *templateLiteral = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(ast: expression);
268 QVERIFY(templateLiteral);
269
270 QCOMPARE(templateLiteral->firstSourceLocation().begin(), 0);
271 auto *e = templateLiteral->expression;
272 QVERIFY(e);
273}
274
275QTEST_MAIN(tst_qqmlparser)
276
277#include "tst_qqmlparser.moc"
278

source code of qtdoc/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp