PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://gitlab.com/x33n/phantomjs
C++ | 1431 lines | 1148 code | 48 blank | 235 comment | 224 complexity | b5809c04c792df6e280ed6759d8062e3 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. cppcodeparser.cpp
  43. */
  44. #include <qfile.h>
  45. #include <stdio.h>
  46. #include <errno.h>
  47. #include "codechunk.h"
  48. #include "config.h"
  49. #include "cppcodeparser.h"
  50. #include "tokenizer.h"
  51. #include "qdocdatabase.h"
  52. #include <qdebug.h>
  53. QT_BEGIN_NAMESPACE
  54. /* qmake ignore Q_OBJECT */
  55. static bool inMacroCommand_ = false;
  56. QStringList CppCodeParser::exampleFiles;
  57. QStringList CppCodeParser::exampleDirs;
  58. /*!
  59. The constructor initializes some regular expressions
  60. and calls reset().
  61. */
  62. CppCodeParser::CppCodeParser()
  63. : varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
  64. {
  65. reset();
  66. }
  67. /*!
  68. The destructor is trivial.
  69. */
  70. CppCodeParser::~CppCodeParser()
  71. {
  72. // nothing.
  73. }
  74. /*!
  75. The constructor initializes a map of special node types
  76. for identifying important nodes. And it initializes
  77. some filters for identifying certain kinds of files.
  78. */
  79. void CppCodeParser::initializeParser(const Config &config)
  80. {
  81. CodeParser::initializeParser(config);
  82. /*
  83. All these can appear in a C++ namespace. Don't add
  84. anything that can't be in a C++ namespace.
  85. */
  86. nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
  87. nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
  88. nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
  89. nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
  90. nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
  91. nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);
  92. exampleFiles = config.getCleanPathList(CONFIG_EXAMPLES);
  93. exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
  94. QStringList exampleFilePatterns = config.getStringList(
  95. CONFIG_EXAMPLES + Config::dot + CONFIG_FILEEXTENSIONS);
  96. if (!exampleFilePatterns.isEmpty())
  97. exampleNameFilter = exampleFilePatterns.join(' ');
  98. else
  99. exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.dita *.ui";
  100. QStringList exampleImagePatterns = config.getStringList(
  101. CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
  102. if (!exampleImagePatterns.isEmpty())
  103. exampleImageFilter = exampleImagePatterns.join(' ');
  104. else
  105. exampleImageFilter = "*.png";
  106. }
  107. /*!
  108. Clear the map of common node types and call
  109. the same function in the base class.
  110. */
  111. void CppCodeParser::terminateParser()
  112. {
  113. nodeTypeMap.clear();
  114. CodeParser::terminateParser();
  115. }
  116. /*!
  117. Returns "Cpp".
  118. */
  119. QString CppCodeParser::language()
  120. {
  121. return "Cpp";
  122. }
  123. /*!
  124. Returns a list of extensions for header files.
  125. */
  126. QStringList CppCodeParser::headerFileNameFilter()
  127. {
  128. return QStringList() << "*.ch" << "*.h" << "*.h++" << "*.hh" << "*.hpp" << "*.hxx";
  129. }
  130. /*!
  131. Returns a list of extensions for source files, i.e. not
  132. header files.
  133. */
  134. QStringList CppCodeParser::sourceFileNameFilter()
  135. {
  136. return QStringList() << "*.c++" << "*.cc" << "*.cpp" << "*.cxx" << "*.mm";
  137. }
  138. /*!
  139. Parse the C++ header file identified by \a filePath and add
  140. the parsed contents to the database. The \a location is used
  141. for reporting errors.
  142. */
  143. void CppCodeParser::parseHeaderFile(const Location& location, const QString& filePath)
  144. {
  145. QFile in(filePath);
  146. currentFile_ = filePath;
  147. if (!in.open(QIODevice::ReadOnly)) {
  148. location.error(tr("Cannot open C++ header file '%1'").arg(filePath));
  149. currentFile_.clear();
  150. return;
  151. }
  152. reset();
  153. Location fileLocation(filePath);
  154. Tokenizer fileTokenizer(fileLocation, in);
  155. tokenizer = &fileTokenizer;
  156. readToken();
  157. matchDeclList(qdb_->treeRoot());
  158. if (!fileTokenizer.version().isEmpty())
  159. qdb_->setVersion(fileTokenizer.version());
  160. in.close();
  161. if (fileLocation.fileName() == "qiterator.h")
  162. parseQiteratorDotH(location, filePath);
  163. currentFile_.clear();
  164. }
  165. /*!
  166. Get ready to parse the C++ cpp file identified by \a filePath
  167. and add its parsed contents to the database. \a location is
  168. used for reporting errors.
  169. Call matchDocsAndStuff() to do all the parsing and tree building.
  170. */
  171. void CppCodeParser::parseSourceFile(const Location& location, const QString& filePath)
  172. {
  173. QFile in(filePath);
  174. currentFile_ = filePath;
  175. if (!in.open(QIODevice::ReadOnly)) {
  176. location.error(tr("Cannot open C++ source file '%1' (%2)").arg(filePath).arg(strerror(errno)));
  177. currentFile_.clear();
  178. return;
  179. }
  180. reset();
  181. Location fileLocation(filePath);
  182. Tokenizer fileTokenizer(fileLocation, in);
  183. tokenizer = &fileTokenizer;
  184. readToken();
  185. /*
  186. The set of open namespaces is cleared before parsing
  187. each source file. The word "source" here means cpp file.
  188. */
  189. qdb_->clearOpenNamespaces();
  190. matchDocsAndStuff();
  191. in.close();
  192. currentFile_.clear();
  193. }
  194. /*!
  195. This is called after all the header files have been parsed.
  196. I think the most important thing it does is resolve class
  197. inheritance links in the tree. But it also initializes a
  198. bunch of stuff.
  199. */
  200. void CppCodeParser::doneParsingHeaderFiles()
  201. {
  202. qdb_->resolveInheritance();
  203. QMapIterator<QString, QString> i(sequentialIteratorClasses);
  204. while (i.hasNext()) {
  205. i.next();
  206. instantiateIteratorMacro(i.key(), i.value(), sequentialIteratorDefinition);
  207. }
  208. i = mutableSequentialIteratorClasses;
  209. while (i.hasNext()) {
  210. i.next();
  211. instantiateIteratorMacro(i.key(), i.value(), mutableSequentialIteratorDefinition);
  212. }
  213. i = associativeIteratorClasses;
  214. while (i.hasNext()) {
  215. i.next();
  216. instantiateIteratorMacro(i.key(), i.value(), associativeIteratorDefinition);
  217. }
  218. i = mutableAssociativeIteratorClasses;
  219. while (i.hasNext()) {
  220. i.next();
  221. instantiateIteratorMacro(i.key(), i.value(), mutableAssociativeIteratorDefinition);
  222. }
  223. sequentialIteratorDefinition.clear();
  224. mutableSequentialIteratorDefinition.clear();
  225. associativeIteratorDefinition.clear();
  226. mutableAssociativeIteratorDefinition.clear();
  227. sequentialIteratorClasses.clear();
  228. mutableSequentialIteratorClasses.clear();
  229. associativeIteratorClasses.clear();
  230. mutableAssociativeIteratorClasses.clear();
  231. }
  232. /*!
  233. This is called after all the source files (i.e., not the
  234. header files) have been parsed. It traverses the tree to
  235. resolve property links, normalize overload signatures, and
  236. do other housekeeping of the database.
  237. */
  238. void CppCodeParser::doneParsingSourceFiles()
  239. {
  240. qdb_->treeRoot()->clearCurrentChildPointers();
  241. qdb_->treeRoot()->normalizeOverloads();
  242. qdb_->fixInheritance();
  243. qdb_->resolveProperties();
  244. qdb_->treeRoot()->makeUndocumentedChildrenInternal();
  245. }
  246. static QSet<QString> topicCommands_;
  247. /*!
  248. Returns the set of strings reopresenting the topic commands.
  249. */
  250. const QSet<QString>& CppCodeParser::topicCommands()
  251. {
  252. if (topicCommands_.isEmpty()) {
  253. topicCommands_ << COMMAND_CLASS
  254. << COMMAND_DITAMAP
  255. << COMMAND_ENUM
  256. << COMMAND_EXAMPLE
  257. << COMMAND_EXTERNALPAGE
  258. << COMMAND_FILE
  259. << COMMAND_FN
  260. << COMMAND_GROUP
  261. << COMMAND_HEADERFILE
  262. << COMMAND_MACRO
  263. << COMMAND_MODULE
  264. << COMMAND_NAMESPACE
  265. << COMMAND_PAGE
  266. << COMMAND_PROPERTY
  267. << COMMAND_TYPEDEF
  268. << COMMAND_VARIABLE
  269. << COMMAND_QMLCLASS
  270. << COMMAND_QMLTYPE
  271. << COMMAND_QMLPROPERTY
  272. << COMMAND_QMLPROPERTYGROUP
  273. << COMMAND_QMLATTACHEDPROPERTY
  274. << COMMAND_QMLSIGNAL
  275. << COMMAND_QMLATTACHEDSIGNAL
  276. << COMMAND_QMLMETHOD
  277. << COMMAND_QMLATTACHEDMETHOD
  278. << COMMAND_QMLBASICTYPE
  279. << COMMAND_QMLMODULE;
  280. }
  281. return topicCommands_;
  282. }
  283. /*!
  284. Process the topic \a command found in the \a doc with argument \a arg.
  285. */
  286. Node* CppCodeParser::processTopicCommand(const Doc& doc,
  287. const QString& command,
  288. const ArgLocPair& arg)
  289. {
  290. ExtraFuncData extra;
  291. if (command == COMMAND_FN) {
  292. QStringList parentPath;
  293. FunctionNode *func = 0;
  294. FunctionNode *clone = 0;
  295. if (!makeFunctionNode(arg.first, &parentPath, &clone, extra) &&
  296. !makeFunctionNode("void " + arg.first, &parentPath, &clone, extra)) {
  297. doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN));
  298. }
  299. else {
  300. func = qdb_->findNodeInOpenNamespace(parentPath, clone);
  301. /*
  302. Search the root namespace if no match was found.
  303. */
  304. if (func == 0) {
  305. func = qdb_->findFunctionNode(parentPath, clone);
  306. }
  307. if (func == 0) {
  308. if (parentPath.isEmpty() && !lastPath_.isEmpty()) {
  309. func = qdb_->findFunctionNode(lastPath_, clone);
  310. }
  311. if (func == 0) {
  312. doc.location().warning(tr("Cannot find '%1' in '\\%2' %3")
  313. .arg(clone->name() + "(...)")
  314. .arg(COMMAND_FN)
  315. .arg(arg.first),
  316. tr("I cannot find any function of that name with the "
  317. "specified signature. Make sure that the signature "
  318. "is identical to the declaration, including 'const' "
  319. "qualifiers."));
  320. }
  321. else {
  322. doc.location().warning(tr("Missing '%1::' for '%2' in '\\%3'")
  323. .arg(lastPath_.join("::"))
  324. .arg(clone->name() + "()")
  325. .arg(COMMAND_FN));
  326. }
  327. }
  328. else {
  329. lastPath_ = parentPath;
  330. }
  331. if (func) {
  332. func->borrowParameterNames(clone);
  333. func->setParentPath(clone->parentPath());
  334. }
  335. delete clone;
  336. }
  337. return func;
  338. }
  339. else if (command == COMMAND_MACRO) {
  340. QStringList parentPath;
  341. FunctionNode *func = 0;
  342. extra.root = qdb_->treeRoot();
  343. extra.isMacro = true;
  344. if (makeFunctionNode(arg.first, &parentPath, &func, extra)) {
  345. if (!parentPath.isEmpty()) {
  346. doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO));
  347. delete func;
  348. func = 0;
  349. }
  350. else {
  351. func->setMetaness(FunctionNode::MacroWithParams);
  352. QList<Parameter> params = func->parameters();
  353. for (int i = 0; i < params.size(); ++i) {
  354. Parameter &param = params[i];
  355. if (param.name().isEmpty() && !param.leftType().isEmpty()
  356. && param.leftType() != "...")
  357. param = Parameter("", "", param.leftType());
  358. }
  359. func->setParameters(params);
  360. }
  361. return func;
  362. }
  363. else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg.first)) {
  364. func = new FunctionNode(qdb_->treeRoot(), arg.first);
  365. func->setAccess(Node::Public);
  366. func->setLocation(doc.startLocation());
  367. func->setMetaness(FunctionNode::MacroWithoutParams);
  368. }
  369. else {
  370. doc.location().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO));
  371. }
  372. return func;
  373. }
  374. else if (nodeTypeMap.contains(command)) {
  375. /*
  376. We should only get in here if the command refers to
  377. something that can appear in a C++ namespace,
  378. i.e. a class, another namespace, an enum, a typedef,
  379. a property or a variable. I think these are handled
  380. this way to allow the writer to refer to the entity
  381. without including the namespace qualifier.
  382. */
  383. Node::Type type = nodeTypeMap[command];
  384. Node::SubType subtype = Node::NoSubType;
  385. if (type == Node::Document)
  386. subtype = Node::QmlClass;
  387. QStringList paths = arg.first.split(QLatin1Char(' '));
  388. QStringList path = paths[0].split("::");
  389. Node *node = 0;
  390. /*
  391. If the command refers to something that can be in a
  392. C++ namespace, search for it first in all the known
  393. C++ namespaces.
  394. */
  395. node = qdb_->findNodeInOpenNamespace(path, type, subtype);
  396. /*
  397. If the node was not found in a C++ namespace, search
  398. for it in the root namespace.
  399. */
  400. if (node == 0) {
  401. node = qdb_->findNodeByNameAndType(path, type, subtype);
  402. }
  403. if (node == 0) {
  404. doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
  405. .arg(arg.first).arg(command));
  406. lastPath_ = path;
  407. }
  408. else if (node->isInnerNode()) {
  409. /*
  410. This treets a class as a namespace.
  411. */
  412. if (path.size() > 1) {
  413. path.pop_back();
  414. QString ns = path.join("::");
  415. qdb_->insertOpenNamespace(ns);
  416. }
  417. }
  418. return node;
  419. }
  420. else if (command == COMMAND_EXAMPLE) {
  421. if (Config::generateExamples) {
  422. ExampleNode* en = new ExampleNode(qdb_->treeRoot(), arg.first);
  423. en->setLocation(doc.startLocation());
  424. createExampleFileNodes(en);
  425. return en;
  426. }
  427. }
  428. else if (command == COMMAND_EXTERNALPAGE) {
  429. DocNode* dn = new DocNode(qdb_->treeRoot(), arg.first, Node::ExternalPage, Node::ArticlePage);
  430. dn->setLocation(doc.startLocation());
  431. return dn;
  432. }
  433. else if (command == COMMAND_FILE) {
  434. DocNode* dn = new DocNode(qdb_->treeRoot(), arg.first, Node::File, Node::NoPageType);
  435. dn->setLocation(doc.startLocation());
  436. return dn;
  437. }
  438. else if (command == COMMAND_GROUP) {
  439. DocNode* dn = qdb_->addGroup(arg.first);
  440. dn->setLocation(doc.startLocation());
  441. return dn;
  442. }
  443. else if (command == COMMAND_HEADERFILE) {
  444. DocNode* dn = new DocNode(qdb_->treeRoot(), arg.first, Node::HeaderFile, Node::ApiPage);
  445. dn->setLocation(doc.startLocation());
  446. return dn;
  447. }
  448. else if (command == COMMAND_MODULE) {
  449. DocNode* dn = qdb_->addModule(arg.first);
  450. dn->setLocation(doc.startLocation());
  451. return dn;
  452. }
  453. else if (command == COMMAND_QMLMODULE) {
  454. DocNode* dn = qdb_->addQmlModule(arg.first);
  455. dn->setLocation(doc.startLocation());
  456. return dn;
  457. }
  458. else if (command == COMMAND_PAGE) {
  459. Node::PageType ptype = Node::ArticlePage;
  460. QStringList args = arg.first.split(QLatin1Char(' '));
  461. if (args.size() > 1) {
  462. QString t = args[1].toLower();
  463. if (t == "howto")
  464. ptype = Node::HowToPage;
  465. else if (t == "api")
  466. ptype = Node::ApiPage;
  467. else if (t == "example")
  468. ptype = Node::ExamplePage;
  469. else if (t == "overview")
  470. ptype = Node::OverviewPage;
  471. else if (t == "tutorial")
  472. ptype = Node::TutorialPage;
  473. else if (t == "faq")
  474. ptype = Node::FAQPage;
  475. else if (t == "ditamap")
  476. ptype = Node::DitaMapPage;
  477. }
  478. /*
  479. Search for a node with the same name. If there is one,
  480. then there is a collision, so create a collision node
  481. and make the existing node a child of the collision
  482. node, and then create the new Page node and make
  483. it a child of the collision node as well. Return the
  484. collision node.
  485. If there is no collision, just create a new Page
  486. node and return that one.
  487. */
  488. NameCollisionNode* ncn = qdb_->checkForCollision(args[0]);
  489. DocNode* dn = 0;
  490. if (ptype == Node::DitaMapPage)
  491. dn = new DitaMapNode(qdb_->treeRoot(), args[0]);
  492. else
  493. dn = new DocNode(qdb_->treeRoot(), args[0], Node::Page, ptype);
  494. dn->setLocation(doc.startLocation());
  495. if (ncn) {
  496. ncn->addCollision(dn);
  497. }
  498. return dn;
  499. }
  500. else if (command == COMMAND_DITAMAP) {
  501. DocNode* dn = new DitaMapNode(qdb_->treeRoot(), arg.first);
  502. dn->setLocation(doc.startLocation());
  503. return dn;
  504. }
  505. else if ((command == COMMAND_QMLCLASS) || (command == COMMAND_QMLTYPE)) {
  506. if (command == COMMAND_QMLCLASS)
  507. doc.startLocation().warning(tr("\\qmlclass is deprecated; use \\qmltype instead"));
  508. ClassNode* classNode = 0;
  509. QStringList names = arg.first.split(QLatin1Char(' '));
  510. if (names.size() > 1) {
  511. if (names[1] != "0")
  512. doc.startLocation().warning(tr("\\qmltype no longer has a 2nd argument; "
  513. "use '\\instantiates <class>' in \\qmltype "
  514. "comments instead"));
  515. else
  516. doc.startLocation().warning(tr("The 0 arg is no longer used for indicating "
  517. "that the QML type does not instantiate a "
  518. "C++ class"));
  519. /*
  520. If the second argument of the \\qmlclass command
  521. is 0 we should ignore the C++ class. The second
  522. argument should only be 0 when you are documenting
  523. QML in a .qdoc file.
  524. */
  525. if (names[1] != "0")
  526. classNode = qdb_->findClassNode(names[1].split("::"));
  527. }
  528. /*
  529. Search for a node with the same name. If there is one,
  530. then there is a collision, so create a collision node
  531. and make the existing node a child of the collision
  532. node, and then create the new QML class node and make
  533. it a child of the collision node as well. Return the
  534. collision node.
  535. If there is no collision, just create a new QML class
  536. node and return that one.
  537. */
  538. NameCollisionNode* ncn = qdb_->checkForCollision(names[0]);
  539. QmlClassNode* qcn = new QmlClassNode(qdb_->treeRoot(), names[0]);
  540. qcn->setClassNode(classNode);
  541. qcn->setLocation(doc.startLocation());
  542. #if 0
  543. // to be removed if \qmltype and \instantiates work ok
  544. if (isParsingCpp() || isParsingQdoc()) {
  545. qcn->requireCppClass();
  546. if (names.size() < 2) {
  547. QString msg = "C++ class name not specified for class documented as "
  548. "QML type: '\\qmlclass " + arg.first + " <class name>'";
  549. doc.startLocation().warning(tr(msg.toLatin1().data()));
  550. }
  551. else if (!classNode) {
  552. QString msg = "C++ class not found in any .h file for class documented "
  553. "as QML type: '\\qmlclass " + arg.first + "'";
  554. doc.startLocation().warning(tr(msg.toLatin1().data()));
  555. }
  556. }
  557. #endif
  558. if (ncn)
  559. ncn->addCollision(qcn);
  560. return qcn;
  561. }
  562. else if (command == COMMAND_QMLBASICTYPE) {
  563. QmlBasicTypeNode* n = new QmlBasicTypeNode(qdb_->treeRoot(), arg.first);
  564. n->setLocation(doc.startLocation());
  565. return n;
  566. }
  567. else if ((command == COMMAND_QMLSIGNAL) ||
  568. (command == COMMAND_QMLMETHOD) ||
  569. (command == COMMAND_QMLATTACHEDSIGNAL) ||
  570. (command == COMMAND_QMLATTACHEDMETHOD)) {
  571. QString module;
  572. QString qmlType;
  573. QString type;
  574. if (splitQmlMethodArg(arg.first,type,module,qmlType)) {
  575. QmlClassNode* qmlClass = qdb_->findQmlType(module,qmlType);
  576. if (qmlClass) {
  577. bool attached = false;
  578. Node::Type nodeType = Node::QmlMethod;
  579. if (command == COMMAND_QMLSIGNAL)
  580. nodeType = Node::QmlSignal;
  581. else if (command == COMMAND_QMLATTACHEDSIGNAL) {
  582. nodeType = Node::QmlSignal;
  583. attached = true;
  584. }
  585. else if (command == COMMAND_QMLMETHOD) {
  586. // do nothing
  587. }
  588. else if (command == COMMAND_QMLATTACHEDMETHOD)
  589. attached = true;
  590. else
  591. return 0; // never get here.
  592. FunctionNode* fn = makeFunctionNode(doc,
  593. arg.first,
  594. qmlClass,
  595. nodeType,
  596. attached,
  597. command);
  598. if (fn)
  599. fn->setLocation(doc.startLocation());
  600. return fn;
  601. }
  602. }
  603. }
  604. return 0;
  605. }
  606. /*!
  607. A QML property group argument has the form...
  608. <QML-module>::<QML-type>::<name>
  609. This function splits the argument into those parts.
  610. A <QML-module> is the QML equivalent of a C++ namespace.
  611. So this function splits \a arg on "::" and stores the
  612. parts in \a module, \a qmlType, and \a name, and returns
  613. true. If any part is not found, a qdoc warning is emitted
  614. and false is returned.
  615. */
  616. bool CppCodeParser::splitQmlPropertyGroupArg(const QString& arg,
  617. QString& module,
  618. QString& qmlType,
  619. QString& name)
  620. {
  621. QStringList colonSplit = arg.split("::");
  622. if (colonSplit.size() == 3) {
  623. module = colonSplit[0];
  624. qmlType = colonSplit[1];
  625. name = colonSplit[2];
  626. return true;
  627. }
  628. QString msg = "Unrecognizable QML module/component qualifier for " + arg;
  629. location().warning(tr(msg.toLatin1().data()));
  630. return false;
  631. }
  632. /*!
  633. A QML property argument has the form...
  634. <type> <QML-type>::<name>
  635. <type> <QML-module>::<QML-type>::<name>
  636. This function splits the argument into one of those
  637. two forms. The three part form is the old form, which
  638. was used before the creation of Qt Quick 2 and Qt
  639. Components. A <QML-module> is the QML equivalent of a
  640. C++ namespace. So this function splits \a arg on "::"
  641. and stores the parts in \a type, \a module, \a qmlType,
  642. and \a name, and returns \c true. If any part other than
  643. \a module is not found, a qdoc warning is emitted and
  644. false is returned.
  645. \note The two QML types \e{Component} and \e{QtObject}
  646. never have a module qualifier.
  647. */
  648. bool CppCodeParser::splitQmlPropertyArg(const QString& arg,
  649. QString& type,
  650. QString& module,
  651. QString& qmlType,
  652. QString& name)
  653. {
  654. QStringList blankSplit = arg.split(QLatin1Char(' '));
  655. if (blankSplit.size() > 1) {
  656. type = blankSplit[0];
  657. QStringList colonSplit(blankSplit[1].split("::"));
  658. if (colonSplit.size() == 3) {
  659. module = colonSplit[0];
  660. qmlType = colonSplit[1];
  661. name = colonSplit[2];
  662. return true;
  663. }
  664. if (colonSplit.size() == 2) {
  665. module.clear();
  666. qmlType = colonSplit[0];
  667. name = colonSplit[1];
  668. return true;
  669. }
  670. QString msg = "Unrecognizable QML module/component qualifier for " + arg;
  671. location().warning(tr(msg.toLatin1().data()));
  672. }
  673. else {
  674. QString msg = "Missing property type for " + arg;
  675. location().warning(tr(msg.toLatin1().data()));
  676. }
  677. return false;
  678. }
  679. /*!
  680. A QML signal or method argument has the form...
  681. <type> <QML-type>::<name>(<param>, <param>, ...)
  682. <type> <QML-module>::<QML-type>::<name>(<param>, <param>, ...)
  683. This function splits the argument into one of those two
  684. forms, sets \a module, \a qmlType, and \a name, and returns
  685. true. If the argument doesn't match either form, an error
  686. message is emitted and false is returned.
  687. \note The two QML types \e{Component} and \e{QtObject} never
  688. have a module qualifier.
  689. */
  690. bool CppCodeParser::splitQmlMethodArg(const QString& arg,
  691. QString& type,
  692. QString& module,
  693. QString& qmlType)
  694. {
  695. QStringList colonSplit(arg.split("::"));
  696. if (colonSplit.size() > 1) {
  697. QStringList blankSplit = colonSplit[0].split(QLatin1Char(' '));
  698. if (blankSplit.size() > 1) {
  699. type = blankSplit[0];
  700. if (colonSplit.size() > 2) {
  701. module = blankSplit[1];
  702. qmlType = colonSplit[1];
  703. }
  704. else {
  705. module.clear();
  706. qmlType = blankSplit[1];
  707. }
  708. }
  709. else {
  710. type.clear();
  711. if (colonSplit.size() > 2) {
  712. module = colonSplit[0];
  713. qmlType = colonSplit[1];
  714. }
  715. else {
  716. module.clear();
  717. qmlType = colonSplit[0];
  718. }
  719. }
  720. return true;
  721. }
  722. QString msg = "Unrecognizable QML module/component qualifier for " + arg;
  723. location().warning(tr(msg.toLatin1().data()));
  724. return false;
  725. }
  726. /*!
  727. Process the topic \a command group found in the \a doc with arguments \a args.
  728. Currently, this function is called only for \e{qmlproperty}
  729. and \e{qmlattachedproperty}.
  730. */
  731. void CppCodeParser::processQmlProperties(const Doc& doc, NodeList& nodes, DocList& docs)
  732. {
  733. QString arg;
  734. QString type;
  735. QString topic;
  736. QString module;
  737. QString qmlType;
  738. QString property;
  739. QmlPropertyNode* qpn = 0;
  740. QmlClassNode* qmlClass = 0;
  741. QmlPropertyGroupNode* qpgn = 0;
  742. Topic qmlPropertyGroupTopic;
  743. const TopicList& topics = doc.topicsUsed();
  744. for (int i=0; i<topics.size(); ++i) {
  745. if (topics.at(i).topic == COMMAND_QMLPROPERTYGROUP) {
  746. qmlPropertyGroupTopic = topics.at(i);
  747. break;
  748. }
  749. }
  750. if (qmlPropertyGroupTopic.isEmpty() && topics.size() > 1) {
  751. qmlPropertyGroupTopic = topics.at(0);
  752. qmlPropertyGroupTopic.topic = COMMAND_QMLPROPERTYGROUP;
  753. arg = qmlPropertyGroupTopic.args;
  754. if (splitQmlPropertyArg(arg, type, module, qmlType, property)) {
  755. int i = property.indexOf('.');
  756. if (i != -1) {
  757. property = property.left(i);
  758. qmlPropertyGroupTopic.args = module + "::" + qmlType + "::" + property;
  759. doc.location().warning(tr("No QML property group command found; using \\%1 %2")
  760. .arg(COMMAND_QMLPROPERTYGROUP).arg(qmlPropertyGroupTopic.args));
  761. }
  762. else {
  763. /*
  764. Assumption: No '.' in the property name
  765. means there is no property group.
  766. */
  767. qmlPropertyGroupTopic.clear();
  768. }
  769. }
  770. }
  771. if (!qmlPropertyGroupTopic.isEmpty()) {
  772. arg = qmlPropertyGroupTopic.args;
  773. if (splitQmlPropertyGroupArg(arg, module, qmlType, property)) {
  774. qmlClass = qdb_->findQmlType(module, qmlType);
  775. if (qmlClass) {
  776. qpgn = new QmlPropertyGroupNode(qmlClass, property);
  777. qpgn->setLocation(doc.startLocation());
  778. nodes.append(qpgn);
  779. docs.append(doc);
  780. }
  781. }
  782. }
  783. for (int i=0; i<topics.size(); ++i) {
  784. if (topics.at(i).topic == COMMAND_QMLPROPERTYGROUP) {
  785. continue;
  786. }
  787. topic = topics.at(i).topic;
  788. arg = topics.at(i).args;
  789. if ((topic == COMMAND_QMLPROPERTY) || (topic == COMMAND_QMLATTACHEDPROPERTY)) {
  790. bool attached = (topic == COMMAND_QMLATTACHEDPROPERTY);
  791. if (splitQmlPropertyArg(arg, type, module, qmlType, property)) {
  792. qmlClass = qdb_->findQmlType(module, qmlType);
  793. if (qmlClass) {
  794. if (qmlClass->hasQmlProperty(property) != 0) {
  795. QString msg = tr("QML property documented multiple times: '%1'").arg(arg);
  796. doc.startLocation().warning(msg);
  797. }
  798. else if (qpgn) {
  799. qpn = new QmlPropertyNode(qpgn, property, type, attached);
  800. qpn->setLocation(doc.startLocation());
  801. }
  802. else {
  803. qpn = new QmlPropertyNode(qmlClass, property, type, attached);
  804. qpn->setLocation(doc.startLocation());
  805. nodes.append(qpn);
  806. docs.append(doc);
  807. }
  808. }
  809. }
  810. }
  811. }
  812. }
  813. static QSet<QString> otherMetaCommands_;
  814. /*!
  815. Returns the set of strings representing the common metacommands
  816. plus some other metacommands.
  817. */
  818. const QSet<QString>& CppCodeParser::otherMetaCommands()
  819. {
  820. if (otherMetaCommands_.isEmpty()) {
  821. otherMetaCommands_ = commonMetaCommands();
  822. otherMetaCommands_ << COMMAND_INHEADERFILE
  823. << COMMAND_OVERLOAD
  824. << COMMAND_REIMP
  825. << COMMAND_RELATES
  826. << COMMAND_CONTENTSPAGE
  827. << COMMAND_NEXTPAGE
  828. << COMMAND_PREVIOUSPAGE
  829. << COMMAND_INDEXPAGE
  830. << COMMAND_STARTPAGE
  831. << COMMAND_QMLINHERITS
  832. << COMMAND_QMLINSTANTIATES
  833. << COMMAND_QMLDEFAULT
  834. << COMMAND_QMLREADONLY
  835. << COMMAND_QMLABSTRACT;
  836. }
  837. return otherMetaCommands_;
  838. }
  839. /*!
  840. Process the metacommand \a command in the context of the
  841. \a node associated with the topic command and the \a doc.
  842. \a arg is the argument to the metacommand.
  843. */
  844. void CppCodeParser::processOtherMetaCommand(const Doc& doc,
  845. const QString& command,
  846. const ArgLocPair& argLocPair,
  847. Node *node)
  848. {
  849. QString arg = argLocPair.first;
  850. if (command == COMMAND_INHEADERFILE) {
  851. if (node != 0 && node->isInnerNode()) {
  852. ((InnerNode *) node)->addInclude(arg);
  853. }
  854. else {
  855. doc.location().warning(tr("Ignored '\\%1'")
  856. .arg(COMMAND_INHEADERFILE));
  857. }
  858. }
  859. else if (command == COMMAND_OVERLOAD) {
  860. if (node != 0 && node->type() == Node::Function) {
  861. ((FunctionNode *) node)->setOverload(true);
  862. }
  863. else {
  864. doc.location().warning(tr("Ignored '\\%1'")
  865. .arg(COMMAND_OVERLOAD));
  866. }
  867. }
  868. else if (command == COMMAND_REIMP) {
  869. if (node != 0 && node->parent() && !node->parent()->isInternal()) {
  870. if (node->type() == Node::Function) {
  871. FunctionNode *func = (FunctionNode *) node;
  872. const FunctionNode *from = func->reimplementedFrom();
  873. if (from == 0) {
  874. doc.location().warning(tr("Cannot find base function for '\\%1' in %2()")
  875. .arg(COMMAND_REIMP).arg(node->name()),
  876. tr("The function either doesn't exist in any "
  877. "base class with the same signature or it "
  878. "exists but isn't virtual."));
  879. }
  880. /*
  881. Ideally, we would enable this check to warn whenever
  882. \reimp is used incorrectly, and only make the node
  883. internal if the function is a reimplementation of
  884. another function in a base class.
  885. */
  886. else if (from->access() == Node::Private
  887. || from->parent()->access() == Node::Private) {
  888. doc.location().warning(tr("'\\%1' in %2() should be '\\internal' "
  889. "because its base function is private "
  890. "or internal").arg(COMMAND_REIMP).arg(node->name()));
  891. }
  892. func->setReimp(true);
  893. }
  894. else {
  895. doc.location().warning(tr("Ignored '\\%1' in %2")
  896. .arg(COMMAND_REIMP)
  897. .arg(node->name()));
  898. }
  899. }
  900. }
  901. else if (command == COMMAND_RELATES) {
  902. /*
  903. Find the node that this node relates to.
  904. */
  905. Node* n = 0;
  906. if (arg.startsWith(QLatin1Char('<')) || arg.startsWith('"')) {
  907. /*
  908. It should be a header file, I think.
  909. */
  910. n = qdb_->findNodeByNameAndType(QStringList(arg), Node::Document, Node::NoSubType);
  911. }
  912. else {
  913. /*
  914. If it wasn't a file, it should be either a class or a namespace.
  915. */
  916. QStringList newPath = arg.split("::");
  917. n = qdb_->findClassNode(newPath);
  918. if (!n)
  919. n = qdb_->findNamespaceNode(newPath);
  920. }
  921. if (!n) {
  922. /*
  923. Didn't ind it. Error...
  924. */
  925. doc.location().warning(tr("Cannot find '%1' in '\\%2'").arg(arg).arg(COMMAND_RELATES));
  926. }
  927. else {
  928. /*
  929. Found it. This node relates to it.
  930. */
  931. node->setRelates(static_cast<InnerNode*>(n));
  932. }
  933. }
  934. else if (command == COMMAND_CONTENTSPAGE) {
  935. setLink(node, Node::ContentsLink, arg);
  936. }
  937. else if (command == COMMAND_NEXTPAGE) {
  938. setLink(node, Node::NextLink, arg);
  939. }
  940. else if (command == COMMAND_PREVIOUSPAGE) {
  941. setLink(node, Node::PreviousLink, arg);
  942. }
  943. else if (command == COMMAND_INDEXPAGE) {
  944. setLink(node, Node::IndexLink, arg);
  945. }
  946. else if (command == COMMAND_STARTPAGE) {
  947. setLink(node, Node::StartLink, arg);
  948. }
  949. else if (command == COMMAND_QMLINHERITS) {
  950. if (node->name() == arg)
  951. doc.location().warning(tr("%1 tries to inherit itself").arg(arg));
  952. else if (node->subType() == Node::QmlClass) {
  953. QmlClassNode *qmlClass = static_cast<QmlClassNode*>(node);
  954. qmlClass->setQmlBaseName(arg);
  955. QmlClassNode::addInheritedBy(arg,node);
  956. }
  957. }
  958. else if (command == COMMAND_QMLINSTANTIATES) {
  959. if ((node->type() == Node::Document) && (node->subType() == Node::QmlClass)) {
  960. ClassNode* classNode = qdb_->findClassNode(arg.split("::"));
  961. if (classNode)
  962. node->setClassNode(classNode);
  963. else
  964. doc.location().warning(tr("C++ class %1 not found: \\instantiates %1").arg(arg));
  965. }
  966. else
  967. doc.location().warning(tr("\\instantiates is only allowed in \\qmltype"));
  968. }
  969. else if (command == COMMAND_QMLDEFAULT) {
  970. if (node->type() == Node::QmlProperty) {
  971. QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
  972. qpn->setDefault();
  973. }
  974. else if (node->type() == Node::QmlPropertyGroup) {
  975. QmlPropertyGroupNode* qpgn = static_cast<QmlPropertyGroupNode*>(node);
  976. NodeList::ConstIterator p = qpgn->childNodes().constBegin();
  977. while (p != qpgn->childNodes().constEnd()) {
  978. if ((*p)->type() == Node::QmlProperty) {
  979. QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(*p);
  980. qpn->setDefault();
  981. }
  982. ++p;
  983. }
  984. }
  985. }
  986. else if (command == COMMAND_QMLREADONLY) {
  987. if (node->type() == Node::QmlProperty) {
  988. QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node);
  989. qpn->setReadOnly(1);
  990. }
  991. else if (node->type() == Node::QmlPropertyGroup) {
  992. QmlPropertyGroupNode* qpgn = static_cast<QmlPropertyGroupNode*>(node);
  993. NodeList::ConstIterator p = qpgn->childNodes().constBegin();
  994. while (p != qpgn->childNodes().constEnd()) {
  995. if ((*p)->type() == Node::QmlProperty) {
  996. QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(*p);
  997. qpn->setReadOnly(1);
  998. }
  999. ++p;
  1000. }
  1001. }
  1002. }
  1003. else if (command == COMMAND_QMLABSTRACT) {
  1004. if ((node->type() == Node::Document) && (node->subType() == Node::QmlClass)) {
  1005. node->setAbstract(true);
  1006. }
  1007. }
  1008. else {
  1009. processCommonMetaCommand(doc.location(),command,argLocPair,node);
  1010. }
  1011. }
  1012. /*!
  1013. The topic command has been processed resulting in the \a doc
  1014. and \a node passed in here. Process the other meta commands,
  1015. which are found in \a doc, in the context of the topic \a node.
  1016. */
  1017. void CppCodeParser::processOtherMetaCommands(const Doc& doc, Node *node)
  1018. {
  1019. const QSet<QString> metaCommands = doc.metaCommandsUsed();
  1020. QSet<QString>::ConstIterator cmd = metaCommands.constBegin();
  1021. while (cmd != metaCommands.constEnd()) {
  1022. ArgList args = doc.metaCommandArgs(*cmd);
  1023. ArgList::ConstIterator arg = args.constBegin();
  1024. while (arg != args.constEnd()) {
  1025. processOtherMetaCommand(doc, *cmd, *arg, node);
  1026. ++arg;
  1027. }
  1028. ++cmd;
  1029. }
  1030. }
  1031. /*!
  1032. Resets the C++ code parser to its default initialized state.
  1033. */
  1034. void CppCodeParser::reset()
  1035. {
  1036. tokenizer = 0;
  1037. tok = 0;
  1038. access = Node::Public;
  1039. metaness = FunctionNode::Plain;
  1040. lastPath_.clear();
  1041. moduleName.clear();
  1042. }
  1043. /*!
  1044. Get the next token from the file being parsed and store it
  1045. in the token variable.
  1046. */
  1047. void CppCodeParser::readToken()
  1048. {
  1049. tok = tokenizer->getToken();
  1050. }
  1051. /*!
  1052. Return the current location in the file being parsed,
  1053. i.e. the file name, line number, and column number.
  1054. */
  1055. const Location& CppCodeParser::location()
  1056. {
  1057. return tokenizer->location();
  1058. }
  1059. /*!
  1060. Return the previous string read from the file being parsed.
  1061. */
  1062. QString CppCodeParser::previousLexeme()
  1063. {
  1064. return tokenizer->previousLexeme();
  1065. }
  1066. /*!
  1067. Return the current string string from the file being parsed.
  1068. */
  1069. QString CppCodeParser::lexeme()
  1070. {
  1071. return tokenizer->lexeme();
  1072. }
  1073. bool CppCodeParser::match(int target)
  1074. {
  1075. if (tok == target) {
  1076. readToken();
  1077. return true;
  1078. }
  1079. else
  1080. return false;
  1081. }
  1082. /*!
  1083. Skip to \a target. If \a target is found before the end
  1084. of input, return true. Otherwise return false.
  1085. */
  1086. bool CppCodeParser::skipTo(int target)
  1087. {
  1088. while ((tok != Tok_Eoi) && (tok != target))
  1089. readToken();
  1090. return (tok == target ? true : false);
  1091. }
  1092. /*!
  1093. If the current token is one of the keyword thingees that
  1094. are used in Qt, skip over it to the next token and return
  1095. true. Otherwise just return false without reading the
  1096. next token.
  1097. */
  1098. bool CppCodeParser::matchCompat()
  1099. {
  1100. switch (tok) {
  1101. case Tok_QT_COMPAT:
  1102. case Tok_QT_COMPAT_CONSTRUCTOR:
  1103. case Tok_QT_DEPRECATED:
  1104. case Tok_QT_MOC_COMPAT:
  1105. case Tok_QT3_SUPPORT:
  1106. case Tok_QT3_SUPPORT_CONSTRUCTOR:
  1107. case Tok_QT3_MOC_SUPPORT:
  1108. readToken();
  1109. return true;
  1110. default:
  1111. return false;
  1112. }
  1113. }
  1114. bool CppCodeParser::matchModuleQualifier(QString& name)
  1115. {
  1116. bool matches = (lexeme() == QString('.'));
  1117. if (matches) {
  1118. do {
  1119. name += lexeme();
  1120. readToken();
  1121. } while ((tok == Tok_Ident) || (lexeme() == QString('.')));
  1122. }
  1123. return matches;
  1124. }
  1125. bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
  1126. {
  1127. bool matches = (tok == Tok_LeftAngle);
  1128. if (matches) {
  1129. int leftAngleDepth = 0;
  1130. int parenAndBraceDepth = 0;
  1131. do {
  1132. if (tok == Tok_LeftAngle) {
  1133. leftAngleDepth++;
  1134. }
  1135. else if (tok == Tok_RightAngle) {
  1136. leftAngleDepth--;
  1137. }
  1138. else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) {
  1139. ++parenAndBraceDepth;
  1140. }
  1141. else if (tok == Tok_RightParen || tok == Tok_RightBrace) {
  1142. if (--parenAndBraceDepth < 0)
  1143. return false;
  1144. }
  1145. if (dataType != 0)
  1146. dataType->append(lexeme());
  1147. readToken();
  1148. } while (leftAngleDepth > 0 && tok != Tok_Eoi);
  1149. }
  1150. return matches;
  1151. }
  1152. /*
  1153. This function is no longer used.
  1154. */
  1155. bool CppCodeParser::matchTemplateHeader()
  1156. {
  1157. readToken();
  1158. return matchTemplateAngles();
  1159. }
  1160. bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
  1161. {
  1162. /*
  1163. This code is really hard to follow... sorry. The loop is there to match
  1164. Alpha::Beta::Gamma::...::Omega.
  1165. */
  1166. for (;;) {
  1167. bool virgin = true;
  1168. if (tok != Tok_Ident) {
  1169. /*
  1170. There is special processing for 'Foo::operator int()'
  1171. and such elsewhere. This is the only case where we
  1172. return something with a trailing gulbrandsen ('Foo::').
  1173. */
  1174. if (tok == Tok_operator)
  1175. return true;
  1176. /*
  1177. People may write 'const unsigned short' or
  1178. 'short unsigned const' or any other permutation.
  1179. */
  1180. while (match(Tok_const) || match(Tok_volatile))
  1181. dataType->append(previousLexeme());
  1182. while (match(Tok_signed) || match(Tok_unsigned) ||
  1183. match(Tok_short) || match(Tok_long) || match(Tok_int64)) {
  1184. dataType->append(previousLexeme());
  1185. virgin = false;
  1186. }
  1187. while (match(Tok_const) || match(Tok_volatile))
  1188. dataType->append(previousLexeme());
  1189. if (match(Tok_Tilde))
  1190. dataType->append(previousLexeme());
  1191. }
  1192. if (virgin) {
  1193. if (match(Tok_Ident)) {
  1194. /*
  1195. This is a hack until we replace this "parser"
  1196. with the real one used in Qt Creator.
  1197. */
  1198. if (!inMacroCommand_ && lexeme() == "(" &&
  1199. ((previousLexeme() == "QT_PREPEND_NAMESPACE") || (previousLexeme() == "NS"))) {
  1200. readToken();
  1201. readToken();
  1202. dataType->append(previousLexeme());
  1203. readToken();
  1204. }
  1205. else
  1206. dataType->append(previousLexeme());
  1207. }
  1208. else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||
  1209. match(Tok_double) || match(Tok_Ellipsis))
  1210. dataType->append(previousLexeme());
  1211. else {
  1212. return false;
  1213. }
  1214. }
  1215. else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
  1216. dataType->append(previousLexeme());
  1217. }
  1218. matchTemplateAngles(dataType);
  1219. while (match(Tok_const) || match(Tok_volatile))
  1220. dataType->append(previousLexeme());
  1221. if (match(Tok_Gulbrandsen))
  1222. dataType->append(previousLexeme());
  1223. else
  1224. break;
  1225. }
  1226. while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) ||
  1227. match(Tok_Caret))
  1228. dataType->append(previousLexeme());
  1229. if (match(Tok_LeftParenAster)) {
  1230. /*
  1231. A function pointer. This would be rather hard to handle without a
  1232. tokenizer hack, because a type can be followed with a left parenthesis
  1233. in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
  1234. as a single token.
  1235. */
  1236. dataType->append(previousLexeme());
  1237. dataType->appendHotspot();
  1238. if (var != 0 && match(Tok_Ident))
  1239. *var = previousLexeme();
  1240. if (!match(Tok_RightParen) || tok != Tok_LeftParen) {
  1241. return false;
  1242. }
  1243. dataType->append(previousLexeme());
  1244. int parenDepth0 = tokenizer->parenDepth();
  1245. while (tokenizer->parenDepth() >= parenDepth0 && tok != Tok_Eoi) {
  1246. dataType->append(lexeme());
  1247. readToken();
  1248. }
  1249. if (match(Tok_RightParen))
  1250. dataType->append(previousLexeme());
  1251. }
  1252. else {
  1253. /*
  1254. The common case: Look for an optional identifier, then for
  1255. some array brackets.
  1256. */
  1257. dataType->appendHotspot();
  1258. if (var != 0) {
  1259. if (match(Tok_Ident)) {
  1260. *var = previousLexeme();
  1261. }
  1262. else if (match(Tok_Comment)) {
  1263. /*
  1264. A neat hack: Commented-out parameter names are
  1265. recognized by qdoc. It's impossible to illustrate
  1266. here inside a C-style comment, because it requires
  1267. an asterslash. It's also impossible to illustrate
  1268. inside a C++-style comment, because the explanation
  1269. does not fit on one line.
  1270. */
  1271. if (varComment.exactMatch(previousLexeme()))
  1272. *var = varComment.cap(1);
  1273. }
  1274. }
  1275. if (tok == Tok_LeftBracket) {
  1276. int bracketDepth0 = tokenizer->bracketDepth();
  1277. while ((tokenizer->bracketDepth() >= bracketDepth0 &&
  1278. tok != Tok_Eoi) ||
  1279. tok == Tok_RightBracket) {
  1280. dataType->append(lexeme());
  1281. readToken();
  1282. }
  1283. }
  1284. }
  1285. return true;
  1286. }
  1287. bool CppCodeParser::matchParameter(FunctionNode *func)
  1288. {
  1289. CodeChunk dataType;
  1290. QString name;
  1291. CodeChunk defaultValue;
  1292. if (!matchDataType(&dataType, &name)) {
  1293. return false;
  1294. }
  1295. match(Tok_Comment);
  1296. if (match(Tok_Equal)) {
  1297. int parenDepth0 = tokenizer->parenDepth();
  1298. while (tokenizer->parenDepth() >= parenDepth0 &&
  1299. (tok != Tok_Comma ||
  1300. tokenizer->parenDepth() > parenDepth0) &&
  1301. tok != Tok_Eoi) {
  1302. defaultValue.append(lexeme());
  1303. readToken();
  1304. }
  1305. }
  1306. func->addParameter(Parameter(dataType.toString(), "", name, defaultValue.toString())); // ###
  1307. return true;
  1308. }
  1309. bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
  1310. QStringList *parentPathPtr,
  1311. FunctionNode **funcPtr,
  1312. const QString &templateStuff,
  1313. ExtraFuncData& extra)
  1314. {
  1315. CodeChunk returnType;
  1316. QStringList parentPath;
  1317. QString name;
  1318. bool compat = false;
  1319. if (match(Tok_friend)) {
  1320. return false;
  1321. }
  1322. match(Tok_explicit);
  1323. if (matchCompat())
  1324. compat = true;
  1325. bool sta = false;
  1326. if (match(Tok_static)) {
  1327. sta = true;
  1328. if (matchCompat())
  1329. compat = true;
  1330. }
  1331. FunctionNode::Virtualness vir = FunctionNode::NonVirtual;
  1332. if (match(Tok_virtual)) {
  1333. vir = FunctionNode::ImpureVirtual;
  1334. if (matchCompat())
  1335. compat = true;
  1336. }
  1337. if (!matchDataType(&returnType)) {
  1338. if (tokenizer->parsingFnOrMacro()
  1339. && (match(Tok