PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/qt/qtbase/src/tools/qdoc/codeparser.cpp

https://gitlab.com/x33n/phantomjs
C++ | 485 lines | 338 code | 32 blank | 115 comment | 114 complexity | 81da1b3131aea3c6ad09f7147a072c76 MD5 | raw file
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of the tools applications of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL$
  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 Digia. For licensing terms and
  14. ** conditions see http://qt.digia.com/licensing. For further information
  15. ** use the contact form at http://qt.digia.com/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 2.1 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 2.1 requirements
  23. ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  24. **
  25. ** In addition, as a special exception, Digia gives you certain additional
  26. ** rights. These rights are described in the Digia Qt LGPL Exception
  27. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  28. **
  29. ** GNU General Public License Usage
  30. ** Alternatively, this file may be used under the terms of the GNU
  31. ** General Public License version 3.0 as published by the Free Software
  32. ** Foundation and appearing in the file LICENSE.GPL included in the
  33. ** packaging of this file. Please review the following information to
  34. ** ensure the GNU General Public License version 3.0 requirements will be
  35. ** met: http://www.gnu.org/copyleft/gpl.html.
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. /*
  42. codeparser.cpp
  43. */
  44. #include "codeparser.h"
  45. #include "node.h"
  46. #include "tree.h"
  47. #include "config.h"
  48. #include "generator.h"
  49. #include "qdocdatabase.h"
  50. #include <qdebug.h>
  51. QT_BEGIN_NAMESPACE
  52. #define COMMAND_COMPAT Doc::alias(QLatin1String("compat"))
  53. #define COMMAND_DEPRECATED Doc::alias(QLatin1String("deprecated")) // ### don't document
  54. #define COMMAND_INGROUP Doc::alias(QLatin1String("ingroup"))
  55. #define COMMAND_INMODULE Doc::alias(QLatin1String("inmodule")) // ### don't document
  56. #define COMMAND_INQMLMODULE Doc::alias(QLatin1String("inqmlmodule"))
  57. #define COMMAND_INTERNAL Doc::alias(QLatin1String("internal"))
  58. #define COMMAND_MAINCLASS Doc::alias(QLatin1String("mainclass"))
  59. #define COMMAND_NONREENTRANT Doc::alias(QLatin1String("nonreentrant"))
  60. #define COMMAND_OBSOLETE Doc::alias(QLatin1String("obsolete"))
  61. #define COMMAND_PAGEKEYWORDS Doc::alias(QLatin1String("pagekeywords"))
  62. #define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary"))
  63. #define COMMAND_INPUBLICGROUP Doc::alias(QLatin1String("inpublicgroup"))
  64. #define COMMAND_QTVARIABLE Doc::alias(QLatin1String("qtvariable"))
  65. #define COMMAND_REENTRANT Doc::alias(QLatin1String("reentrant"))
  66. #define COMMAND_SINCE Doc::alias(QLatin1String("since"))
  67. #define COMMAND_SUBTITLE Doc::alias(QLatin1String("subtitle"))
  68. #define COMMAND_THREADSAFE Doc::alias(QLatin1String("threadsafe"))
  69. #define COMMAND_TITLE Doc::alias(QLatin1String("title"))
  70. #define COMMAND_WRAPPER Doc::alias(QLatin1String("wrapper"))
  71. QString CodeParser::currentSubDir_;
  72. QList<CodeParser *> CodeParser::parsers;
  73. bool CodeParser::showInternal = false;
  74. QMap<QString,QString> CodeParser::nameToTitle;
  75. /*!
  76. The constructor adds this code parser to the static
  77. list of code parsers.
  78. */
  79. CodeParser::CodeParser()
  80. {
  81. qdb_ = QDocDatabase::qdocDB();
  82. parsers.prepend(this);
  83. }
  84. /*!
  85. The destructor removes this code parser from the static
  86. list of code parsers.
  87. */
  88. CodeParser::~CodeParser()
  89. {
  90. parsers.removeAll(this);
  91. }
  92. /*!
  93. Initialize the code parser base class.
  94. */
  95. void CodeParser::initializeParser(const Config& config)
  96. {
  97. showInternal = config.getBool(CONFIG_SHOWINTERNAL);
  98. }
  99. /*!
  100. Terminating a code parser is trivial.
  101. */
  102. void CodeParser::terminateParser()
  103. {
  104. // nothing.
  105. }
  106. QStringList CodeParser::headerFileNameFilter()
  107. {
  108. return sourceFileNameFilter();
  109. }
  110. void CodeParser::parseHeaderFile(const Location& location, const QString& filePath)
  111. {
  112. parseSourceFile(location, filePath);
  113. }
  114. void CodeParser::doneParsingHeaderFiles()
  115. {
  116. doneParsingSourceFiles();
  117. }
  118. /*!
  119. All the code parsers in the static list are initialized here,
  120. after the qdoc configuration variables have been set.
  121. */
  122. void CodeParser::initialize(const Config& config)
  123. {
  124. QList<CodeParser *>::ConstIterator p = parsers.constBegin();
  125. while (p != parsers.constEnd()) {
  126. (*p)->initializeParser(config);
  127. ++p;
  128. }
  129. }
  130. /*!
  131. All the code parsers in the static list are terminated here.
  132. */
  133. void CodeParser::terminate()
  134. {
  135. QList<CodeParser *>::ConstIterator p = parsers.constBegin();
  136. while (p != parsers.constEnd()) {
  137. (*p)->terminateParser();
  138. ++p;
  139. }
  140. }
  141. CodeParser *CodeParser::parserForLanguage(const QString& language)
  142. {
  143. QList<CodeParser *>::ConstIterator p = parsers.constBegin();
  144. while (p != parsers.constEnd()) {
  145. if ((*p)->language() == language)
  146. return *p;
  147. ++p;
  148. }
  149. return 0;
  150. }
  151. CodeParser *CodeParser::parserForHeaderFile(const QString &filePath)
  152. {
  153. QString fileName = QFileInfo(filePath).fileName();
  154. QList<CodeParser *>::ConstIterator p = parsers.constBegin();
  155. while (p != parsers.constEnd()) {
  156. QStringList headerPatterns = (*p)->headerFileNameFilter();
  157. foreach (const QString &pattern, headerPatterns) {
  158. QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
  159. if (re.exactMatch(fileName))
  160. return *p;
  161. }
  162. ++p;
  163. }
  164. return 0;
  165. }
  166. CodeParser *CodeParser::parserForSourceFile(const QString &filePath)
  167. {
  168. QString fileName = QFileInfo(filePath).fileName();
  169. QList<CodeParser *>::ConstIterator p = parsers.constBegin();
  170. while (p != parsers.constEnd()) {
  171. QStringList sourcePatterns = (*p)->sourceFileNameFilter();
  172. foreach (const QString &pattern, sourcePatterns) {
  173. QRegExp re(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
  174. if (re.exactMatch(fileName))
  175. return *p;
  176. }
  177. ++p;
  178. }
  179. return 0;
  180. }
  181. static QSet<QString> commonMetaCommands_;
  182. /*!
  183. Returns the set of strings representing the common metacommands.
  184. */
  185. const QSet<QString>& CodeParser::commonMetaCommands()
  186. {
  187. if (commonMetaCommands_.isEmpty()) {
  188. commonMetaCommands_ << COMMAND_COMPAT
  189. << COMMAND_DEPRECATED
  190. << COMMAND_INGROUP
  191. << COMMAND_INMODULE
  192. << COMMAND_INQMLMODULE
  193. << COMMAND_INTERNAL
  194. << COMMAND_MAINCLASS
  195. << COMMAND_NONREENTRANT
  196. << COMMAND_OBSOLETE
  197. << COMMAND_PAGEKEYWORDS
  198. << COMMAND_PRELIMINARY
  199. << COMMAND_INPUBLICGROUP
  200. << COMMAND_QTVARIABLE
  201. << COMMAND_REENTRANT
  202. << COMMAND_SINCE
  203. << COMMAND_SUBTITLE
  204. << COMMAND_THREADSAFE
  205. << COMMAND_TITLE
  206. << COMMAND_WRAPPER;
  207. }
  208. return commonMetaCommands_;
  209. }
  210. /*!
  211. The topic command has been processed. Now process the other
  212. metacommands that were found. These are not the text markup
  213. commands.
  214. */
  215. void CodeParser::processCommonMetaCommand(const Location& location,
  216. const QString& command,
  217. const ArgLocPair& arg,
  218. Node* node)
  219. {
  220. if (command == COMMAND_COMPAT) {
  221. location.warning(tr("\\compat command used, but Qt3 compatibility is no longer supported"));
  222. node->setStatus(Node::Compat);
  223. }
  224. else if (command == COMMAND_DEPRECATED) {
  225. node->setStatus(Node::Obsolete);
  226. }
  227. else if ((command == COMMAND_INGROUP) || (command == COMMAND_INPUBLICGROUP)) {
  228. // Note: \ingroup and \inpublicgroup are now the same.
  229. // Not that they were ever different.
  230. qdb_->addToGroup(arg.first, node);
  231. }
  232. else if (command == COMMAND_INMODULE) {
  233. qdb_->addToModule(arg.first,node);
  234. }
  235. else if (command == COMMAND_INQMLMODULE) {
  236. qdb_->addToQmlModule(arg.first,node);
  237. }
  238. else if (command == COMMAND_MAINCLASS) {
  239. node->setStatus(Node::Main);
  240. }
  241. else if (command == COMMAND_OBSOLETE) {
  242. if (node->status() != Node::Compat)
  243. node->setStatus(Node::Obsolete);
  244. }
  245. else if (command == COMMAND_NONREENTRANT) {
  246. node->setThreadSafeness(Node::NonReentrant);
  247. }
  248. else if (command == COMMAND_PRELIMINARY) {
  249. node->setStatus(Node::Preliminary);
  250. }
  251. else if (command == COMMAND_INTERNAL) {
  252. if (!showInternal) {
  253. node->setAccess(Node::Private);
  254. node->setStatus(Node::Internal);
  255. if (node->type() == Node::QmlPropertyGroup) {
  256. const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node);
  257. NodeList::ConstIterator p = qpgn->childNodes().constBegin();
  258. while (p != qpgn->childNodes().constEnd()) {
  259. if ((*p)->type() == Node::QmlProperty) {
  260. (*p)->setAccess(Node::Private);
  261. (*p)->setStatus(Node::Internal);
  262. }
  263. ++p;
  264. }
  265. }
  266. }
  267. }
  268. else if (command == COMMAND_REENTRANT) {
  269. node->setThreadSafeness(Node::Reentrant);
  270. }
  271. else if (command == COMMAND_SINCE) {
  272. node->setSince(arg.first);
  273. }
  274. else if (command == COMMAND_WRAPPER) {
  275. node->setWrapper();
  276. }
  277. else if (command == COMMAND_PAGEKEYWORDS) {
  278. node->addPageKeywords(arg.first);
  279. }
  280. else if (command == COMMAND_SUBTITLE) {
  281. if (node->type() == Node::Document) {
  282. DocNode *dn = static_cast<DocNode *>(node);
  283. dn->setSubTitle(arg.first);
  284. }
  285. else
  286. location.warning(tr("Ignored '\\%1'").arg(COMMAND_SUBTITLE));
  287. }
  288. else if (command == COMMAND_THREADSAFE) {
  289. node->setThreadSafeness(Node::ThreadSafe);
  290. }
  291. else if (command == COMMAND_TITLE) {
  292. if (node->type() == Node::Document) {
  293. DocNode *dn = static_cast<DocNode *>(node);
  294. dn->setTitle(arg.first);
  295. if (dn->subType() == Node::Example) {
  296. ExampleNode::exampleNodeMap.insert(dn->title(),static_cast<ExampleNode*>(dn));
  297. }
  298. nameToTitle.insert(dn->name(),arg.first);
  299. }
  300. else
  301. location.warning(tr("Ignored '\\%1'").arg(COMMAND_TITLE));
  302. }
  303. else if (command == COMMAND_QTVARIABLE) {
  304. if (node->subType() == Node::Module) {
  305. DocNode *dn = static_cast<DocNode *>(node);
  306. dn->setQtVariable(arg.first);
  307. }
  308. else
  309. location.warning(tr("Command '\\%1' found outside of '\\module'. It can only be used within a module page.")
  310. .arg(COMMAND_QTVARIABLE));
  311. }
  312. }
  313. /*!
  314. Find the page title given the page \a name and return it.
  315. */
  316. const QString CodeParser::titleFromName(const QString& name)
  317. {
  318. const QString t = nameToTitle.value(name);
  319. return t;
  320. }
  321. /*!
  322. \internal
  323. */
  324. void CodeParser::extractPageLinkAndDesc(const QString& arg,
  325. QString* link,
  326. QString* desc)
  327. {
  328. QRegExp bracedRegExp(QLatin1String("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?"));
  329. if (bracedRegExp.exactMatch(arg)) {
  330. *link = bracedRegExp.cap(1);
  331. *desc = bracedRegExp.cap(2);
  332. if (desc->isEmpty())
  333. *desc = *link;
  334. }
  335. else {
  336. int spaceAt = arg.indexOf(QLatin1Char(' '));
  337. if (arg.contains(QLatin1String(".html")) && spaceAt != -1) {
  338. *link = arg.left(spaceAt).trimmed();
  339. *desc = arg.mid(spaceAt).trimmed();
  340. }
  341. else {
  342. *link = arg;
  343. *desc = arg;
  344. }
  345. }
  346. }
  347. /*!
  348. \internal
  349. */
  350. void CodeParser::setLink(Node* node, Node::LinkType linkType, const QString& arg)
  351. {
  352. QString link;
  353. QString desc;
  354. extractPageLinkAndDesc(arg, &link, &desc);
  355. node->setLink(linkType, link, desc);
  356. }
  357. /*!
  358. Returns \c true if the file being parsed is a .h file.
  359. */
  360. bool CodeParser::isParsingH() const
  361. {
  362. return currentFile_.endsWith(".h");
  363. }
  364. /*!
  365. Returns \c true if the file being parsed is a .cpp file.
  366. */
  367. bool CodeParser::isParsingCpp() const
  368. {
  369. return currentFile_.endsWith(".cpp");
  370. }
  371. /*!
  372. Returns \c true if the file being parsed is a .qdoc file.
  373. */
  374. bool CodeParser::isParsingQdoc() const
  375. {
  376. return currentFile_.endsWith(".qdoc");
  377. }
  378. /*!
  379. For each node that will produce a documentation page, this function
  380. ensures that the node belongs to a module. Normally, the qdoc comment
  381. for an entity that will produce a documentation page will contain an
  382. \inmodule command to tell qdoc which module the entity belongs to.
  383. But now that we normally run qdoc on each module in two passes. The
  384. first produces an index file; the second pass generates the docs
  385. after reading all the index files it needs.
  386. This means that all the pages generated during each pass 2 run of
  387. qdoc almost certainly belong to a single module, and the name of
  388. that module is, as a rule, used as the project name in the qdocconf
  389. file used when running qdoc on the module.
  390. So this function first asks if the node \a n has a non-empty module
  391. name. If it it does not have a non-empty module name, it sets the
  392. module name to be the project name.
  393. In some cases it prints a qdoc warning that it has done this. Namely,
  394. for C++ classes and namespaces.
  395. */
  396. void CodeParser::checkModuleInclusion(Node* n)
  397. {
  398. if (n->moduleName().isEmpty()) {
  399. switch (n->type()) {
  400. case Node::Class:
  401. if (n->access() != Node::Private && !n->doc().isEmpty()) {
  402. n->setModuleName(Generator::defaultModuleName());
  403. n->doc().location().warning(tr("Class %1 has no \\inmodule command; "
  404. "using project name by default: %2")
  405. .arg(n->name()).arg(Generator::defaultModuleName()));
  406. }
  407. break;
  408. case Node::Namespace:
  409. if (n->access() != Node::Private && !n->name().isEmpty() && !n->doc().isEmpty()) {
  410. n->setModuleName(Generator::defaultModuleName());
  411. n->doc().location().warning(tr("Namespace %1 has no \\inmodule command; "
  412. "using project name by default: %2")
  413. .arg(n->name()).arg(Generator::defaultModuleName()));
  414. }
  415. break;
  416. case Node::Document:
  417. if (n->access() != Node::Private && !n->doc().isEmpty()) {
  418. if (n->subType() == Node::HeaderFile) {
  419. n->setModuleName(Generator::defaultModuleName());
  420. #if 0
  421. n->doc().location().warning(tr("Header file with title \"%1\" has no \\inmodule command; "
  422. "using project name by default: %2")
  423. .arg(n->title()).arg(Generator::defaultModuleName()));
  424. #endif
  425. }
  426. else if (n->subType() == Node::Page) {
  427. n->setModuleName(Generator::defaultModuleName());
  428. #if 0
  429. n->doc().location().warning(tr("Page with title \"%1\" has no \\inmodule command; "
  430. "using project name by default: %2")
  431. .arg(n->title()).arg(Generator::defaultModuleName()));
  432. #endif
  433. }
  434. else if (n->subType() == Node::Example) {
  435. n->setModuleName(Generator::defaultModuleName());
  436. #if 0
  437. n->doc().location().warning(tr("Example with title \"%1\" has no \\inmodule command; "
  438. "using project name by default: %2")
  439. .arg(n->title()).arg(Generator::defaultModuleName()));
  440. #endif
  441. }
  442. }
  443. break;
  444. default:
  445. break;
  446. }
  447. }
  448. }
  449. QT_END_NAMESPACE