Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.