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