source: trunk/tools/qdoc3/cppcodeparser.cpp@ 441

Last change on this file since 441 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 65.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 cppcodeparser.cpp
44*/
45
46#include <QtCore>
47#include <qfile.h>
48
49#include <stdio.h>
50
51#include "codechunk.h"
52#include "config.h"
53#include "cppcodeparser.h"
54#include "tokenizer.h"
55#include "tree.h"
56
57QT_BEGIN_NAMESPACE
58
59/* qmake ignore Q_OBJECT */
60
61#define COMMAND_CLASS Doc::alias("class")
62#define COMMAND_CONTENTSPAGE Doc::alias("contentspage")
63#define COMMAND_ENUM Doc::alias("enum")
64#define COMMAND_EXAMPLE Doc::alias("example")
65#define COMMAND_EXTERNALPAGE Doc::alias("externalpage")
66#define COMMAND_FILE Doc::alias("file") // ### don't document
67#define COMMAND_FN Doc::alias("fn")
68#define COMMAND_GROUP Doc::alias("group")
69#define COMMAND_HEADERFILE Doc::alias("headerfile")
70#define COMMAND_INDEXPAGE Doc::alias("indexpage")
71#define COMMAND_INHEADERFILE Doc::alias("inheaderfile") // ### don't document
72#define COMMAND_MACRO Doc::alias("macro")
73#define COMMAND_MODULE Doc::alias("module") // ### don't document
74#define COMMAND_NAMESPACE Doc::alias("namespace")
75#define COMMAND_OVERLOAD Doc::alias("overload")
76#define COMMAND_NEXTPAGE Doc::alias("nextpage")
77#define COMMAND_PAGE Doc::alias("page")
78#define COMMAND_PREVIOUSPAGE Doc::alias("previouspage")
79#define COMMAND_PROPERTY Doc::alias("property")
80#define COMMAND_REIMP Doc::alias("reimp")
81#define COMMAND_RELATES Doc::alias("relates")
82#define COMMAND_SERVICE Doc::alias("service")
83#define COMMAND_STARTPAGE Doc::alias("startpage")
84#define COMMAND_TYPEDEF Doc::alias("typedef")
85#define COMMAND_VARIABLE Doc::alias("variable")
86
87#ifdef QDOC_QML
88#define COMMAND_QMLCLASS Doc::alias("qmlclass")
89#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty")
90#endif
91
92QStringList CppCodeParser::exampleFiles;
93QStringList CppCodeParser::exampleDirs;
94
95static void extractPageLinkAndDesc(const QString &arg,
96 QString *link,
97 QString *desc)
98{
99 QRegExp bracedRegExp("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?");
100
101 if (bracedRegExp.exactMatch(arg)) {
102 *link = bracedRegExp.cap(1);
103 *desc = bracedRegExp.cap(2);
104 if (desc->isEmpty())
105 *desc = *link;
106 }
107 else {
108 int spaceAt = arg.indexOf(" ");
109 if (arg.contains(".html") && spaceAt != -1) {
110 *link = arg.left(spaceAt).trimmed();
111 *desc = arg.mid(spaceAt).trimmed();
112 } else {
113 *link = arg;
114 *desc = arg;
115 }
116 }
117}
118
119static void setLink(Node *node, Node::LinkType linkType, const QString &arg)
120{
121 QString link;
122 QString desc;
123 extractPageLinkAndDesc(arg, &link, &desc);
124 node->setLink(linkType, link, desc);
125}
126
127/*
128 This is used for fuzzy matching only, which in turn is only used
129 for Qt Jambi.
130*/
131static QString cleanType(const QString &type, const Tree *tree)
132{
133 QString result = type;
134 result.replace("qlonglong", "long long");
135 result.replace("qulonglong", "unsigned long long");
136 result.replace("qreal", "double");
137 result.replace(QRegExp("\\bu(int|short|char|long)\\b"), "unsigned \\1");
138 result.replace("QRgb", "unsigned int");
139 result.replace(" >", ">");
140 result.remove(" const[]");
141 result.replace("QStringList<QString>", "QStringList");
142 result.replace("qint8", "char");
143 result.replace("qint16", "short");
144 result.replace("qint32", "int");
145 result.replace("qint64", "long long");
146 result.replace("quint8", "unsigned char");
147 result.replace("quint16", "unsigned short");
148 result.replace("quint32", "unsigned int");
149 result.replace("quint64", "unsigned long long");
150
151 if (result.contains("QFlags")) {
152 QRegExp regExp("QFlags<(((?:[^<>]+::)*)([^<>:]+))>");
153 int pos = 0;
154 while ((pos = result.indexOf(regExp, pos)) != -1) {
155 // we assume that the path for the associated enum
156 // is the same as for the flag typedef
157 QStringList path = regExp.cap(2).split("::",
158 QString::SkipEmptyParts);
159 const EnumNode *enume = static_cast<const EnumNode *>(
160 tree->findNode(QStringList(path) << regExp.cap(3),
161 Node::Enum));
162 if (enume && enume->flagsType())
163 result.replace(pos, regExp.matchedLength(),
164 (QStringList(path) << enume->flagsType()->name()).join("::"));
165 ++pos;
166 }
167 }
168 if (result.contains("::")) {
169 // remove needless (and needful) class prefixes
170 QRegExp regExp("[A-Za-z0-9_]+::");
171 result.replace(regExp, "");
172 }
173 return result;
174}
175
176/*!
177 The constructor initializes some regular expressions
178 and calls reset().
179 */
180CppCodeParser::CppCodeParser()
181 : varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
182{
183 reset(0);
184}
185
186/*!
187 The destructor is trivial.
188 */
189CppCodeParser::~CppCodeParser()
190{
191}
192
193void CppCodeParser::initializeParser(const Config &config)
194{
195 CodeParser::initializeParser(config);
196
197 nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
198 nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
199 nodeTypeMap.insert(COMMAND_SERVICE, Node::Class);
200 nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
201 nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
202 nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
203 nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);
204
205#ifdef QDOC_QML
206 // nodeTypeMap.insert(COMMAND_QMLCLASS, Node::Class);
207 nodeTypeMap.insert(COMMAND_QMLPROPERTY, Node::Property);
208#endif
209
210 exampleFiles = config.getStringList(CONFIG_EXAMPLES);
211 exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
212 QStringList exampleFilePatterns = config.getStringList(
213 CONFIG_EXAMPLES + Config::dot + CONFIG_FILEEXTENSIONS);
214
215 if (!exampleFilePatterns.isEmpty())
216 exampleNameFilter = exampleFilePatterns.join(" ");
217 else
218 exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
219}
220
221void CppCodeParser::terminateParser()
222{
223 nodeTypeMap.clear();
224 CodeParser::terminateParser();
225}
226
227QString CppCodeParser::language()
228{
229 return "Cpp";
230}
231
232QString CppCodeParser::headerFileNameFilter()
233{
234 return "*.ch *.h *.h++ *.hh *.hpp *.hxx";
235}
236
237QString CppCodeParser::sourceFileNameFilter()
238{
239 return "*.c++ *.cc *.cpp *.cxx";
240}
241
242/*!
243 Parse the C++ header file identified by \a filePath
244 and add the parsed contents to the big \a tree. The
245 \a location is used for reporting errors.
246 */
247void CppCodeParser::parseHeaderFile(const Location& location,
248 const QString& filePath,
249 Tree *tree)
250{
251 FILE *in = fopen(QFile::encodeName(filePath), "r");
252 if (!in) {
253 location.error(tr("Cannot open C++ header file '%1'").arg(filePath));
254 return;
255 }
256
257 reset(tree);
258 Location fileLocation(filePath);
259 Tokenizer fileTokenizer(fileLocation, in);
260 tokenizer = &fileTokenizer;
261 readToken();
262 matchDeclList(tree->root());
263 if (!fileTokenizer.version().isEmpty())
264 tree->setVersion(fileTokenizer.version());
265 fclose(in);
266
267 if (fileLocation.fileName() == "qiterator.h")
268 parseQiteratorDotH(location, filePath);
269}
270
271/*!
272 Get ready to parse the C++ cpp file identified by \a filePath
273 and add its parsed contents to the big \a tree. \a location is
274 used for reporting errors.
275
276 Call matchDocsAndStuff() to do all the parsing and tree building.
277 */
278void CppCodeParser::parseSourceFile(const Location& location,
279 const QString& filePath,
280 Tree *tree)
281{
282 FILE *in = fopen(QFile::encodeName(filePath), "r");
283 if (!in) {
284 location.error(tr("Cannot open C++ source file '%1'").arg(filePath));
285 return;
286 }
287
288 reset(tree);
289 Location fileLocation(filePath);
290 Tokenizer fileTokenizer(fileLocation, in);
291 tokenizer = &fileTokenizer;
292 readToken();
293 usedNamespaces.clear();
294 matchDocsAndStuff();
295 fclose(in);
296}
297
298void CppCodeParser::doneParsingHeaderFiles(Tree *tree)
299{
300 tree->resolveInheritance();
301
302 QMapIterator<QString, QString> i(sequentialIteratorClasses);
303 while (i.hasNext()) {
304 i.next();
305 instantiateIteratorMacro(i.key(),
306 i.value(),
307 sequentialIteratorDefinition,
308 tree);
309 }
310 i = mutableSequentialIteratorClasses;
311 while (i.hasNext()) {
312 i.next();
313 instantiateIteratorMacro(i.key(),
314 i.value(),
315 mutableSequentialIteratorDefinition,
316 tree);
317 }
318 i = associativeIteratorClasses;
319 while (i.hasNext()) {
320 i.next();
321 instantiateIteratorMacro(i.key(),
322 i.value(),
323 associativeIteratorDefinition,
324 tree);
325 }
326 i = mutableAssociativeIteratorClasses;
327 while (i.hasNext()) {
328 i.next();
329 instantiateIteratorMacro(i.key(),
330 i.value(),
331 mutableAssociativeIteratorDefinition,
332 tree);
333 }
334 sequentialIteratorDefinition.clear();
335 mutableSequentialIteratorDefinition.clear();
336 associativeIteratorDefinition.clear();
337 mutableAssociativeIteratorDefinition.clear();
338 sequentialIteratorClasses.clear();
339 mutableSequentialIteratorClasses.clear();
340 associativeIteratorClasses.clear();
341 mutableAssociativeIteratorClasses.clear();
342}
343
344void CppCodeParser::doneParsingSourceFiles(Tree *tree)
345{
346 tree->root()->makeUndocumentedChildrenInternal();
347 tree->root()->normalizeOverloads();
348 tree->fixInheritance();
349 tree->resolveProperties();
350}
351
352const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis,
353 Tree *tree,
354 Node *relative,
355 bool fuzzy)
356{
357 QStringList parentPath;
358 FunctionNode *clone;
359 FunctionNode *func = 0;
360 int flags = fuzzy ? int(Tree::SearchBaseClasses) : 0;
361
362 reset(tree);
363 if (makeFunctionNode(synopsis, &parentPath, &clone)) {
364 func = tree->findFunctionNode(parentPath, clone, relative, flags);
365
366 /*
367 This is necessary because Roberto's parser resolves typedefs.
368 */
369 if (!func && fuzzy) {
370 func = tre->findFunctionNode(parentPath +
371 QStringList(clone->name()),
372 relative,
373 flags);
374 if (!func && clone->name().contains('_')) {
375 QStringList path = parentPath;
376 path << clone->name().split('_');
377 func = tre->findFunctionNode(path, relative, flags);
378 }
379
380 if (func) {
381 NodeList overloads = func->parent()->overloads(func->name());
382 NodeList candidates;
383 for (int i = 0; i < overloads.count(); ++i) {
384 FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));
385 if (overload->status() != Node::Compat
386 && overload->parameters().count() == clone->parameters().count()
387 && !overload->isConst() == !clone->isConst())
388 candidates << overload;
389 }
390 if (candidates.count() == 0)
391 return 0;
392
393 /*
394 There's only one function with the correct number
395 of parameters. That must be the one.
396 */
397 if (candidates.count() == 1)
398 return static_cast<FunctionNode *>(candidates.first());
399
400 overloads = candidates;
401 candidates.clear();
402 for (int i = 0; i < overloads.count(); ++i) {
403 FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));
404 QList<Parameter> params1 = overload->parameters();
405 QList<Parameter> params2 = clone->parameters();
406
407 int j;
408 for (j = 0; j < params1.count(); ++j) {
409 if (!params2.at(j).name().startsWith(params1.at(j).name()))
410 break;
411 }
412 if (j == params1.count())
413 candidates << overload;
414 }
415
416 /*
417 There are several functions with the correct
418 parameter count, but only one has the correct
419 parameter names.
420 */
421 if (candidates.count() == 1)
422 return static_cast<FunctionNode *>(candidates.first());
423
424 candidates.clear();
425 for (int i = 0; i < overloads.count(); ++i) {
426 FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));
427 QList<Parameter> params1 = overload->parameters();
428 QList<Parameter> params2 = clone->parameters();
429
430 int j;
431 for (j = 0; j < params1.count(); ++j) {
432 if (params1.at(j).rightType() != params2.at(j).rightType())
433 break;
434
435 if (cleanType(params1.at(j).leftType(), tree)
436 != cleanType(params2.at(j).leftType(), tree))
437 break;
438 }
439 if (j == params1.count())
440 candidates << overload;
441 }
442
443
444 /*
445 There are several functions with the correct
446 parameter count, but only one has the correct
447 types, loosely compared.
448 */
449 if (candidates.count() == 1)
450 return static_cast<FunctionNode *>(candidates.first());
451
452 return 0;
453 }
454 }
455 delete clone;
456 }
457 return func;
458}
459
460/*!
461 Returns the set of strings reopresenting the topic commands.
462 */
463QSet<QString> CppCodeParser::topicCommands()
464{
465 return QSet<QString>() << COMMAND_CLASS
466 << COMMAND_ENUM
467 << COMMAND_EXAMPLE
468 << COMMAND_EXTERNALPAGE
469 << COMMAND_FILE
470 << COMMAND_FN
471 << COMMAND_GROUP
472 << COMMAND_HEADERFILE
473 << COMMAND_MACRO
474 << COMMAND_MODULE
475 << COMMAND_NAMESPACE
476 << COMMAND_PAGE
477 << COMMAND_PROPERTY
478 << COMMAND_SERVICE
479 << COMMAND_TYPEDEF
480#ifdef QDOC_QML
481 << COMMAND_VARIABLE
482 << COMMAND_QMLCLASS
483 << COMMAND_QMLPROPERTY;
484#else
485 << COMMAND_VARIABLE;
486#endif
487}
488
489/*!
490 Process the topic \a command in context \a doc with argument \a arg.
491 */
492Node *CppCodeParser::processTopicCommand(const Doc& doc,
493 const QString& command,
494 const QString& arg)
495{
496 if (command == COMMAND_FN) {
497 QStringList parentPath;
498 FunctionNode *func = 0;
499 FunctionNode *clone = 0;
500
501 if (!makeFunctionNode(arg, &parentPath, &clone) &&
502 !makeFunctionNode("void " + arg, &parentPath, &clone)) {
503 doc.location().warning(tr("Invalid syntax in '\\%1'")
504 .arg(COMMAND_FN));
505 }
506 else {
507 if (!usedNamespaces.isEmpty()) {
508 foreach (const QString &usedNamespace, usedNamespaces) {
509 QStringList newPath = usedNamespace.split("::") + parentPath;
510 func = tre->findFunctionNode(newPath, clone);
511 if (func)
512 break;
513 }
514 }
515 // Search the root namespace if no match was found.
516 if (func == 0)
517 func = tre->findFunctionNode(parentPath, clone);
518
519 if (func == 0) {
520 if (parentPath.isEmpty() && !lastPath.isEmpty())
521 func = tre->findFunctionNode(lastPath, clone);
522 if (func == 0) {
523 doc.location().warning(tr("Cannot find '%1' in '\\%2'")
524 .arg(clone->name() + "(...)")
525 .arg(COMMAND_FN),
526 tr("I cannot find any function of that name with the "
527 "specified signature. Make sure that the signature "
528 "is identical to the declaration, including 'const' "
529 "qualifiers."));
530 }
531 else {
532 doc.location().warning(tr("Missing '%1::' for '%2' in '\\%3'")
533 .arg(lastPath.join("::"))
534 .arg(clone->name() + "()")
535 .arg(COMMAND_FN));
536 }
537 }
538 else {
539 lastPath = parentPath;
540 }
541
542 if (func)
543 func->borrowParameterNames(clone);
544 delete clone;
545 }
546 return func;
547 }
548 else if (command == COMMAND_MACRO) {
549 QStringList parentPath;
550 FunctionNode *func = 0;
551
552 if (makeFunctionNode(arg, &parentPath, &func, tre->root())) {
553 if (!parentPath.isEmpty()) {
554 doc.location().warning(tr("Invalid syntax in '\\%1'")
555 .arg(COMMAND_MACRO));
556 delete func;
557 func = 0;
558 }
559 else {
560 func->setMetaness(FunctionNode::MacroWithParams);
561 QList<Parameter> params = func->parameters();
562 for (int i = 0; i < params.size(); ++i) {
563 Parameter &param = params[i];
564 if (param.name().isEmpty() && !param.leftType().isEmpty()
565 && param.leftType() != "...")
566 param = Parameter("", "", param.leftType());
567 }
568 func->setParameters(params);
569 }
570 return func;
571 }
572 else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg)) {
573 func = new FunctionNode(tre->root(), arg);
574 func->setAccess(Node::Public);
575 func->setLocation(doc.location());
576 func->setMetaness(FunctionNode::MacroWithoutParams);
577 }
578 else {
579 doc.location().warning(tr("Invalid syntax in '\\%1'")
580 .arg(COMMAND_MACRO));
581
582 }
583 return func;
584 }
585 else if (nodeTypeMap.contains(command)) {
586 /*
587 The command was neither "fn" nor "macro" .
588 */
589 // ### split(" ") hack is there to support header file syntax
590 QStringList paths = arg.split(" ");
591 QStringList path = paths[0].split("::");
592
593#if QDOC2DOX
594 // qdoc -> doxygen.
595 if (Doc::isDoxPass(1)) {
596 if (command == COMMAND_PROPERTY) {
597 Doc::insertProperty(path);
598 }
599 else if (command == COMMAND_VARIABLE) {
600 Doc::insertVariable(path);
601 }
602 else if (command == COMMAND_ENUM) {
603 // zzz
604 }
605 }
606#endif
607
608 Node *node = 0;
609 if (!usedNamespaces.isEmpty()) {
610 foreach (const QString &usedNamespace, usedNamespaces) {
611 QStringList newPath = usedNamespace.split("::") + path;
612 node = tre->findNode(newPath, nodeTypeMap[command]);
613 if (node) {
614 path = newPath;
615 break;
616 }
617 }
618 }
619 // Search the root namespace if no match was found.
620 if (node == 0)
621 node = tre->findNode(path, nodeTypeMap[command]);
622
623 if (node == 0) {
624 doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
625 .arg(arg).arg(command));
626 lastPath = path;
627
628 }
629 else if (command == COMMAND_SERVICE) {
630 // If the command is "\service", then we need to tag the
631 // class with the actual service name.
632 QStringList args = arg.split(" ");
633 if (args.size() > 1) {
634 ClassNode *cnode = static_cast<ClassNode *>(node);
635 cnode->setServiceName(args[1]);
636 cnode->setHideFromMainList(true);
637 }
638 }
639 else if (node->isInnerNode()) {
640 if (path.size() > 1) {
641 path.pop_back();
642 usedNamespaces.insert(path.join("::"));
643 }
644 }
645
646 return node;
647 }
648 else if (command == COMMAND_EXAMPLE) {
649 FakeNode *fake = new FakeNode(tre->root(), arg, FakeNode::Example);
650 createExampleFileNodes(fake);
651 return fake;
652 }
653 else if (command == COMMAND_EXTERNALPAGE) {
654 return new FakeNode(tre->root(), arg, FakeNode::ExternalPage);
655 }
656 else if (command == COMMAND_FILE) {
657 return new FakeNode(tre->root(), arg, FakeNode::File);
658 }
659 else if (command == COMMAND_GROUP) {
660 return new FakeNode(tre->root(), arg, FakeNode::Group);
661 }
662 else if (command == COMMAND_HEADERFILE) {
663 return new FakeNode(tre->root(), arg, FakeNode::HeaderFile);
664 }
665 else if (command == COMMAND_MODULE) {
666 return new FakeNode(tre->root(), arg, FakeNode::Module);
667 }
668 else if (command == COMMAND_PAGE) {
669 return new FakeNode(tre->root(), arg, FakeNode::Page);
670 }
671#ifdef QDOC_QML
672 else if (command == COMMAND_QMLCLASS) {
673 const ClassNode* classNode = 0;
674 QStringList names = arg.split(" ");
675 //qDebug() << "QMLCLASS" << names;
676 if (names.size() > 1) {
677 Node* n = tre->findNode(names[1].split("::"),Node::Class);
678 if (n) {
679 classNode = static_cast<const ClassNode*>(n);
680 //qDebug() << "FOUND IT!" << classNode->name();
681 }
682 }
683 return new QmlNode(tre->root(), names[0], classNode);
684 }
685#endif
686 return 0;
687}
688
689/*!
690 Returns the set of strings representing the common metacommands
691 plus some other metacommands.
692 */
693QSet<QString> CppCodeParser::otherMetaCommands()
694{
695 return commonMetaCommands() << COMMAND_INHEADERFILE
696 << COMMAND_OVERLOAD
697 << COMMAND_REIMP
698 << COMMAND_RELATES
699 << COMMAND_CONTENTSPAGE
700 << COMMAND_NEXTPAGE
701 << COMMAND_PREVIOUSPAGE
702 << COMMAND_INDEXPAGE
703 << COMMAND_STARTPAGE;
704}
705
706/*!
707 Process the metacommand \a command in the context of the
708 \a node associated with the topic command and the \a doc.
709 \a arg is the argument to the metacommand.
710 */
711void CppCodeParser::processOtherMetaCommand(const Doc& doc,
712 const QString& command,
713 const QString& arg,
714 Node *node)
715{
716 if (command == COMMAND_INHEADERFILE) {
717 if (node != 0 && node->isInnerNode()) {
718 ((InnerNode *) node)->addInclude(arg);
719 }
720 else {
721 doc.location().warning(tr("Ignored '\\%1'")
722 .arg(COMMAND_INHEADERFILE));
723 }
724 }
725 else if (command == COMMAND_OVERLOAD) {
726 if (node != 0 && node->type() == Node::Function) {
727 ((FunctionNode *) node)->setOverload(true);
728 }
729 else {
730 doc.location().warning(tr("Ignored '\\%1'")
731 .arg(COMMAND_OVERLOAD));
732 }
733 }
734 else if (command == COMMAND_REIMP) {
735 if (node != 0 && node->type() == Node::Function) {
736 FunctionNode *func = (FunctionNode *) node;
737 const FunctionNode *from = func->reimplementedFrom();
738 if (from == 0) {
739 doc.location().warning(
740 tr("Cannot find base function for '\\%1' in %2()")
741 .arg(COMMAND_REIMP).arg(node->name()),
742 tr("The function either doesn't exist in any base class "
743 "with the same signature or it exists but isn't virtual."));
744 }
745#if 0 // Ideally, we would enable this check to warn whenever \reimp is used
746 // incorrectly, and only make the node internal if the function is a
747 // reimplementation of another function in a base class.
748 else if (from->access() == Node::Private
749 || from->parent()->access() == Node::Private) {
750 doc.location().warning(
751 tr("Base function for '\\%1' in %2() is private or internal")
752 .arg(COMMAND_REIMP).arg(node->name()));
753 }
754#endif
755 // Note: Setting the access to Private hides the documentation,
756 // but setting the status to Internal makes the node available
757 // in the XML output when the WebXMLGenerator is used.
758 func->setAccess(Node::Private);
759 func->setStatus(Node::Internal);
760 }
761 else {
762 doc.location().warning(tr("Ignored '\\%1' in %2")
763 .arg(COMMAND_REIMP)
764 .arg(node->name()));
765 }
766 }
767 else if (command == COMMAND_RELATES) {
768 InnerNode *pseudoParent;
769 if (arg.startsWith("<") || arg.startsWith("\"")) {
770 pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(arg), Node::Fake));
771 }
772 else {
773 QStringList newPath = arg.split("::");
774 pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(newPath), Node::Class));
775 if (!pseudoParent)
776 pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(newPath),
777 Node::Namespace));
778 }
779 if (!pseudoParent) {
780 doc.location().warning(tr("Cannot find '%1' in '\\%2'")
781 .arg(arg).arg(COMMAND_RELATES));
782 }
783 else {
784 node->setRelates(pseudoParent);
785 }
786 }
787 else if (command == COMMAND_CONTENTSPAGE) {
788 setLink(node, Node::ContentsLink, arg);
789 }
790 else if (command == COMMAND_NEXTPAGE) {
791 setLink(node, Node::NextLink, arg);
792 }
793 else if (command == COMMAND_PREVIOUSPAGE) {
794 setLink(node, Node::PreviousLink, arg);
795 }
796 else if (command == COMMAND_INDEXPAGE) {
797 setLink(node, Node::IndexLink, arg);
798 }
799 else if (command == COMMAND_STARTPAGE) {
800 setLink(node, Node::StartLink, arg);
801 }
802 else {
803 processCommonMetaCommand(doc.location(),command,arg,node,tre);
804 }
805}
806
807/*!
808 The topic command has been processed resulting in the \a doc
809 and \a node passed in here. Process the other meta commands,
810 which are found in \a doc, in the context of the topic \a node.
811 */
812void CppCodeParser::processOtherMetaCommands(const Doc& doc, Node *node)
813{
814 const QSet<QString> metaCommands = doc.metaCommandsUsed();
815 QSet<QString>::ConstIterator cmd = metaCommands.begin();
816 while (cmd != metaCommands.end()) {
817 QStringList args = doc.metaCommandArgs(*cmd);
818 QStringList::ConstIterator arg = args.begin();
819 while (arg != args.end()) {
820 processOtherMetaCommand(doc, *cmd, *arg, node);
821 ++arg;
822 }
823 ++cmd;
824 }
825}
826
827/*!
828 Resets the C++ code parser to its default initialized state.
829 */
830void CppCodeParser::reset(Tree *tree)
831{
832 tre = tree;
833 tokenizer = 0;
834 tok = 0;
835 access = Node::Public;
836 metaness = FunctionNode::Plain;
837 lastPath.clear();
838 moduleName = "";
839}
840
841/*!
842 Get the next token from the file being parsed and store it
843 in the token variable.
844 */
845void CppCodeParser::readToken()
846{
847 tok = tokenizer->getToken();
848}
849
850/*!
851 Return the current location in the file being parsed,
852 i.e. the file name, line number, and column number.
853 */
854const Location& CppCodeParser::location()
855{
856 return tokenizer->location();
857}
858
859/*!
860 Return the previous string read from the file being parsed.
861 */
862QString CppCodeParser::previousLexeme()
863{
864 return tokenizer->previousLexeme();
865}
866
867/*!
868 Return the current string string from the file being parsed.
869 */
870QString CppCodeParser::lexeme()
871{
872 return tokenizer->lexeme();
873}
874
875bool CppCodeParser::match(int target)
876{
877 if (tok == target) {
878 readToken();
879 return true;
880 }
881 else {
882 return false;
883 }
884}
885
886/*!
887 If the current token is one of the keyword thingees that
888 are used in Qt, skip over it to the next token and return
889 true. Otherwise just return false without reading the
890 next token.
891 */
892bool CppCodeParser::matchCompat()
893{
894 switch (tok) {
895 case Tok_QT_COMPAT:
896 case Tok_QT_COMPAT_CONSTRUCTOR:
897 case Tok_QT_DEPRECATED:
898 case Tok_QT_MOC_COMPAT:
899 case Tok_QT3_SUPPORT:
900 case Tok_QT3_SUPPORT_CONSTRUCTOR:
901 case Tok_QT3_MOC_SUPPORT:
902 readToken();
903 return true;
904 default:
905 return false;
906 }
907}
908
909bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
910{
911 bool matches = (tok == Tok_LeftAngle);
912 if (matches) {
913 int leftAngleDepth = 0;
914 int parenAndBraceDepth = 0;
915 do {
916 if (tok == Tok_LeftAngle) {
917 leftAngleDepth++;
918 } else if (tok == Tok_RightAngle) {
919 leftAngleDepth--;
920 } else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) {
921 ++parenAndBraceDepth;
922 } else if (tok == Tok_RightParen || tok == Tok_RightBrace) {
923 if (--parenAndBraceDepth < 0)
924 return false;
925 }
926
927 if (dataType != 0)
928 dataType->append(lexeme());
929 readToken();
930 } while (leftAngleDepth > 0 && tok != Tok_Eoi);
931 }
932 return matches;
933}
934
935bool CppCodeParser::matchTemplateHeader()
936{
937 readToken();
938 return matchTemplateAngles();
939}
940
941bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
942{
943 /*
944 This code is really hard to follow... sorry. The loop is there to match
945 Alpha::Beta::Gamma::...::Omega.
946 */
947 for (;;) {
948 bool virgin = true;
949
950 if (tok != Tok_Ident) {
951 /*
952 There is special processing for 'Foo::operator int()'
953 and such elsewhere. This is the only case where we
954 return something with a trailing gulbrandsen ('Foo::').
955 */
956 if (tok == Tok_operator)
957 return true;
958
959 /*
960 People may write 'const unsigned short' or
961 'short unsigned const' or any other permutation.
962 */
963 while (match(Tok_const) || match(Tok_volatile))
964 dataType->append(previousLexeme());
965 while (match(Tok_signed) || match(Tok_unsigned) ||
966 match(Tok_short) || match(Tok_long) || match(Tok_int64)) {
967 dataType->append(previousLexeme());
968 virgin = false;
969 }
970 while (match(Tok_const) || match(Tok_volatile))
971 dataType->append(previousLexeme());
972
973 if (match(Tok_Tilde))
974 dataType->append(previousLexeme());
975 }
976
977 if (virgin) {
978 if (match(Tok_Ident))
979 dataType->append(previousLexeme());
980 else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||
981 match(Tok_double) || match(Tok_Ellipsis))
982 dataType->append(previousLexeme());
983 else
984 return false;
985 } else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
986 dataType->append(previousLexeme());
987 }
988
989 matchTemplateAngles(dataType);
990
991 while (match(Tok_const) || match(Tok_volatile))
992 dataType->append(previousLexeme());
993
994 if (match(Tok_Gulbrandsen))
995 dataType->append(previousLexeme());
996 else
997 break;
998 }
999
1000 while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) ||
1001 match(Tok_Caret))
1002 dataType->append(previousLexeme());
1003
1004 if (match(Tok_LeftParenAster)) {
1005 /*
1006 A function pointer. This would be rather hard to handle without a
1007 tokenizer hack, because a type can be followed with a left parenthesis
1008 in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
1009 as a single token.
1010 */
1011 dataType->append(previousLexeme());
1012 dataType->appendHotspot();
1013 if (var != 0 && match(Tok_Ident))
1014 *var = previousLexeme();
1015 if (!match(Tok_RightParen) || tok != Tok_LeftParen)
1016 return false;
1017 dataType->append(previousLexeme());
1018
1019 int parenDepth0 = tokenizer->parenDepth();
1020 while (tokenizer->parenDepth() >= parenDepth0 && tok != Tok_Eoi) {
1021 dataType->append(lexeme());
1022 readToken();
1023 }
1024 if (match(Tok_RightParen))
1025 dataType->append(previousLexeme());
1026 }
1027 else {
1028 /*
1029 The common case: Look for an optional identifier, then for
1030 some array brackets.
1031 */
1032 dataType->appendHotspot();
1033
1034 if (var != 0) {
1035 if (match(Tok_Ident)) {
1036 *var = previousLexeme();
1037 }
1038 else if (match(Tok_Comment)) {
1039 /*
1040 A neat hack: Commented-out parameter names are
1041 recognized by qdoc. It's impossible to illustrate
1042 here inside a C-style comment, because it requires
1043 an asterslash. It's also impossible to illustrate
1044 inside a C++-style comment, because the explanation
1045 does not fit on one line.
1046 */
1047 if (varComment.exactMatch(previousLexeme()))
1048 *var = varComment.cap(1);
1049 }
1050 }
1051
1052 if (tok == Tok_LeftBracket) {
1053 int bracketDepth0 = tokenizer->bracketDepth();
1054 while ((tokenizer->bracketDepth() >= bracketDepth0 &&
1055 tok != Tok_Eoi) ||
1056 tok == Tok_RightBracket) {
1057 dataType->append(lexeme());
1058 readToken();
1059 }
1060 }
1061 }
1062 return true;
1063}
1064
1065bool CppCodeParser::matchParameter(FunctionNode *func)
1066{
1067 CodeChunk dataType;
1068 QString name;
1069 CodeChunk defaultValue;
1070
1071 if (!matchDataType(&dataType, &name))
1072 return false;
1073 match(Tok_Comment);
1074 if (match(Tok_Equal)) {
1075 int parenDepth0 = tokenizer->parenDepth();
1076
1077 while (tokenizer->parenDepth() >= parenDepth0 &&
1078 (tok != Tok_Comma ||
1079 tokenizer->parenDepth() > parenDepth0) &&
1080 tok != Tok_Eoi) {
1081 defaultValue.append(lexeme());
1082 readToken();
1083 }
1084 }
1085 func->addParameter(Parameter(dataType.toString(), "", name,
1086 defaultValue.toString())); // ###
1087 return true;
1088}
1089
1090bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
1091 QStringList *parentPathPtr,
1092 FunctionNode **funcPtr,
1093 const QString &templateStuff)
1094{
1095 CodeChunk returnType;
1096 QStringList parentPath;
1097 QString name;
1098
1099 bool compat = false;
1100
1101 if (match(Tok_friend))
1102 return false;
1103 match(Tok_explicit);
1104 if (matchCompat())
1105 compat = true;
1106 bool sta = false;
1107 if (match(Tok_static)) {
1108 sta = true;
1109 if (matchCompat())
1110 compat = true;
1111 }
1112 FunctionNode::Virtualness vir = FunctionNode::NonVirtual;
1113 if (match(Tok_virtual)) {
1114 vir = FunctionNode::ImpureVirtual;
1115 if (matchCompat())
1116 compat = true;
1117 }
1118
1119 if (!matchDataType(&returnType)) {
1120 if (tokenizer->parsingFnOrMacro()
1121 && (match(Tok_Q_DECLARE_FLAGS) || match(Tok_Q_PROPERTY)))
1122 returnType = CodeChunk(previousLexeme());
1123 else
1124 return false;
1125 }
1126
1127 if (returnType.toString() == "QBool")
1128 returnType = CodeChunk("bool");
1129
1130 if (matchCompat())
1131 compat = true;
1132
1133 if (tok == Tok_operator &&
1134 (returnType.toString().isEmpty() || returnType.toString().endsWith("::"))) {
1135 // 'QString::operator const char *()'
1136 parentPath = returnType.toString().split(sep);
1137 parentPath.removeAll(QString());
1138 returnType = CodeChunk();
1139 readToken();
1140
1141 CodeChunk restOfName;
1142 if (tok != Tok_Tilde && matchDataType(&restOfName)) {
1143 name = "operator " + restOfName.toString();
1144 }
1145 else {
1146 name = previousLexeme() + lexeme();
1147 readToken();
1148 while (tok != Tok_LeftParen && tok != Tok_Eoi) {
1149 name += lexeme();
1150 readToken();
1151 }
1152 }
1153 if (tok != Tok_LeftParen)
1154 return false;
1155 }
1156 else if (tok == Tok_LeftParen) {
1157 // constructor or destructor
1158 parentPath = returnType.toString().split(sep);
1159 if (!parentPath.isEmpty()) {
1160 name = parentPath.last();
1161 parentPath.erase(parentPath.end() - 1);
1162 }
1163 returnType = CodeChunk();
1164 }
1165 else {
1166 while (match(Tok_Ident)) {
1167 name = previousLexeme();
1168 matchTemplateAngles();
1169
1170 if (match(Tok_Gulbrandsen)) {
1171 parentPath.append(name);
1172 } else {
1173 break;
1174 }
1175 }
1176
1177 if (tok == Tok_operator) {
1178 name = lexeme();
1179 readToken();
1180 while (tok != Tok_Eoi) {
1181 name += lexeme();
1182 readToken();
1183 if (tok == Tok_LeftParen)
1184 break;
1185 }
1186 }
1187 if (parent && (tok == Tok_Semicolon || tok == Tok_LeftBracket || tok == Tok_Colon)
1188 && access != Node::Private) {
1189 if (tok == Tok_LeftBracket) {
1190 returnType.appendHotspot();
1191
1192 int bracketDepth0 = tokenizer->bracketDepth();
1193 while ((tokenizer->bracketDepth() >= bracketDepth0 &&
1194 tok != Tok_Eoi) ||
1195 tok == Tok_RightBracket) {
1196 returnType.append(lexeme());
1197 readToken();
1198 }
1199 if (tok != Tok_Semicolon)
1200 return false;
1201 } else if (tok == Tok_Colon) {
1202 returnType.appendHotspot();
1203
1204 while (tok != Tok_Semicolon && tok != Tok_Eoi) {
1205 returnType.append(lexeme());
1206 readToken();
1207 }
1208 if (tok != Tok_Semicolon)
1209 return false;
1210 }
1211
1212 VariableNode *var = new VariableNode(parent, name);
1213 var->setAccess(access);
1214 var->setLocation(location());
1215 var->setLeftType(returnType.left());
1216 var->setRightType(returnType.right());
1217 if (compat)
1218 var->setStatus(Node::Compat);
1219 var->setStatic(sta);
1220 return false;
1221 }
1222 if (tok != Tok_LeftParen)
1223 return false;
1224 }
1225 readToken();
1226
1227 FunctionNode *func = new FunctionNode(parent, name);
1228 func->setAccess(access);
1229 func->setLocation(location());
1230 func->setReturnType(returnType.toString());
1231 func->setTemplateStuff(templateStuff);
1232 if (compat)
1233 func->setStatus(Node::Compat);
1234
1235 func->setMetaness(metaness);
1236 if (parent) {
1237 if (name == parent->name()) {
1238 func->setMetaness(FunctionNode::Ctor);
1239 } else if (name.startsWith("~")) {
1240 func->setMetaness(FunctionNode::Dtor);
1241 }
1242 }
1243 func->setStatic(sta);
1244
1245 if (tok != Tok_RightParen) {
1246 do {
1247 if (!matchParameter(func))
1248 return false;
1249 } while (match(Tok_Comma));
1250 }
1251 if (!match(Tok_RightParen))
1252 return false;
1253
1254 func->setConst(match(Tok_const));
1255
1256 if (match(Tok_Equal) && match(Tok_Number))
1257 vir = FunctionNode::PureVirtual;
1258 func->setVirtualness(vir);
1259
1260 if (match(Tok_Colon)) {
1261 while (tok != Tok_LeftBrace && tok != Tok_Eoi)
1262 readToken();
1263 }
1264
1265 if (!match(Tok_Semicolon) && tok != Tok_Eoi) {
1266 int braceDepth0 = tokenizer->braceDepth();
1267
1268 if (!match(Tok_LeftBrace))
1269 return false;
1270 while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi)
1271 readToken();
1272 match(Tok_RightBrace);
1273 }
1274 if (parentPathPtr != 0)
1275 *parentPathPtr = parentPath;
1276 if (funcPtr != 0)
1277 *funcPtr = func;
1278 return true;
1279}
1280
1281bool CppCodeParser::matchBaseSpecifier(ClassNode *classe, bool isClass)
1282{
1283 Node::Access access;
1284
1285 switch (tok) {
1286 case Tok_public:
1287 access = Node::Public;
1288 readToken();
1289 break;
1290 case Tok_protected:
1291 access = Node::Protected;
1292 readToken();
1293 break;
1294 case Tok_private:
1295 access = Node::Private;
1296 readToken();
1297 break;
1298 default:
1299 access = isClass ? Node::Private : Node::Public;
1300 }
1301
1302 if (tok == Tok_virtual)
1303 readToken();
1304
1305 CodeChunk baseClass;
1306 if (!matchDataType(&baseClass))
1307 return false;
1308
1309 tre->addBaseClass(classe,
1310 access,
1311 baseClass.toPath(),
1312 baseClass.toString(),
1313 classe->parent());
1314 return true;
1315}
1316
1317bool CppCodeParser::matchBaseList(ClassNode *classe, bool isClass)
1318{
1319 for (;;) {
1320 if (!matchBaseSpecifier(classe, isClass))
1321 return false;
1322 if (tok == Tok_LeftBrace)
1323 return true;
1324 if (!match(Tok_Comma))
1325 return false;
1326 }
1327}
1328
1329/*!
1330 Parse a C++ class, union, or struct declarion.
1331 */
1332bool CppCodeParser::matchClassDecl(InnerNode *parent,
1333 const QString &templateStuff)
1334{
1335 bool isClass = (tok == Tok_class);
1336 readToken();
1337
1338 bool compat = matchCompat();
1339
1340 if (tok != Tok_Ident)
1341 return false;
1342 while (tok == Tok_Ident)
1343 readToken();
1344 if (tok != Tok_Colon && tok != Tok_LeftBrace)
1345 return false;
1346
1347 /*
1348 So far, so good. We have 'class Foo {' or 'class Foo :'.
1349 This is enough to recognize a class definition.
1350 */
1351 ClassNode *classe = new ClassNode(parent, previousLexeme());
1352 classe->setAccess(access);
1353 classe->setLocation(location());
1354 if (compat)
1355 classe->setStatus(Node::Compat);
1356 if (!moduleName.isEmpty())
1357 classe->setModuleName(moduleName);
1358 classe->setTemplateStuff(templateStuff);
1359
1360 if (match(Tok_Colon) && !matchBaseList(classe, isClass))
1361 return false;
1362 if (!match(Tok_LeftBrace))
1363 return false;
1364
1365 Node::Access outerAccess = access;
1366 access = isClass ? Node::Private : Node::Public;
1367 FunctionNode::Metaness outerMetaness = metaness;
1368 metaness = FunctionNode::Plain;
1369
1370 bool matches = (matchDeclList(classe) && match(Tok_RightBrace) &&
1371 match(Tok_Semicolon));
1372 access = outerAccess;
1373 metaness = outerMetaness;
1374 return matches;
1375}
1376
1377bool CppCodeParser::matchNamespaceDecl(InnerNode *parent)
1378{
1379 readToken(); // skip 'namespace'
1380 if (tok != Tok_Ident)
1381 return false;
1382 while (tok == Tok_Ident)
1383 readToken();
1384 if (tok != Tok_LeftBrace)
1385 return false;
1386
1387 /*
1388 So far, so good. We have 'namespace Foo {'.
1389 */
1390 QString namespaceName = previousLexeme();
1391 NamespaceNode *namespasse = 0;
1392 if (parent)
1393 namespasse = static_cast<NamespaceNode *>(parent->findNode(namespaceName, Node::Namespace));
1394 if (!namespasse) {
1395 namespasse = new NamespaceNode(parent, namespaceName);
1396 namespasse->setAccess(access);
1397 namespasse->setLocation(location());
1398 }
1399
1400 readToken(); // skip '{'
1401 bool matched = matchDeclList(namespasse);
1402
1403 return matched && match(Tok_RightBrace);
1404}
1405
1406bool CppCodeParser::matchUsingDecl()
1407{
1408 readToken(); // skip 'using'
1409
1410 // 'namespace'
1411 if (tok != Tok_namespace)
1412 return false;
1413
1414 readToken();
1415 // identifier
1416 if (tok != Tok_Ident)
1417 return false;
1418
1419 QString name;
1420 while (tok == Tok_Ident) {
1421 name += lexeme();
1422 readToken();
1423 if (tok == Tok_Semicolon)
1424 break;
1425 else if (tok != Tok_Gulbrandsen)
1426 return false;
1427 name += "::";
1428 readToken();
1429 }
1430
1431 /*
1432 So far, so good. We have 'using namespace Foo;'.
1433 */
1434 usedNamespaces.insert(name);
1435 return true;
1436}
1437
1438bool CppCodeParser::matchEnumItem(InnerNode *parent, EnumNode *enume)
1439{
1440 if (!match(Tok_Ident))
1441 return false;
1442
1443 QString name = previousLexeme();
1444 CodeChunk val;
1445
1446 if (match(Tok_Equal)) {
1447 while (tok != Tok_Comma && tok != Tok_RightBrace &&
1448 tok != Tok_Eoi) {
1449 val.append(lexeme());
1450 readToken();
1451 }
1452 }
1453
1454 if (enume) {
1455 QString strVal = val.toString();
1456 if (strVal.isEmpty()) {
1457 if (enume->items().isEmpty()) {
1458 strVal = "0";
1459 } else {
1460 QString last = enume->items().last().value();
1461 bool ok;
1462 int n = last.toInt(&ok);
1463 if (ok) {
1464 if (last.startsWith("0") && last.size() > 1) {
1465 if (last.startsWith("0x") || last.startsWith("0X"))
1466 strVal = last.left(2) + QString::number(n + 1, 16);
1467 else
1468 strVal = "0" + QString::number(n + 1, 8);
1469 } else {
1470 strVal = QString::number(n + 1);
1471 }
1472 }
1473 }
1474 }
1475
1476 enume->addItem(EnumItem(name, strVal));
1477 } else {
1478 VariableNode *var = new VariableNode(parent, name);
1479 var->setAccess(access);
1480 var->setLocation(location());
1481 var->setLeftType("const int");
1482 var->setStatic(true);
1483 }
1484 return true;
1485}
1486
1487bool CppCodeParser::matchEnumDecl(InnerNode *parent)
1488{
1489 QString name;
1490
1491 if (!match(Tok_enum))
1492 return false;
1493 if (match(Tok_Ident))
1494 name = previousLexeme();
1495 if (tok != Tok_LeftBrace)
1496 return false;
1497
1498 EnumNode *enume = 0;
1499
1500 if (!name.isEmpty()) {
1501 enume = new EnumNode(parent, name);
1502 enume->setAccess(access);
1503 enume->setLocation(location());
1504 }
1505
1506 readToken();
1507
1508 if (!matchEnumItem(parent, enume))
1509 return false;
1510
1511 while (match(Tok_Comma)) {
1512 if (!matchEnumItem(parent, enume))
1513 return false;
1514 }
1515 return match(Tok_RightBrace) && match(Tok_Semicolon);
1516}
1517
1518bool CppCodeParser::matchTypedefDecl(InnerNode *parent)
1519{
1520 CodeChunk dataType;
1521 QString name;
1522
1523 if (!match(Tok_typedef))
1524 return false;
1525 if (!matchDataType(&dataType, &name))
1526 return false;
1527 if (!match(Tok_Semicolon))
1528 return false;
1529
1530 if (parent && !parent->findNode(name, Node::Typedef)) {
1531 TypedefNode *typedeffe = new TypedefNode(parent, name);
1532 typedeffe->setAccess(access);
1533 typedeffe->setLocation(location());
1534 }
1535 return true;
1536}
1537
1538bool CppCodeParser::matchProperty(InnerNode *parent)
1539{
1540 if (!match(Tok_Q_PROPERTY) &&
1541 !match(Tok_Q_OVERRIDE) &&
1542 !match(Tok_QDOC_PROPERTY))
1543 return false;
1544 if (!match(Tok_LeftParen))
1545 return false;
1546
1547 QString name;
1548 CodeChunk dataType;
1549 if (!matchDataType(&dataType, &name))
1550 return false;
1551
1552 PropertyNode *property = new PropertyNode(parent, name);
1553 property->setAccess(Node::Public);
1554 property->setLocation(location());
1555 property->setDataType(dataType.toString());
1556
1557 while (tok != Tok_RightParen && tok != Tok_Eoi) {
1558 if (!match(Tok_Ident))
1559 return false;
1560 QString key = previousLexeme();
1561 QString value;
1562
1563 if (match(Tok_Ident)) {
1564 value = previousLexeme();
1565 } else if (match(Tok_LeftParen)) {
1566 int depth = 1;
1567 while (tok != Tok_Eoi) {
1568 if (tok == Tok_LeftParen) {
1569 readToken();
1570 ++depth;
1571 } else if (tok == Tok_RightParen) {
1572 readToken();
1573 if (--depth == 0)
1574 break;
1575 } else {
1576 readToken();
1577 }
1578 }
1579 value = "?";
1580 }
1581
1582 if (key == "READ")
1583 tre->addPropertyFunction(property, value, PropertyNode::Getter);
1584 else if (key == "WRITE")
1585 tre->addPropertyFunction(property, value, PropertyNode::Setter);
1586 else if (key == "STORED")
1587 property->setStored(value.toLower() == "true");
1588 else if (key == "DESIGNABLE")
1589 property->setDesignable(value.toLower() == "true");
1590 else if (key == "RESET")
1591 tre->addPropertyFunction(property, value, PropertyNode::Resetter);
1592 }
1593 match(Tok_RightParen);
1594 return true;
1595}
1596
1597/*!
1598 Parse a C++ declaration.
1599 */
1600bool CppCodeParser::matchDeclList(InnerNode *parent)
1601{
1602 QString templateStuff;
1603 int braceDepth0 = tokenizer->braceDepth();
1604 if (tok == Tok_RightBrace) // prevents failure on empty body
1605 braceDepth0++;
1606
1607 while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi) {
1608 switch (tok) {
1609 case Tok_Colon:
1610 readToken();
1611 break;
1612 case Tok_class:
1613 case Tok_struct:
1614 case Tok_union:
1615 matchClassDecl(parent, templateStuff);
1616 break;
1617 case Tok_namespace:
1618 matchNamespaceDecl(parent);
1619 break;
1620 case Tok_using:
1621 matchUsingDecl();
1622 break;
1623 case Tok_template:
1624 templateStuff = matchTemplateHeader();
1625 continue;
1626 case Tok_enum:
1627 matchEnumDecl(parent);
1628 break;
1629 case Tok_typedef:
1630 matchTypedefDecl(parent);
1631 break;
1632 case Tok_private:
1633 readToken();
1634 access = Node::Private;
1635 metaness = FunctionNode::Plain;
1636 break;
1637 case Tok_protected:
1638 readToken();
1639 access = Node::Protected;
1640 metaness = FunctionNode::Plain;
1641 break;
1642 case Tok_public:
1643 readToken();
1644 access = Node::Public;
1645 metaness = FunctionNode::Plain;
1646 break;
1647 case Tok_signals:
1648 case Tok_Q_SIGNALS:
1649 readToken();
1650 access = Node::Public;
1651 metaness = FunctionNode::Signal;
1652 break;
1653 case Tok_slots:
1654 case Tok_Q_SLOTS:
1655 readToken();
1656 metaness = FunctionNode::Slot;
1657 break;
1658 case Tok_Q_OBJECT:
1659 readToken();
1660 break;
1661 case Tok_Q_OVERRIDE:
1662 case Tok_Q_PROPERTY:
1663 case Tok_QDOC_PROPERTY:
1664 matchProperty(parent);
1665 break;
1666 case Tok_Q_DECLARE_SEQUENTIAL_ITERATOR:
1667 readToken();
1668 if (match(Tok_LeftParen) && match(Tok_Ident))
1669 sequentialIteratorClasses.insert(previousLexeme(),
1670 location().fileName());
1671 match(Tok_RightParen);
1672 break;
1673 case Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR:
1674 readToken();
1675 if (match(Tok_LeftParen) && match(Tok_Ident))
1676 mutableSequentialIteratorClasses.insert(previousLexeme(),
1677 location().fileName());
1678 match(Tok_RightParen);
1679 break;
1680 case Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR:
1681 readToken();
1682 if (match(Tok_LeftParen) && match(Tok_Ident))
1683 associativeIteratorClasses.insert(previousLexeme(),
1684 location().fileName());
1685 match(Tok_RightParen);
1686 break;
1687 case Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR:
1688 readToken();
1689 if (match(Tok_LeftParen) && match(Tok_Ident))
1690 mutableAssociativeIteratorClasses.insert(previousLexeme(),
1691 location().fileName());
1692 match(Tok_RightParen);
1693 break;
1694 case Tok_Q_DECLARE_FLAGS:
1695 readToken();
1696 if (match(Tok_LeftParen) && match(Tok_Ident)) {
1697 QString flagsType = previousLexeme();
1698 if (match(Tok_Comma) && match(Tok_Ident)) {
1699 QString enumType = previousLexeme();
1700 TypedefNode *flagsNode = new TypedefNode(parent, flagsType);
1701 flagsNode->setAccess(access);
1702 flagsNode->setLocation(location());
1703 EnumNode *enumNode =
1704 static_cast<EnumNode*>(parent->findNode(enumType,
1705 Node::Enum));
1706 if (enumNode)
1707 enumNode->setFlagsType(flagsNode);
1708 }
1709 }
1710 match(Tok_RightParen);
1711 break;
1712 case Tok_QT_MODULE:
1713 readToken();
1714 if (match(Tok_LeftParen) && match(Tok_Ident))
1715 moduleName = previousLexeme();
1716 if (!moduleName.startsWith("Qt"))
1717 moduleName.prepend("Qt");
1718 match(Tok_RightParen);
1719 break;
1720 default:
1721 if (!matchFunctionDecl(parent, 0, 0, templateStuff)) {
1722 while (tok != Tok_Eoi &&
1723 (tokenizer->braceDepth() > braceDepth0 ||
1724 (!match(Tok_Semicolon) &&
1725 tok != Tok_public && tok != Tok_protected &&
1726 tok != Tok_private)))
1727 readToken();
1728 }
1729 }
1730 templateStuff.clear();
1731 }
1732 return true;
1733}
1734
1735/*!
1736 This is called by parseSourceFile() to do the actual parsing
1737 and tree building.
1738 */
1739bool CppCodeParser::matchDocsAndStuff()
1740{
1741 QSet<QString> topicCommandsAllowed = topicCommands();
1742 QSet<QString> otherMetacommandsAllowed = otherMetaCommands();
1743 QSet<QString> metacommandsAllowed = topicCommandsAllowed +
1744 otherMetacommandsAllowed;
1745
1746 while (tok != Tok_Eoi) {
1747 if (tok == Tok_Doc) {
1748 /*
1749 lexeme() returns an entire qdoc comment.
1750 */
1751 QString comment = lexeme();
1752 Location start_loc(location());
1753 readToken();
1754
1755 Doc::trimCStyleComment(start_loc,comment);
1756 /*
1757 qdoc --> doxygen
1758 We must also remember the location of the end
1759 of the comment, so we can construct a diff for
1760 it.
1761 */
1762 Location end_loc(location());
1763
1764 /*
1765 Doc parses the comment.
1766 */
1767 Doc doc(start_loc,end_loc,comment,metacommandsAllowed);
1768
1769 QString topic;
1770 QStringList args;
1771
1772 QSet<QString> topicCommandsUsed = topicCommandsAllowed &
1773 doc.metaCommandsUsed();
1774
1775 /*
1776 There should be one topic command in the set,
1777 or none. If the set is empty, then the comment
1778 should be a function description.
1779 */
1780 if (topicCommandsUsed.count() > 0) {
1781 topic = *topicCommandsUsed.begin();
1782 args = doc.metaCommandArgs(topic);
1783 }
1784
1785 NodeList nodes;
1786 QList<Doc> docs;
1787
1788 if (topic.isEmpty()) {
1789 QStringList parentPath;
1790 FunctionNode *clone;
1791 FunctionNode *func = 0;
1792
1793 if (matchFunctionDecl(0, &parentPath, &clone)) {
1794 foreach (const QString &usedNamespace, usedNamespaces) {
1795 QStringList newPath = usedNamespace.split("::") + parentPath;
1796 func = tre->findFunctionNode(newPath, clone);
1797 if (func)
1798 break;
1799 }
1800 if (func == 0)
1801 func = tre->findFunctionNode(parentPath, clone);
1802
1803 if (func) {
1804 func->borrowParameterNames(clone);
1805 nodes.append(func);
1806 docs.append(doc);
1807 }
1808 delete clone;
1809 }
1810 else {
1811 doc.location().warning(
1812 tr("Cannot tie this documentation to anything"),
1813 tr("I found a /*! ... */ comment, but there was no "
1814 "topic command (e.g., '\\%1', '\\%2') in the "
1815 "comment and no function definition following "
1816 "the comment.")
1817 .arg(COMMAND_FN).arg(COMMAND_PAGE));
1818 }
1819 }
1820 else {
1821 /*
1822 There is a topic command. Process it.
1823 */
1824 QStringList::ConstIterator a = args.begin();
1825 while (a != args.end()) {
1826 Doc nodeDoc = doc;
1827 Node *node = processTopicCommand(nodeDoc, topic, *a);
1828 if (node != 0) {
1829 nodes.append(node);
1830 docs.append(nodeDoc);
1831 }
1832 ++a;
1833 }
1834 }
1835
1836 NodeList::Iterator n = nodes.begin();
1837 QList<Doc>::Iterator d = docs.begin();
1838 while (n != nodes.end()) {
1839 processOtherMetaCommands(*d, *n);
1840 (*n)->setDoc(*d);
1841 if ((*n)->isInnerNode() && ((InnerNode *)*n)->includes().isEmpty()) {
1842 InnerNode *m = static_cast<InnerNode *>(*n);
1843 while (m->parent() != tre->root())
1844 m = m->parent();
1845 if (m == *n)
1846 ((InnerNode *)*n)->addInclude((*n)->name());
1847 else
1848 ((InnerNode *)*n)->setIncludes(m->includes());
1849 }
1850 ++d;
1851 ++n;
1852 }
1853 }
1854 else if (tok == Tok_using) {
1855 matchUsingDecl();
1856 }
1857 else {
1858 QStringList parentPath;
1859 FunctionNode *clone;
1860 FunctionNode *node = 0;
1861
1862 if (matchFunctionDecl(0, &parentPath, &clone)) {
1863 /*
1864 The location of the definition is more interesting
1865 than that of the declaration. People equipped with
1866 a sophisticated text editor can respond to warnings
1867 concerning undocumented functions very quickly.
1868
1869 Signals are implemented in uninteresting files
1870 generated by moc.
1871 */
1872 node = tre->findFunctionNode(parentPath, clone);
1873 if (node != 0 && node->metaness() != FunctionNode::Signal)
1874 node->setLocation(clone->location());
1875 delete clone;
1876 }
1877 else {
1878 if (tok != Tok_Doc)
1879 readToken();
1880 }
1881 }
1882 }
1883 return true;
1884}
1885
1886bool CppCodeParser::makeFunctionNode(const QString& synopsis,
1887 QStringList *parentPathPtr,
1888 FunctionNode **funcPtr,
1889 InnerNode *root)
1890{
1891 Tokenizer *outerTokenizer = tokenizer;
1892 int outerTok = tok;
1893
1894 Location loc;
1895 QByteArray latin1 = synopsis.toLatin1();
1896 Tokenizer stringTokenizer(loc, latin1);
1897 stringTokenizer.setParsingFnOrMacro(true);
1898 tokenizer = &stringTokenizer;
1899 readToken();
1900
1901 bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr);
1902 // potential memory leak with funcPtr
1903
1904 tokenizer = outerTokenizer;
1905 tok = outerTok;
1906
1907 return ok;
1908}
1909
1910void CppCodeParser::parseQiteratorDotH(const Location &location,
1911 const QString &filePath)
1912{
1913 QFile file(filePath);
1914 if (!file.open(QFile::ReadOnly))
1915 return;
1916
1917 QString text = file.readAll();
1918 text.remove("\r");
1919 text.replace("\\\n", "");
1920 QStringList lines = text.split("\n");
1921 lines = lines.filter("Q_DECLARE");
1922 lines.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), "");
1923
1924 if (lines.size() == 4) {
1925 sequentialIteratorDefinition = lines[0];
1926 mutableSequentialIteratorDefinition = lines[1];
1927 associativeIteratorDefinition = lines[2];
1928 mutableAssociativeIteratorDefinition = lines[3];
1929 } else {
1930 location.warning(tr("The qiterator.h hack failed"));
1931 }
1932}
1933
1934void CppCodeParser::instantiateIteratorMacro(const QString &container,
1935 const QString &includeFile,
1936 const QString &macroDef,
1937 Tree * /* tree */)
1938{
1939 QString resultingCode = macroDef;
1940 resultingCode.replace(QRegExp("\\bC\\b"), container);
1941 resultingCode.replace(QRegExp("\\s*##\\s*"), "");
1942
1943 Location loc(includeFile); // hack to get the include file for free
1944 QByteArray latin1 = resultingCode.toLatin1();
1945 Tokenizer stringTokenizer(loc, latin1);
1946 tokenizer = &stringTokenizer;
1947 readToken();
1948 matchDeclList(tre->root());
1949}
1950
1951void CppCodeParser::createExampleFileNodes(FakeNode *fake)
1952{
1953 QString examplePath = fake->name();
1954
1955 // we can assume that this file always exists
1956 QString proFileName = examplePath + "/" +
1957 examplePath.split("/").last() + ".pro";
1958
1959 QString userFriendlyFilePath;
1960 QString fullPath = Config::findFile(fake->doc().location(),
1961 exampleFiles,
1962 exampleDirs,
1963 proFileName,
1964 userFriendlyFilePath);
1965 if (fullPath.isEmpty()) {
1966 QString tmp = proFileName;
1967 proFileName = examplePath + "/" + "qbuild.pro";
1968 userFriendlyFilePath.clear();
1969 fullPath = Config::findFile(fake->doc().location(),
1970 exampleFiles,
1971 exampleDirs,
1972 proFileName,
1973 userFriendlyFilePath);
1974 if (fullPath.isEmpty()) {
1975 fake->doc().location().warning(
1976 tr("Cannot find file '%1' or '%2'").arg(tmp).arg(proFileName));
1977 return;
1978 }
1979 }
1980
1981 int sizeOfBoringPartOfName = fullPath.size() - proFileName.size();
1982 fullPath.truncate(fullPath.lastIndexOf('/'));
1983
1984 QStringList exampleFiles = Config::getFilesHere(fullPath,
1985 exampleNameFilter);
1986 if (!exampleFiles.isEmpty()) {
1987 // move main.cpp and to the end, if it exists
1988 QString mainCpp;
1989 QMutableStringListIterator i(exampleFiles);
1990 i.toBack();
1991 while (i.hasPrevious()) {
1992 QString fileName = i.previous();
1993 if (fileName.endsWith("/main.cpp")) {
1994 mainCpp = fileName;
1995 i.remove();
1996 }
1997 else if (fileName.contains("/qrc_") || fileName.contains("/moc_")
1998 || fileName.contains("/ui_"))
1999 i.remove();
2000 }
2001 if (!mainCpp.isEmpty())
2002 exampleFiles.append(mainCpp);
2003
2004 // add any qmake Qt resource files and qmake project files
2005 exampleFiles += Config::getFilesHere(fullPath, "*.qrc *.pro");
2006 }
2007
2008 foreach (const QString &exampleFile, exampleFiles)
2009 (void) new FakeNode(fake,
2010 exampleFile.mid(sizeOfBoringPartOfName),
2011 FakeNode::File);
2012}
2013
2014QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.