1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications 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 /*
30 codeparser.cpp
31 */
32
33 #include "codeparser.h"
34
35 #include "config.h"
36 #include "generator.h"
37 #include "node.h"
38 #include "qdocdatabase.h"
39 #include "tree.h"
40
41 #include <QtCore/qdebug.h>
42
43 QT_BEGIN_NAMESPACE
44
45 QVector<CodeParser *> CodeParser::parsers;
46 bool CodeParser::showInternal_ = false;
47 bool CodeParser::singleExec_ = false;
48
49 /*!
50 The constructor adds this code parser to the static
51 list of code parsers.
52 */
CodeParser()53 CodeParser::CodeParser()
54 {
55 qdb_ = QDocDatabase::qdocDB();
56 parsers.prepend(this);
57 }
58
59 /*!
60 The destructor removes this code parser from the static
61 list of code parsers.
62 */
~CodeParser()63 CodeParser::~CodeParser()
64 {
65 parsers.removeAll(this);
66 }
67
68 /*!
69 Initialize the code parser base class.
70 */
initializeParser()71 void CodeParser::initializeParser()
72 {
73 showInternal_ = Config::instance().getBool(CONFIG_SHOWINTERNAL);
74 singleExec_ = Config::instance().getBool(CONFIG_SINGLEEXEC);
75 }
76
77 /*!
78 Terminating a code parser is trivial.
79 */
terminateParser()80 void CodeParser::terminateParser()
81 {
82 // nothing.
83 }
84
headerFileNameFilter()85 QStringList CodeParser::headerFileNameFilter()
86 {
87 return sourceFileNameFilter();
88 }
89
parseHeaderFile(const Location & location,const QString & filePath)90 void CodeParser::parseHeaderFile(const Location &location, const QString &filePath)
91 {
92 parseSourceFile(location, filePath);
93 }
94
95 /*!
96 All the code parsers in the static list are initialized here,
97 after the qdoc configuration variables have been set.
98 */
initialize()99 void CodeParser::initialize()
100 {
101 for (const auto &parser : qAsConst(parsers))
102 parser->initializeParser();
103 }
104
105 /*!
106 All the code parsers in the static list are terminated here.
107 */
terminate()108 void CodeParser::terminate()
109 {
110 for (const auto parser : parsers)
111 parser->terminateParser();
112 }
113
parserForLanguage(const QString & language)114 CodeParser *CodeParser::parserForLanguage(const QString &language)
115 {
116 for (const auto parser : qAsConst(parsers)) {
117 if (parser->language() == language)
118 return parser;
119 }
120 return nullptr;
121 }
122
parserForHeaderFile(const QString & filePath)123 CodeParser *CodeParser::parserForHeaderFile(const QString &filePath)
124 {
125 QString fileName = QFileInfo(filePath).fileName();
126
127 for (const auto &parser : qAsConst(parsers)) {
128 const QStringList headerPatterns = parser->headerFileNameFilter();
129 for (const auto &pattern : headerPatterns) {
130 QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
131 if (re.exactMatch(fileName))
132 return parser;
133 }
134 }
135 return nullptr;
136 }
137
parserForSourceFile(const QString & filePath)138 CodeParser *CodeParser::parserForSourceFile(const QString &filePath)
139 {
140 QString fileName = QFileInfo(filePath).fileName();
141
142 for (const auto &parser : parsers) {
143 const QStringList sourcePatterns = parser->sourceFileNameFilter();
144 for (const QString &pattern : sourcePatterns) {
145 QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
146 if (re.exactMatch(fileName))
147 return parser;
148 }
149 }
150 return nullptr;
151 }
152
153 static QSet<QString> commonMetaCommands_;
154 /*!
155 Returns the set of strings representing the common metacommands.
156 */
commonMetaCommands()157 const QSet<QString> &CodeParser::commonMetaCommands()
158 {
159 if (commonMetaCommands_.isEmpty()) {
160 commonMetaCommands_ << COMMAND_ABSTRACT << COMMAND_DEPRECATED << COMMAND_INGROUP
161 << COMMAND_INJSMODULE << COMMAND_INMODULE << COMMAND_INPUBLICGROUP
162 << COMMAND_INQMLMODULE << COMMAND_INTERNAL << COMMAND_MAINCLASS
163 << COMMAND_NOAUTOLIST << COMMAND_NONREENTRANT << COMMAND_OBSOLETE
164 << COMMAND_PRELIMINARY << COMMAND_QMLABSTRACT << COMMAND_QMLDEFAULT
165 << COMMAND_QMLINHERITS << COMMAND_QMLREADONLY << COMMAND_QTVARIABLE
166 << COMMAND_REENTRANT << COMMAND_SINCE << COMMAND_STARTPAGE
167 << COMMAND_SUBTITLE << COMMAND_THREADSAFE << COMMAND_TITLE
168 << COMMAND_WRAPPER;
169 }
170 return commonMetaCommands_;
171 }
172
173 /*!
174 \internal
175 */
extractPageLinkAndDesc(const QString & arg,QString * link,QString * desc)176 void CodeParser::extractPageLinkAndDesc(const QString &arg, QString *link, QString *desc)
177 {
178 QRegExp bracedRegExp(QLatin1String("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?"));
179
180 if (bracedRegExp.exactMatch(arg)) {
181 *link = bracedRegExp.cap(1);
182 *desc = bracedRegExp.cap(2);
183 if (desc->isEmpty())
184 *desc = *link;
185 } else {
186 int spaceAt = arg.indexOf(QLatin1Char(' '));
187 if (arg.contains(QLatin1String(".html")) && spaceAt != -1) {
188 *link = arg.leftRef(spaceAt).trimmed().toString();
189 *desc = arg.midRef(spaceAt).trimmed().toString();
190 } else {
191 *link = arg;
192 *desc = arg;
193 }
194 }
195 }
196
197 /*!
198 \internal
199 */
setLink(Node * node,Node::LinkType linkType,const QString & arg)200 void CodeParser::setLink(Node *node, Node::LinkType linkType, const QString &arg)
201 {
202 QString link;
203 QString desc;
204 extractPageLinkAndDesc(arg, &link, &desc);
205 node->setLink(linkType, link, desc);
206 }
207
208 /*!
209 \brief Test for whether a doc comment warrants warnings.
210
211 Returns true if qdoc should report that it has found something
212 wrong with the qdoc comment in \a doc. Sometimes, qdoc should
213 not report the warning, for example, when the comment contains
214 the \c internal command, which normally means qdoc will not use
215 the comment in the documentation anyway, so there is no point
216 in reporting warnings about it.
217 */
isWorthWarningAbout(const Doc & doc)218 bool CodeParser::isWorthWarningAbout(const Doc &doc)
219 {
220 return (showInternal_ || !doc.metaCommandsUsed().contains(QStringLiteral("internal")));
221 }
222
223 /*!
224 Returns \c true if the file being parsed is a .h file.
225 */
isParsingH() const226 bool CodeParser::isParsingH() const
227 {
228 return currentFile_.endsWith(".h");
229 }
230
231 /*!
232 Returns \c true if the file being parsed is a .cpp file.
233 */
isParsingCpp() const234 bool CodeParser::isParsingCpp() const
235 {
236 return currentFile_.endsWith(".cpp");
237 }
238
239 /*!
240 Returns \c true if the file being parsed is a .qdoc file.
241 */
isParsingQdoc() const242 bool CodeParser::isParsingQdoc() const
243 {
244 return currentFile_.endsWith(".qdoc");
245 }
246
247 /*!
248 For each node that will produce a documentation page, this function
249 ensures that the node belongs to a module. Normally, the qdoc comment
250 for an entity that will produce a documentation page will contain an
251 \inmodule command to tell qdoc which module the entity belongs to.
252
253 But now we normally run qdoc on each module in two passes. The first
254 produces an index file; the second pass generates the docs after
255 reading all the index files it needs.
256
257 This means that all the pages generated during each pass 2 run of
258 qdoc almost certainly belong to a single module, and the name of
259 that module is, as a rule, used as the project name in the qdocconf
260 file used when running qdoc on the module.
261
262 So this function first asks if the node \a n has a non-empty module
263 name. If it it does not have a non-empty module name, it sets the
264 module name to be the project name.
265
266 In some cases it prints a qdoc warning that it has done this. Namely,
267 for C++ classes and namespaces.
268 */
checkModuleInclusion(Node * n)269 void CodeParser::checkModuleInclusion(Node *n)
270 {
271 if (n->physicalModuleName().isEmpty()) {
272 n->setPhysicalModuleName(Generator::defaultModuleName());
273
274 if (n->isInAPI() && !n->name().isEmpty()) {
275 QString word;
276 switch (n->nodeType()) {
277 case Node::Class:
278 word = QLatin1String("Class");
279 break;
280 case Node::Struct:
281 word = QLatin1String("Struct");
282 break;
283 case Node::Union:
284 word = QLatin1String("Union");
285 break;
286 case Node::Namespace:
287 word = QLatin1String("Namespace");
288 break;
289 default:
290 return;
291 }
292
293 qdb_->addToModule(Generator::defaultModuleName(), n);
294 n->doc().location().warning(tr("%1 %2 has no \\inmodule command; "
295 "using project name by default: %3")
296 .arg(word)
297 .arg(n->name())
298 .arg(Generator::defaultModuleName()));
299 }
300 }
301 }
302
303 QT_END_NAMESPACE
304