1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qmlcodeparser.h"
5
6#include "node.h"
7#include "qmlvisitor.h"
8#include "utilities.h"
9
10#include <private/qqmljsast_p.h>
11
12#include <qdebug.h>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 Returns "QML".
18 */
19QString QmlCodeParser::language()
20{
21 return "QML";
22}
23
24/*!
25 Returns a string list containing "*.qml". This is the only
26 file type parsed by the QMLN parser.
27 */
28QStringList QmlCodeParser::sourceFileNameFilter()
29{
30 return QStringList() << "*.qml";
31}
32
33/*!
34 Parses the source file at \a filePath and inserts the contents
35 into the database. The \a location is used for error reporting.
36
37 If it can't open the file at \a filePath, it reports an error
38 and returns without doing anything.
39 */
40void QmlCodeParser::parseSourceFile(const Location &location, const QString &filePath, CppCodeParser&)
41{
42 static const QSet<QString> topic_commands{
43 COMMAND_VARIABLE, COMMAND_QMLCLASS, COMMAND_QMLTYPE, COMMAND_QMLPROPERTY,
44 COMMAND_QMLPROPERTYGROUP, COMMAND_QMLATTACHEDPROPERTY, COMMAND_QMLSIGNAL,
45 COMMAND_QMLATTACHEDSIGNAL, COMMAND_QMLMETHOD, COMMAND_QMLATTACHEDMETHOD,
46 COMMAND_QMLVALUETYPE, COMMAND_QMLBASICTYPE,
47 };
48
49 QFile in(filePath);
50 if (!in.open(flags: QIODevice::ReadOnly)) {
51 location.error(QStringLiteral("Cannot open QML file '%1'").arg(a: filePath));
52 return;
53 }
54
55 QString document = in.readAll();
56 in.close();
57
58 QString newCode = document;
59 extractPragmas(script&: newCode);
60
61 QQmlJS::Engine engine{};
62 QQmlJS::Lexer lexer{&engine};
63 lexer.setCode(code: newCode, lineno: 1);
64
65 QQmlJS::Parser parser{&engine};
66
67 if (parser.parse()) {
68 QQmlJS::AST::UiProgram *ast = parser.ast();
69 QmlDocVisitor visitor(filePath, newCode, &engine, topic_commands + CodeParser::common_meta_commands,
70 topic_commands);
71 QQmlJS::AST::Node::accept(node: ast, visitor: &visitor);
72 if (visitor.hasError())
73 Location(filePath).warning(message: "Could not analyze QML file, output is incomplete.");
74 }
75 const auto &messages = parser.diagnosticMessages();
76 for (const auto &msg : messages) {
77 qCDebug(lcQdoc, "%s: %d: %d: QML syntax error: %s", qUtf8Printable(filePath),
78 msg.loc.startLine, msg.loc.startColumn, qUtf8Printable(msg.message));
79 }
80}
81
82/*!
83 Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp.
84 This function blanks out the section of the \a str beginning at \a idx
85 and running for \a n characters.
86*/
87void replaceWithSpace(QString &str, int idx, int n) // Also used in qmlcodemarker.cpp.
88{
89 QChar *data = str.data() + idx;
90 const QChar space(QLatin1Char(' '));
91 for (int ii = 0; ii < n; ++ii)
92 *data++ = space;
93}
94
95/*!
96 Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp,
97 then modified to return no values.
98
99 Searches for ".pragma <value>" declarations within \a script.
100 Currently supported pragmas are: library
101*/
102void QmlCodeParser::extractPragmas(QString &script)
103{
104 const QString pragma(QLatin1String("pragma"));
105
106 QQmlJS::Lexer l(nullptr);
107 l.setCode(code: script, lineno: 0);
108
109 int token = l.lex();
110
111 while (true) {
112 if (token != QQmlJSGrammar::T_DOT)
113 return;
114
115 int startOffset = l.tokenOffset();
116 int startLine = l.tokenStartLine();
117
118 token = l.lex();
119
120 if (token != QQmlJSGrammar::T_IDENTIFIER || l.tokenStartLine() != startLine
121 || script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
122 return;
123
124 token = l.lex();
125
126 if (token != QQmlJSGrammar::T_IDENTIFIER || l.tokenStartLine() != startLine)
127 return;
128
129 QString pragmaValue = script.mid(position: l.tokenOffset(), n: l.tokenLength());
130 int endOffset = l.tokenLength() + l.tokenOffset();
131
132 token = l.lex();
133 if (l.tokenStartLine() == startLine)
134 return;
135
136 if (pragmaValue == QLatin1String("library"))
137 replaceWithSpace(str&: script, idx: startOffset, n: endOffset - startOffset);
138 else
139 return;
140 }
141}
142
143QT_END_NAMESPACE
144

source code of qttools/src/qdoc/qdoc/qmlcodeparser.cpp