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

Last change on this file since 1054 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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