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

Last change on this file since 814 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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