1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the tools applications of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
52 #include "codechunk.h"
54 #include "cppcodeparser.h"
55 #include "tokenizer.h"
60 /* qmake ignore Q_OBJECT */
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")
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")
100 QStringList
CppCodeParser::exampleFiles
;
101 QStringList
CppCodeParser::exampleDirs
;
103 static void extractPageLinkAndDesc(const QString
&arg
,
107 QRegExp
bracedRegExp("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?");
109 if (bracedRegExp
.exactMatch(arg
)) {
110 *link
= bracedRegExp
.cap(1);
111 *desc
= bracedRegExp
.cap(2);
116 int spaceAt
= arg
.indexOf(" ");
117 if (arg
.contains(".html") && spaceAt
!= -1) {
118 *link
= arg
.left(spaceAt
).trimmed();
119 *desc
= arg
.mid(spaceAt
).trimmed();
128 static void setLink(Node
*node
, Node::LinkType linkType
, const QString
&arg
)
132 extractPageLinkAndDesc(arg
, &link
, &desc
);
133 node
->setLink(linkType
, link
, desc
);
137 This is used for fuzzy matching only, which in turn is only used
140 static QString
cleanType(const QString
&type
, const Tree
*tree
)
142 QString result
= type
;
143 result
.replace("qlonglong", "long long");
144 result
.replace("qulonglong", "unsigned long long");
145 result
.replace("qreal", "double");
146 result
.replace(QRegExp("\\bu(int|short|char|long)\\b"), "unsigned \\1");
147 result
.replace("QRgb", "unsigned int");
148 result
.replace(" >", ">");
149 result
.remove(" const[]");
150 result
.replace("QStringList<QString>", "QStringList");
151 result
.replace("qint8", "char");
152 result
.replace("qint16", "short");
153 result
.replace("qint32", "int");
154 result
.replace("qint64", "long long");
155 result
.replace("quint8", "unsigned char");
156 result
.replace("quint16", "unsigned short");
157 result
.replace("quint32", "unsigned int");
158 result
.replace("quint64", "unsigned long long");
160 if (result
.contains("QFlags")) {
161 QRegExp
regExp("QFlags<(((?:[^<>]+::)*)([^<>:]+))>");
163 while ((pos
= result
.indexOf(regExp
, pos
)) != -1) {
164 // we assume that the path for the associated enum
165 // is the same as for the flag typedef
166 QStringList path
= regExp
.cap(2).split("::",
167 QString::SkipEmptyParts
);
168 const EnumNode
*enume
= static_cast<const EnumNode
*>(
169 tree
->findNode(QStringList(path
) << regExp
.cap(3),
171 if (enume
&& enume
->flagsType())
172 result
.replace(pos
, regExp
.matchedLength(),
173 (QStringList(path
) << enume
->flagsType()->name()).join("::"));
177 if (result
.contains("::")) {
178 // remove needless (and needful) class prefixes
179 QRegExp
regExp("[A-Za-z0-9_]+::");
180 result
.replace(regExp
, "");
186 The constructor initializes some regular expressions
189 CppCodeParser::CppCodeParser()
190 : varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
196 The destructor is trivial.
198 CppCodeParser::~CppCodeParser()
204 The constructor initializes a map of special node types
205 for identifying important nodes. And it initializes
206 some filters for identifying certain kinds of files.
208 void CppCodeParser::initializeParser(const Config
&config
)
210 CodeParser::initializeParser(config
);
212 nodeTypeMap
.insert(COMMAND_NAMESPACE
, Node::Namespace
);
213 nodeTypeMap
.insert(COMMAND_CLASS
, Node::Class
);
214 nodeTypeMap
.insert(COMMAND_SERVICE
, Node::Class
);
215 nodeTypeMap
.insert(COMMAND_ENUM
, Node::Enum
);
216 nodeTypeMap
.insert(COMMAND_TYPEDEF
, Node::Typedef
);
217 nodeTypeMap
.insert(COMMAND_PROPERTY
, Node::Property
);
218 nodeTypeMap
.insert(COMMAND_VARIABLE
, Node::Variable
);
220 exampleFiles
= config
.getStringList(CONFIG_EXAMPLES
);
221 exampleDirs
= config
.getStringList(CONFIG_EXAMPLEDIRS
);
222 QStringList exampleFilePatterns
= config
.getStringList(
223 CONFIG_EXAMPLES
+ Config::dot
+ CONFIG_FILEEXTENSIONS
);
225 if (!exampleFilePatterns
.isEmpty())
226 exampleNameFilter
= exampleFilePatterns
.join(" ");
228 exampleNameFilter
= "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
230 QStringList exampleImagePatterns
= config
.getStringList(
231 CONFIG_EXAMPLES
+ Config::dot
+ CONFIG_IMAGEEXTENSIONS
);
233 if (!exampleImagePatterns
.isEmpty())
234 exampleImageFilter
= exampleImagePatterns
.join(" ");
236 exampleImageFilter
= "*.png";
240 Clear the map of common node types and call
241 the same function in the base class.
243 void CppCodeParser::terminateParser()
246 CodeParser::terminateParser();
252 QString
CppCodeParser::language()
258 Returns a list of extensions for header files.
260 QString
CppCodeParser::headerFileNameFilter()
262 return "*.ch *.h *.h++ *.hh *.hpp *.hxx";
266 Returns a list of extensions for source files, i.e. not
269 QString
CppCodeParser::sourceFileNameFilter()
271 return "*.c++ *.cc *.cpp *.cxx";
275 Parse the C++ header file identified by \a filePath
276 and add the parsed contents to the big \a tree. The
277 \a location is used for reporting errors.
279 void CppCodeParser::parseHeaderFile(const Location
& location
,
280 const QString
& filePath
,
283 FILE *in
= fopen(QFile::encodeName(filePath
), "r");
285 location
.error(tr("Cannot open C++ header file '%1'").arg(filePath
));
290 Location
fileLocation(filePath
);
291 Tokenizer
fileTokenizer(fileLocation
, in
);
292 tokenizer
= &fileTokenizer
;
294 matchDeclList(tree
->root());
295 if (!fileTokenizer
.version().isEmpty())
296 tree
->setVersion(fileTokenizer
.version());
299 if (fileLocation
.fileName() == "qiterator.h")
300 parseQiteratorDotH(location
, filePath
);
304 Get ready to parse the C++ cpp file identified by \a filePath
305 and add its parsed contents to the big \a tree. \a location is
306 used for reporting errors.
308 Call matchDocsAndStuff() to do all the parsing and tree building.
310 void CppCodeParser::parseSourceFile(const Location
& location
,
311 const QString
& filePath
,
314 FILE *in
= fopen(QFile::encodeName(filePath
), "r");
316 location
.error(tr("Cannot open C++ source file '%1' (%2)").arg(filePath
).arg(strerror(errno
)));
321 Location
fileLocation(filePath
);
322 Tokenizer
fileTokenizer(fileLocation
, in
);
323 tokenizer
= &fileTokenizer
;
325 usedNamespaces
.clear();
331 This is called after all the header files have been parsed.
332 I think the most important thing it does is resolve class
333 inheritance links in the tree. But it also initializes a
336 void CppCodeParser::doneParsingHeaderFiles(Tree
*tree
)
338 tree
->resolveInheritance();
340 QMapIterator
<QString
, QString
> i(sequentialIteratorClasses
);
341 while (i
.hasNext()) {
343 instantiateIteratorMacro(i
.key(),
345 sequentialIteratorDefinition
,
348 i
= mutableSequentialIteratorClasses
;
349 while (i
.hasNext()) {
351 instantiateIteratorMacro(i
.key(),
353 mutableSequentialIteratorDefinition
,
356 i
= associativeIteratorClasses
;
357 while (i
.hasNext()) {
359 instantiateIteratorMacro(i
.key(),
361 associativeIteratorDefinition
,
364 i
= mutableAssociativeIteratorClasses
;
365 while (i
.hasNext()) {
367 instantiateIteratorMacro(i
.key(),
369 mutableAssociativeIteratorDefinition
,
372 sequentialIteratorDefinition
.clear();
373 mutableSequentialIteratorDefinition
.clear();
374 associativeIteratorDefinition
.clear();
375 mutableAssociativeIteratorDefinition
.clear();
376 sequentialIteratorClasses
.clear();
377 mutableSequentialIteratorClasses
.clear();
378 associativeIteratorClasses
.clear();
379 mutableAssociativeIteratorClasses
.clear();
383 This is called after all the source files (i.e., not the
384 header files) have been parsed. It traverses the tree to
385 resolve property links, normalize overload signatures, and
386 do other housekeeping of the tree.
388 void CppCodeParser::doneParsingSourceFiles(Tree
*tree
)
390 tree
->root()->makeUndocumentedChildrenInternal();
391 tree
->root()->normalizeOverloads();
392 tree
->fixInheritance();
393 tree
->resolveProperties();
397 This function searches the \a tree to find a FunctionNode
398 for a function with the signature \a synopsis. If the
399 \a relative node is provided, the search begins there. If
400 \a fuzzy is true, base classes are searched. The function
401 node is returned, if found.
403 const FunctionNode
*CppCodeParser::findFunctionNode(const QString
& synopsis
,
408 QStringList parentPath
;
410 FunctionNode
*func
= 0;
411 int flags
= fuzzy
? int(Tree::SearchBaseClasses
) : 0;
414 if (makeFunctionNode(synopsis
, &parentPath
, &clone
)) {
415 func
= tree
->findFunctionNode(parentPath
, clone
, relative
, flags
);
418 This is necessary because Roberto's parser resolves typedefs.
420 if (!func
&& fuzzy
) {
421 func
= tre
->findFunctionNode(parentPath
+
422 QStringList(clone
->name()),
425 if (!func
&& clone
->name().contains('_')) {
426 QStringList path
= parentPath
;
427 path
<< clone
->name().split('_');
428 func
= tre
->findFunctionNode(path
, relative
, flags
);
432 NodeList overloads
= func
->parent()->overloads(func
->name());
434 for (int i
= 0; i
< overloads
.count(); ++i
) {
435 FunctionNode
*overload
= static_cast<FunctionNode
*>(overloads
.at(i
));
436 if (overload
->status() != Node::Compat
437 && overload
->parameters().count() == clone
->parameters().count()
438 && !overload
->isConst() == !clone
->isConst())
439 candidates
<< overload
;
441 if (candidates
.count() == 0)
445 There's only one function with the correct number
446 of parameters. That must be the one.
448 if (candidates
.count() == 1)
449 return static_cast<FunctionNode
*>(candidates
.first());
451 overloads
= candidates
;
453 for (int i
= 0; i
< overloads
.count(); ++i
) {
454 FunctionNode
*overload
= static_cast<FunctionNode
*>(overloads
.at(i
));
455 QList
<Parameter
> params1
= overload
->parameters();
456 QList
<Parameter
> params2
= clone
->parameters();
459 for (j
= 0; j
< params1
.count(); ++j
) {
460 if (!params2
.at(j
).name().startsWith(params1
.at(j
).name()))
463 if (j
== params1
.count())
464 candidates
<< overload
;
468 There are several functions with the correct
469 parameter count, but only one has the correct
472 if (candidates
.count() == 1)
473 return static_cast<FunctionNode
*>(candidates
.first());
476 for (int i
= 0; i
< overloads
.count(); ++i
) {
477 FunctionNode
*overload
= static_cast<FunctionNode
*>(overloads
.at(i
));
478 QList
<Parameter
> params1
= overload
->parameters();
479 QList
<Parameter
> params2
= clone
->parameters();
482 for (j
= 0; j
< params1
.count(); ++j
) {
483 if (params1
.at(j
).rightType() != params2
.at(j
).rightType())
486 if (cleanType(params1
.at(j
).leftType(), tree
)
487 != cleanType(params2
.at(j
).leftType(), tree
))
490 if (j
== params1
.count())
491 candidates
<< overload
;
496 There are several functions with the correct
497 parameter count, but only one has the correct
498 types, loosely compared.
500 if (candidates
.count() == 1)
501 return static_cast<FunctionNode
*>(candidates
.first());
512 Returns the set of strings reopresenting the topic commands.
514 QSet
<QString
> CppCodeParser::topicCommands()
516 return QSet
<QString
>() << COMMAND_CLASS
519 << COMMAND_EXTERNALPAGE
523 << COMMAND_HEADERFILE
534 << COMMAND_QMLPROPERTY
535 << COMMAND_QMLATTACHEDPROPERTY
537 << COMMAND_QMLATTACHEDSIGNAL
539 << COMMAND_QMLATTACHEDMETHOD
;
546 Process the topic \a command in context \a doc with argument \a arg.
548 Node
*CppCodeParser::processTopicCommand(const Doc
& doc
,
549 const QString
& command
,
552 if (command
== COMMAND_FN
) {
553 QStringList parentPath
;
554 FunctionNode
*func
= 0;
555 FunctionNode
*clone
= 0;
557 if (!makeFunctionNode(arg
, &parentPath
, &clone
) &&
558 !makeFunctionNode("void " + arg
, &parentPath
, &clone
)) {
559 doc
.location().warning(tr("Invalid syntax in '\\%1'")
563 if (!usedNamespaces
.isEmpty()) {
564 foreach (const QString
&usedNamespace
, usedNamespaces
) {
565 QStringList newPath
= usedNamespace
.split("::") + parentPath
;
566 func
= tre
->findFunctionNode(newPath
, clone
);
571 // Search the root namespace if no match was found.
573 func
= tre
->findFunctionNode(parentPath
, clone
);
576 if (parentPath
.isEmpty() && !lastPath
.isEmpty())
577 func
= tre
->findFunctionNode(lastPath
, clone
);
579 doc
.location().warning(tr("Cannot find '%1' in '\\%2'")
580 .arg(clone
->name() + "(...)")
582 tr("I cannot find any function of that name with the "
583 "specified signature. Make sure that the signature "
584 "is identical to the declaration, including 'const' "
588 doc
.location().warning(tr("Missing '%1::' for '%2' in '\\%3'")
589 .arg(lastPath
.join("::"))
590 .arg(clone
->name() + "()")
595 lastPath
= parentPath
;
598 func
->borrowParameterNames(clone
);
599 func
->setParentPath(clone
->parentPath());
605 else if (command
== COMMAND_MACRO
) {
606 QStringList parentPath
;
607 FunctionNode
*func
= 0;
609 if (makeFunctionNode(arg
, &parentPath
, &func
, tre
->root())) {
610 if (!parentPath
.isEmpty()) {
611 doc
.location().warning(tr("Invalid syntax in '\\%1'")
612 .arg(COMMAND_MACRO
));
617 func
->setMetaness(FunctionNode::MacroWithParams
);
618 QList
<Parameter
> params
= func
->parameters();
619 for (int i
= 0; i
< params
.size(); ++i
) {
620 Parameter
¶m
= params
[i
];
621 if (param
.name().isEmpty() && !param
.leftType().isEmpty()
622 && param
.leftType() != "...")
623 param
= Parameter("", "", param
.leftType());
625 func
->setParameters(params
);
629 else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg
)) {
630 func
= new FunctionNode(tre
->root(), arg
);
631 func
->setAccess(Node::Public
);
632 func
->setLocation(doc
.location());
633 func
->setMetaness(FunctionNode::MacroWithoutParams
);
636 doc
.location().warning(tr("Invalid syntax in '\\%1'")
637 .arg(COMMAND_MACRO
));
642 else if (nodeTypeMap
.contains(command
)) {
644 The command was neither "fn" nor "macro" .
646 // ### split(" ") hack is there to support header file syntax
647 QStringList paths
= arg
.split(" ");
648 QStringList path
= paths
[0].split("::");
650 if (!usedNamespaces
.isEmpty()) {
651 foreach (const QString
&usedNamespace
, usedNamespaces
) {
652 QStringList newPath
= usedNamespace
.split("::") + path
;
653 node
= tre
->findNode(newPath
, nodeTypeMap
[command
]);
660 // Search the root namespace if no match was found.
662 node
= tre
->findNode(path
, nodeTypeMap
[command
]);
665 doc
.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
666 .arg(arg
).arg(command
));
670 else if (command
== COMMAND_SERVICE
) {
671 // If the command is "\service", then we need to tag the
672 // class with the actual service name.
673 QStringList args
= arg
.split(" ");
674 if (args
.size() > 1) {
675 ClassNode
*cnode
= static_cast<ClassNode
*>(node
);
676 cnode
->setServiceName(args
[1]);
677 cnode
->setHideFromMainList(true);
680 else if (node
->isInnerNode()) {
681 if (path
.size() > 1) {
683 usedNamespaces
.insert(path
.join("::"));
687 if (command
== COMMAND_CLASS
) {
688 if (paths
.size() > 1) {
689 if (!paths
[1].endsWith(".h")) {
690 ClassNode
*cnode
= static_cast<ClassNode
*>(node
);
691 cnode
->setQmlElement(paths
[1]);
697 else if (command
== COMMAND_EXAMPLE
) {
698 FakeNode
*fake
= new FakeNode(tre
->root(), arg
, Node::Example
);
699 createExampleFileNodes(fake
);
702 else if (command
== COMMAND_EXTERNALPAGE
) {
703 return new FakeNode(tre
->root(), arg
, Node::ExternalPage
);
705 else if (command
== COMMAND_FILE
) {
706 return new FakeNode(tre
->root(), arg
, Node::File
);
708 else if (command
== COMMAND_GROUP
) {
709 return new FakeNode(tre
->root(), arg
, Node::Group
);
711 else if (command
== COMMAND_HEADERFILE
) {
712 return new FakeNode(tre
->root(), arg
, Node::HeaderFile
);
714 else if (command
== COMMAND_MODULE
) {
715 return new FakeNode(tre
->root(), arg
, Node::Module
);
717 else if (command
== COMMAND_PAGE
) {
718 return new FakeNode(tre
->root(), arg
, Node::Page
);
721 else if (command
== COMMAND_QMLCLASS
) {
722 const ClassNode
* classNode
= 0;
723 QStringList names
= arg
.split(" ");
724 if (names
.size() > 1) {
725 Node
* n
= tre
->findNode(names
[1].split("::"),Node::Class
);
727 classNode
= static_cast<const ClassNode
*>(n
);
729 return new QmlClassNode(tre
->root(), names
[0], classNode
);
731 else if ((command
== COMMAND_QMLSIGNAL
) ||
732 (command
== COMMAND_QMLMETHOD
) ||
733 (command
== COMMAND_QMLATTACHEDSIGNAL
) ||
734 (command
== COMMAND_QMLATTACHEDMETHOD
)) {
737 QmlClassNode
* qmlClass
= 0;
738 if (splitQmlMethodArg(doc
,arg
,type
,element
)) {
739 Node
* n
= tre
->findNode(QStringList(element
),Node::Fake
);
740 if (n
&& n
->subType() == Node::QmlClass
) {
741 qmlClass
= static_cast<QmlClassNode
*>(n
);
742 if (command
== COMMAND_QMLSIGNAL
)
743 return makeFunctionNode(doc
,arg
,qmlClass
,Node::QmlSignal
,false,COMMAND_QMLSIGNAL
);
744 else if (command
== COMMAND_QMLATTACHEDSIGNAL
)
745 return makeFunctionNode(doc
,arg
,qmlClass
,Node::QmlSignal
,true,COMMAND_QMLATTACHEDSIGNAL
);
746 else if (command
== COMMAND_QMLMETHOD
)
747 return makeFunctionNode(doc
,arg
,qmlClass
,Node::QmlMethod
,false,COMMAND_QMLMETHOD
);
748 else if (command
== COMMAND_QMLATTACHEDMETHOD
)
749 return makeFunctionNode(doc
,arg
,qmlClass
,Node::QmlMethod
,true,COMMAND_QMLATTACHEDMETHOD
);
751 return 0; // never get here.
762 A QML property argument has the form...
764 <type> <element>::<name>
766 This function splits the argument into those three
767 parts, sets \a type, \a element, and \a name,
768 and returns true. If any of the parts isn't found,
769 a qdoc warning is output and false is returned.
771 bool CppCodeParser::splitQmlPropertyArg(const Doc
& doc
,
777 QStringList blankSplit
= arg
.split(" ");
778 if (blankSplit
.size() > 1) {
779 type
= blankSplit
[0];
780 QStringList
colonSplit(blankSplit
[1].split("::"));
781 if (colonSplit
.size() > 1) {
782 element
= colonSplit
[0];
783 name
= colonSplit
[1];
787 doc
.location().warning(tr("Missing parent QML element name"));
790 doc
.location().warning(tr("Missing property type"));
795 A QML signal or method argument has the form...
797 <type> <element>::<name>(<param>, <param>, ...)
799 This function splits the argument into those two
800 parts, sets \a element, and \a name, and returns
801 true. If either of the parts isn't found, a debug
802 message is output and false is returned.
804 bool CppCodeParser::splitQmlMethodArg(const Doc
& doc
,
809 QStringList
colonSplit(arg
.split("::"));
810 if (colonSplit
.size() > 1) {
811 QStringList blankSplit
= colonSplit
[0].split(" ");
812 if (blankSplit
.size() > 1) {
813 type
= blankSplit
[0];
814 element
= blankSplit
[1];
818 element
= colonSplit
[0];
823 doc
.location().warning(tr("Missing parent QML element or method signature"));
828 Process the topic \a command group with arguments \a args.
830 Currently, this function is called only for \e{qmlproperty}
831 and \e{qmlattachedproperty}.
833 Node
*CppCodeParser::processTopicCommandGroup(const Doc
& doc
,
834 const QString
& command
,
835 const QStringList
& args
)
837 QmlPropGroupNode
* qmlPropGroup
= 0;
838 if ((command
== COMMAND_QMLPROPERTY
) ||
839 (command
== COMMAND_QMLATTACHEDPROPERTY
)) {
843 bool attached
= (command
== COMMAND_QMLATTACHEDPROPERTY
);
844 QStringList::ConstIterator arg
= args
.begin();
845 if (splitQmlPropertyArg(doc
,(*arg
),type
,element
,property
)) {
846 Node
* n
= tre
->findNode(QStringList(element
),Node::Fake
);
847 if (n
&& n
->subType() == Node::QmlClass
) {
848 QmlClassNode
* qmlClass
= static_cast<QmlClassNode
*>(n
);
850 qmlPropGroup
= new QmlPropGroupNode(qmlClass
,
856 const ClassNode
*correspondingClass
= static_cast<const QmlClassNode
*>(qmlPropGroup
->parent())->classNode();
857 PropertyNode
*correspondingProperty
= 0;
858 if (correspondingClass
)
859 correspondingProperty
= static_cast<PropertyNode
*>((Node
*)correspondingClass
->findNode(property
, Node::Property
));
860 QmlPropertyNode
*qmlPropNode
= new QmlPropertyNode(qmlPropGroup
,property
,type
,attached
);
861 if (correspondingProperty
) {
862 bool writableList
= type
.startsWith("list") && correspondingProperty
->dataType().endsWith('*');
863 qmlPropNode
->setWritable(writableList
|| correspondingProperty
->isWritable());
866 while (arg
!= args
.end()) {
867 if (splitQmlPropertyArg(doc
,(*arg
),type
,element
,property
)) {
868 QmlPropertyNode
* qmlPropNode
= new QmlPropertyNode(qmlPropGroup
,
872 if (correspondingProperty
) {
873 bool writableList
= type
.startsWith("list") && correspondingProperty
->dataType().endsWith('*');
874 qmlPropNode
->setWritable(writableList
|| correspondingProperty
->isWritable());
886 Returns the set of strings representing the common metacommands
887 plus some other metacommands.
889 QSet
<QString
> CppCodeParser::otherMetaCommands()
891 return commonMetaCommands() << COMMAND_INHEADERFILE
895 << COMMAND_CONTENTSPAGE
897 << COMMAND_PREVIOUSPAGE
901 << COMMAND_QMLINHERITS
902 << COMMAND_QMLDEFAULT
;
904 << COMMAND_STARTPAGE
;
909 Process the metacommand \a command in the context of the
910 \a node associated with the topic command and the \a doc.
911 \a arg is the argument to the metacommand.
913 void CppCodeParser::processOtherMetaCommand(const Doc
& doc
,
914 const QString
& command
,
918 if (command
== COMMAND_INHEADERFILE
) {
919 if (node
!= 0 && node
->isInnerNode()) {
920 ((InnerNode
*) node
)->addInclude(arg
);
923 doc
.location().warning(tr("Ignored '\\%1'")
924 .arg(COMMAND_INHEADERFILE
));
927 else if (command
== COMMAND_OVERLOAD
) {
928 if (node
!= 0 && node
->type() == Node::Function
) {
929 ((FunctionNode
*) node
)->setOverload(true);
932 doc
.location().warning(tr("Ignored '\\%1'")
933 .arg(COMMAND_OVERLOAD
));
936 else if (command
== COMMAND_REIMP
) {
937 if (node
!= 0 && node
->type() == Node::Function
) {
938 FunctionNode
*func
= (FunctionNode
*) node
;
939 const FunctionNode
*from
= func
->reimplementedFrom();
941 doc
.location().warning(
942 tr("Cannot find base function for '\\%1' in %2()")
943 .arg(COMMAND_REIMP
).arg(node
->name()),
944 tr("The function either doesn't exist in any base class "
945 "with the same signature or it exists but isn't virtual."));
948 Ideally, we would enable this check to warn whenever
949 \reimp is used incorrectly, and only make the node
950 internal if the function is a reimplementation of
951 another function in a base class.
953 else if (from
->access() == Node::Private
954 || from
->parent()->access() == Node::Private
) {
955 doc
.location().warning(tr("'\\%1' in %2() should be '\\internal' because its base function is private or internal")
956 .arg(COMMAND_REIMP
).arg(node
->name()));
960 // Reimplemented functions now reported in separate sections.
962 Note: Setting the access to Private hides the documentation,
963 but setting the status to Internal makes the node available
964 in the XML output when the WebXMLGenerator is used.
966 func
->setAccess(Node::Private
);
967 func
->setStatus(Node::Internal
);
969 func
->setReimp(true);
972 doc
.location().warning(tr("Ignored '\\%1' in %2")
977 else if (command
== COMMAND_RELATES
) {
978 InnerNode
*pseudoParent
;
979 if (arg
.startsWith("<") || arg
.startsWith("\"")) {
981 static_cast<InnerNode
*>(tre
->findNode(QStringList(arg
),
985 QStringList newPath
= arg
.split("::");
987 static_cast<InnerNode
*>(tre
->findNode(QStringList(newPath
),
991 static_cast<InnerNode
*>(tre
->findNode(QStringList(newPath
),
995 doc
.location().warning(tr("Cannot find '%1' in '\\%2'")
996 .arg(arg
).arg(COMMAND_RELATES
));
999 node
->setRelates(pseudoParent
);
1002 else if (command
== COMMAND_CONTENTSPAGE
) {
1003 setLink(node
, Node::ContentsLink
, arg
);
1005 else if (command
== COMMAND_NEXTPAGE
) {
1006 setLink(node
, Node::NextLink
, arg
);
1008 else if (command
== COMMAND_PREVIOUSPAGE
) {
1009 setLink(node
, Node::PreviousLink
, arg
);
1011 else if (command
== COMMAND_INDEXPAGE
) {
1012 setLink(node
, Node::IndexLink
, arg
);
1014 else if (command
== COMMAND_STARTPAGE
) {
1015 setLink(node
, Node::StartLink
, arg
);
1018 else if (command
== COMMAND_QMLINHERITS
) {
1019 setLink(node
, Node::InheritsLink
, arg
);
1021 else if (command
== COMMAND_QMLDEFAULT
) {
1022 QmlPropGroupNode
* qpgn
= static_cast<QmlPropGroupNode
*>(node
);
1027 processCommonMetaCommand(doc
.location(),command
,arg
,node
,tre
);
1032 The topic command has been processed resulting in the \a doc
1033 and \a node passed in here. Process the other meta commands,
1034 which are found in \a doc, in the context of the topic \a node.
1036 void CppCodeParser::processOtherMetaCommands(const Doc
& doc
, Node
*node
)
1038 const QSet
<QString
> metaCommands
= doc
.metaCommandsUsed();
1039 QSet
<QString
>::ConstIterator cmd
= metaCommands
.begin();
1040 while (cmd
!= metaCommands
.end()) {
1041 QStringList args
= doc
.metaCommandArgs(*cmd
);
1042 QStringList::ConstIterator arg
= args
.begin();
1043 while (arg
!= args
.end()) {
1044 processOtherMetaCommand(doc
, *cmd
, *arg
, node
);
1052 Resets the C++ code parser to its default initialized state.
1054 void CppCodeParser::reset(Tree
*tree
)
1059 access
= Node::Public
;
1060 metaness
= FunctionNode::Plain
;
1066 Get the next token from the file being parsed and store it
1067 in the token variable.
1069 void CppCodeParser::readToken()
1071 tok
= tokenizer
->getToken();
1075 Return the current location in the file being parsed,
1076 i.e. the file name, line number, and column number.
1078 const Location
& CppCodeParser::location()
1080 return tokenizer
->location();
1084 Return the previous string read from the file being parsed.
1086 QString
CppCodeParser::previousLexeme()
1088 return tokenizer
->previousLexeme();
1092 Return the current string string from the file being parsed.
1094 QString
CppCodeParser::lexeme()
1096 return tokenizer
->lexeme();
1099 bool CppCodeParser::match(int target
)
1101 if (tok
== target
) {
1110 If the current token is one of the keyword thingees that
1111 are used in Qt, skip over it to the next token and return
1112 true. Otherwise just return false without reading the
1115 bool CppCodeParser::matchCompat()
1119 case Tok_QT_COMPAT_CONSTRUCTOR
:
1120 case Tok_QT_DEPRECATED
:
1121 case Tok_QT_MOC_COMPAT
:
1122 case Tok_QT3_SUPPORT
:
1123 case Tok_QT3_SUPPORT_CONSTRUCTOR
:
1124 case Tok_QT3_MOC_SUPPORT
:
1132 bool CppCodeParser::matchTemplateAngles(CodeChunk
*dataType
)
1134 bool matches
= (tok
== Tok_LeftAngle
);
1136 int leftAngleDepth
= 0;
1137 int parenAndBraceDepth
= 0;
1139 if (tok
== Tok_LeftAngle
) {
1142 else if (tok
== Tok_RightAngle
) {
1145 else if (tok
== Tok_LeftParen
|| tok
== Tok_LeftBrace
) {
1146 ++parenAndBraceDepth
;
1148 else if (tok
== Tok_RightParen
|| tok
== Tok_RightBrace
) {
1149 if (--parenAndBraceDepth
< 0)
1154 dataType
->append(lexeme());
1156 } while (leftAngleDepth
> 0 && tok
!= Tok_Eoi
);
1161 bool CppCodeParser::matchTemplateHeader()
1164 return matchTemplateAngles();
1167 bool CppCodeParser::matchDataType(CodeChunk
*dataType
, QString
*var
)
1170 This code is really hard to follow... sorry. The loop is there to match
1171 Alpha::Beta::Gamma::...::Omega.
1176 if (tok
!= Tok_Ident
) {
1178 There is special processing for 'Foo::operator int()'
1179 and such elsewhere. This is the only case where we
1180 return something with a trailing gulbrandsen ('Foo::').
1182 if (tok
== Tok_operator
)
1186 People may write 'const unsigned short' or
1187 'short unsigned const' or any other permutation.
1189 while (match(Tok_const
) || match(Tok_volatile
))
1190 dataType
->append(previousLexeme());
1191 while (match(Tok_signed
) || match(Tok_unsigned
) ||
1192 match(Tok_short
) || match(Tok_long
) || match(Tok_int64
)) {
1193 dataType
->append(previousLexeme());
1196 while (match(Tok_const
) || match(Tok_volatile
))
1197 dataType
->append(previousLexeme());
1199 if (match(Tok_Tilde
))
1200 dataType
->append(previousLexeme());
1204 if (match(Tok_Ident
))
1205 dataType
->append(previousLexeme());
1206 else if (match(Tok_void
) || match(Tok_int
) || match(Tok_char
) ||
1207 match(Tok_double
) || match(Tok_Ellipsis
))
1208 dataType
->append(previousLexeme());
1212 else if (match(Tok_int
) || match(Tok_char
) || match(Tok_double
)) {
1213 dataType
->append(previousLexeme());
1216 matchTemplateAngles(dataType
);
1218 while (match(Tok_const
) || match(Tok_volatile
))
1219 dataType
->append(previousLexeme());
1221 if (match(Tok_Gulbrandsen
))
1222 dataType
->append(previousLexeme());
1227 while (match(Tok_Ampersand
) || match(Tok_Aster
) || match(Tok_const
) ||
1229 dataType
->append(previousLexeme());
1231 if (match(Tok_LeftParenAster
)) {
1233 A function pointer. This would be rather hard to handle without a
1234 tokenizer hack, because a type can be followed with a left parenthesis
1235 in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
1238 dataType
->append(previousLexeme());
1239 dataType
->appendHotspot();
1240 if (var
!= 0 && match(Tok_Ident
))
1241 *var
= previousLexeme();
1242 if (!match(Tok_RightParen
) || tok
!= Tok_LeftParen
)
1244 dataType
->append(previousLexeme());
1246 int parenDepth0
= tokenizer
->parenDepth();
1247 while (tokenizer
->parenDepth() >= parenDepth0
&& tok
!= Tok_Eoi
) {
1248 dataType
->append(lexeme());
1251 if (match(Tok_RightParen
))
1252 dataType
->append(previousLexeme());
1256 The common case: Look for an optional identifier, then for
1257 some array brackets.
1259 dataType
->appendHotspot();
1262 if (match(Tok_Ident
)) {
1263 *var
= previousLexeme();
1265 else if (match(Tok_Comment
)) {
1267 A neat hack: Commented-out parameter names are
1268 recognized by qdoc. It's impossible to illustrate
1269 here inside a C-style comment, because it requires
1270 an asterslash. It's also impossible to illustrate
1271 inside a C++-style comment, because the explanation
1272 does not fit on one line.
1274 if (varComment
.exactMatch(previousLexeme()))
1275 *var
= varComment
.cap(1);
1279 if (tok
== Tok_LeftBracket
) {
1280 int bracketDepth0
= tokenizer
->bracketDepth();
1281 while ((tokenizer
->bracketDepth() >= bracketDepth0
&&
1283 tok
== Tok_RightBracket
) {
1284 dataType
->append(lexeme());
1292 bool CppCodeParser::matchParameter(FunctionNode
*func
)
1296 CodeChunk defaultValue
;
1298 if (!matchDataType(&dataType
, &name
))
1301 if (match(Tok_Equal
)) {
1302 int parenDepth0
= tokenizer
->parenDepth();
1304 while (tokenizer
->parenDepth() >= parenDepth0
&&
1305 (tok
!= Tok_Comma
||
1306 tokenizer
->parenDepth() > parenDepth0
) &&
1308 defaultValue
.append(lexeme());
1312 func
->addParameter(Parameter(dataType
.toString(),
1315 defaultValue
.toString())); // ###
1319 bool CppCodeParser::matchFunctionDecl(InnerNode
*parent
,
1320 QStringList
*parentPathPtr
,
1321 FunctionNode
**funcPtr
,
1322 const QString
&templateStuff
,
1326 CodeChunk returnType
;
1327 QStringList parentPath
;
1330 bool compat
= false;
1332 if (match(Tok_friend
))
1334 match(Tok_explicit
);
1338 if (match(Tok_static
)) {
1343 FunctionNode::Virtualness vir
= FunctionNode::NonVirtual
;
1344 if (match(Tok_virtual
)) {
1345 vir
= FunctionNode::ImpureVirtual
;
1350 if (!matchDataType(&returnType
)) {
1351 if (tokenizer
->parsingFnOrMacro()
1352 && (match(Tok_Q_DECLARE_FLAGS
) || match(Tok_Q_PROPERTY
)))
1353 returnType
= CodeChunk(previousLexeme());
1359 if (returnType
.toString() == "QBool")
1360 returnType
= CodeChunk("bool");
1365 if (tok
== Tok_operator
&&
1366 (returnType
.toString().isEmpty() ||
1367 returnType
.toString().endsWith("::"))) {
1368 // 'QString::operator const char *()'
1369 parentPath
= returnType
.toString().split(sep
);
1370 parentPath
.removeAll(QString());
1371 returnType
= CodeChunk();
1374 CodeChunk restOfName
;
1375 if (tok
!= Tok_Tilde
&& matchDataType(&restOfName
)) {
1376 name
= "operator " + restOfName
.toString();
1379 name
= previousLexeme() + lexeme();
1381 while (tok
!= Tok_LeftParen
&& tok
!= Tok_Eoi
) {
1386 if (tok
!= Tok_LeftParen
) {
1390 else if (tok
== Tok_LeftParen
) {
1391 // constructor or destructor
1392 parentPath
= returnType
.toString().split(sep
);
1393 if (!parentPath
.isEmpty()) {
1394 name
= parentPath
.last();
1395 parentPath
.erase(parentPath
.end() - 1);
1397 returnType
= CodeChunk();
1400 while (match(Tok_Ident
)) {
1401 name
= previousLexeme();
1402 matchTemplateAngles();
1404 if (match(Tok_Gulbrandsen
))
1405 parentPath
.append(name
);
1410 if (tok
== Tok_operator
) {
1413 while (tok
!= Tok_Eoi
) {
1416 if (tok
== Tok_LeftParen
)
1420 if (parent
&& (tok
== Tok_Semicolon
||
1421 tok
== Tok_LeftBracket
||
1423 && access
!= Node::Private
) {
1424 if (tok
== Tok_LeftBracket
) {
1425 returnType
.appendHotspot();
1427 int bracketDepth0
= tokenizer
->bracketDepth();
1428 while ((tokenizer
->bracketDepth() >= bracketDepth0
&&
1430 tok
== Tok_RightBracket
) {
1431 returnType
.append(lexeme());
1434 if (tok
!= Tok_Semicolon
) {
1438 else if (tok
== Tok_Colon
) {
1439 returnType
.appendHotspot();
1441 while (tok
!= Tok_Semicolon
&& tok
!= Tok_Eoi
) {
1442 returnType
.append(lexeme());
1445 if (tok
!= Tok_Semicolon
) {
1450 VariableNode
*var
= new VariableNode(parent
, name
);
1451 var
->setAccess(access
);
1452 var
->setLocation(location());
1453 var
->setLeftType(returnType
.left());
1454 var
->setRightType(returnType
.right());
1456 var
->setStatus(Node::Compat
);
1457 var
->setStatic(sta
);
1460 if (tok
!= Tok_LeftParen
) {
1466 FunctionNode
*func
= new FunctionNode(type
, parent
, name
, attached
);
1467 func
->setAccess(access
);
1468 func
->setLocation(location());
1469 func
->setReturnType(returnType
.toString());
1470 func
->setParentPath(parentPath
);
1471 func
->setTemplateStuff(templateStuff
);
1473 func
->setStatus(Node::Compat
);
1475 func
->setMetaness(metaness
);
1477 if (name
== parent
->name()) {
1478 func
->setMetaness(FunctionNode::Ctor
);
1479 } else if (name
.startsWith("~")) {
1480 func
->setMetaness(FunctionNode::Dtor
);
1483 func
->setStatic(sta
);
1485 if (tok
!= Tok_RightParen
) {
1487 if (!matchParameter(func
)) {
1490 } while (match(Tok_Comma
));
1492 if (!match(Tok_RightParen
)) {
1496 func
->setConst(match(Tok_const
));
1498 if (match(Tok_Equal
) && match(Tok_Number
))
1499 vir
= FunctionNode::PureVirtual
;
1500 func
->setVirtualness(vir
);
1502 if (match(Tok_Colon
)) {
1503 while (tok
!= Tok_LeftBrace
&& tok
!= Tok_Eoi
)
1507 if (!match(Tok_Semicolon
) && tok
!= Tok_Eoi
) {
1508 int braceDepth0
= tokenizer
->braceDepth();
1510 if (!match(Tok_LeftBrace
)) {
1513 while (tokenizer
->braceDepth() >= braceDepth0
&& tok
!= Tok_Eoi
)
1515 match(Tok_RightBrace
);
1517 if (parentPathPtr
!= 0)
1518 *parentPathPtr
= parentPath
;
1524 bool CppCodeParser::matchBaseSpecifier(ClassNode
*classe
, bool isClass
)
1526 Node::Access access
;
1530 access
= Node::Public
;
1534 access
= Node::Protected
;
1538 access
= Node::Private
;
1542 access
= isClass
? Node::Private
: Node::Public
;
1545 if (tok
== Tok_virtual
)
1548 CodeChunk baseClass
;
1549 if (!matchDataType(&baseClass
))
1552 tre
->addBaseClass(classe
,
1555 baseClass
.toString(),
1560 bool CppCodeParser::matchBaseList(ClassNode
*classe
, bool isClass
)
1563 if (!matchBaseSpecifier(classe
, isClass
))
1565 if (tok
== Tok_LeftBrace
)
1567 if (!match(Tok_Comma
))
1573 Parse a C++ class, union, or struct declarion.
1575 bool CppCodeParser::matchClassDecl(InnerNode
*parent
,
1576 const QString
&templateStuff
)
1578 bool isClass
= (tok
== Tok_class
);
1581 bool compat
= matchCompat();
1583 if (tok
!= Tok_Ident
)
1585 while (tok
== Tok_Ident
)
1587 if (tok
!= Tok_Colon
&& tok
!= Tok_LeftBrace
)
1591 So far, so good. We have 'class Foo {' or 'class Foo :'.
1592 This is enough to recognize a class definition.
1594 ClassNode
*classe
= new ClassNode(parent
, previousLexeme());
1595 classe
->setAccess(access
);
1596 classe
->setLocation(location());
1598 classe
->setStatus(Node::Compat
);
1599 if (!moduleName
.isEmpty())
1600 classe
->setModuleName(moduleName
);
1601 classe
->setTemplateStuff(templateStuff
);
1603 if (match(Tok_Colon
) && !matchBaseList(classe
, isClass
))
1605 if (!match(Tok_LeftBrace
))
1608 Node::Access outerAccess
= access
;
1609 access
= isClass
? Node::Private
: Node::Public
;
1610 FunctionNode::Metaness outerMetaness
= metaness
;
1611 metaness
= FunctionNode::Plain
;
1613 bool matches
= (matchDeclList(classe
) && match(Tok_RightBrace
) &&
1614 match(Tok_Semicolon
));
1615 access
= outerAccess
;
1616 metaness
= outerMetaness
;
1620 bool CppCodeParser::matchNamespaceDecl(InnerNode
*parent
)
1622 readToken(); // skip 'namespace'
1623 if (tok
!= Tok_Ident
)
1625 while (tok
== Tok_Ident
)
1627 if (tok
!= Tok_LeftBrace
)
1631 So far, so good. We have 'namespace Foo {'.
1633 QString namespaceName
= previousLexeme();
1634 NamespaceNode
*namespasse
= 0;
1636 namespasse
= static_cast<NamespaceNode
*>(parent
->findNode(namespaceName
, Node::Namespace
));
1638 namespasse
= new NamespaceNode(parent
, namespaceName
);
1639 namespasse
->setAccess(access
);
1640 namespasse
->setLocation(location());
1643 readToken(); // skip '{'
1644 bool matched
= matchDeclList(namespasse
);
1646 return matched
&& match(Tok_RightBrace
);
1649 bool CppCodeParser::matchUsingDecl()
1651 readToken(); // skip 'using'
1654 if (tok
!= Tok_namespace
)
1659 if (tok
!= Tok_Ident
)
1663 while (tok
== Tok_Ident
) {
1666 if (tok
== Tok_Semicolon
)
1668 else if (tok
!= Tok_Gulbrandsen
)
1675 So far, so good. We have 'using namespace Foo;'.
1677 usedNamespaces
.insert(name
);
1681 bool CppCodeParser::matchEnumItem(InnerNode
*parent
, EnumNode
*enume
)
1683 if (!match(Tok_Ident
))
1686 QString name
= previousLexeme();
1689 if (match(Tok_Equal
)) {
1690 while (tok
!= Tok_Comma
&& tok
!= Tok_RightBrace
&&
1692 val
.append(lexeme());
1698 QString strVal
= val
.toString();
1699 if (strVal
.isEmpty()) {
1700 if (enume
->items().isEmpty()) {
1704 QString last
= enume
->items().last().value();
1706 int n
= last
.toInt(&ok
);
1708 if (last
.startsWith("0") && last
.size() > 1) {
1709 if (last
.startsWith("0x") || last
.startsWith("0X"))
1710 strVal
= last
.left(2) + QString::number(n
+ 1, 16);
1712 strVal
= "0" + QString::number(n
+ 1, 8);
1715 strVal
= QString::number(n
+ 1);
1720 enume
->addItem(EnumItem(name
, strVal
));
1723 VariableNode
*var
= new VariableNode(parent
, name
);
1724 var
->setAccess(access
);
1725 var
->setLocation(location());
1726 var
->setLeftType("const int");
1727 var
->setStatic(true);
1732 bool CppCodeParser::matchEnumDecl(InnerNode
*parent
)
1736 if (!match(Tok_enum
))
1738 if (match(Tok_Ident
))
1739 name
= previousLexeme();
1740 if (tok
!= Tok_LeftBrace
)
1743 EnumNode
*enume
= 0;
1745 if (!name
.isEmpty()) {
1746 enume
= new EnumNode(parent
, name
);
1747 enume
->setAccess(access
);
1748 enume
->setLocation(location());
1753 if (!matchEnumItem(parent
, enume
))
1756 while (match(Tok_Comma
)) {
1757 if (!matchEnumItem(parent
, enume
))
1760 return match(Tok_RightBrace
) && match(Tok_Semicolon
);
1763 bool CppCodeParser::matchTypedefDecl(InnerNode
*parent
)
1768 if (!match(Tok_typedef
))
1770 if (!matchDataType(&dataType
, &name
))
1772 if (!match(Tok_Semicolon
))
1775 if (parent
&& !parent
->findNode(name
, Node::Typedef
)) {
1776 TypedefNode
*typedeffe
= new TypedefNode(parent
, name
);
1777 typedeffe
->setAccess(access
);
1778 typedeffe
->setLocation(location());
1783 bool CppCodeParser::matchProperty(InnerNode
*parent
)
1785 if (!match(Tok_Q_PROPERTY
) &&
1786 !match(Tok_Q_OVERRIDE
) &&
1787 !match(Tok_QDOC_PROPERTY
))
1789 if (!match(Tok_LeftParen
))
1794 if (!matchDataType(&dataType
, &name
))
1797 PropertyNode
*property
= new PropertyNode(parent
, name
);
1798 property
->setAccess(Node::Public
);
1799 property
->setLocation(location());
1800 property
->setDataType(dataType
.toString());
1802 while (tok
!= Tok_RightParen
&& tok
!= Tok_Eoi
) {
1803 if (!match(Tok_Ident
))
1805 QString key
= previousLexeme();
1808 if (match(Tok_Ident
)) {
1809 value
= previousLexeme();
1811 else if (match(Tok_LeftParen
)) {
1813 while (tok
!= Tok_Eoi
) {
1814 if (tok
== Tok_LeftParen
) {
1817 } else if (tok
== Tok_RightParen
) {
1829 tre
->addPropertyFunction(property
, value
, PropertyNode::Getter
);
1830 else if (key
== "WRITE") {
1831 tre
->addPropertyFunction(property
, value
, PropertyNode::Setter
);
1832 property
->setWritable(true);
1833 } else if (key
== "STORED")
1834 property
->setStored(value
.toLower() == "true");
1835 else if (key
== "DESIGNABLE")
1836 property
->setDesignable(value
.toLower() == "true");
1837 else if (key
== "RESET")
1838 tre
->addPropertyFunction(property
, value
, PropertyNode::Resetter
);
1839 else if (key
== "NOTIFY") {
1840 tre
->addPropertyFunction(property
, value
, PropertyNode::Notifier
);
1844 match(Tok_RightParen
);
1849 Parse a C++ declaration.
1851 bool CppCodeParser::matchDeclList(InnerNode
*parent
)
1853 QString templateStuff
;
1854 int braceDepth0
= tokenizer
->braceDepth();
1855 if (tok
== Tok_RightBrace
) // prevents failure on empty body
1858 while (tokenizer
->braceDepth() >= braceDepth0
&& tok
!= Tok_Eoi
) {
1866 matchClassDecl(parent
, templateStuff
);
1869 matchNamespaceDecl(parent
);
1875 templateStuff
= matchTemplateHeader();
1878 matchEnumDecl(parent
);
1881 matchTypedefDecl(parent
);
1885 access
= Node::Private
;
1886 metaness
= FunctionNode::Plain
;
1890 access
= Node::Protected
;
1891 metaness
= FunctionNode::Plain
;
1895 access
= Node::Public
;
1896 metaness
= FunctionNode::Plain
;
1901 access
= Node::Public
;
1902 metaness
= FunctionNode::Signal
;
1907 metaness
= FunctionNode::Slot
;
1912 case Tok_Q_OVERRIDE
:
1913 case Tok_Q_PROPERTY
:
1914 case Tok_QDOC_PROPERTY
:
1915 matchProperty(parent
);
1917 case Tok_Q_DECLARE_SEQUENTIAL_ITERATOR
:
1919 if (match(Tok_LeftParen
) && match(Tok_Ident
))
1920 sequentialIteratorClasses
.insert(previousLexeme(),
1921 location().fileName());
1922 match(Tok_RightParen
);
1924 case Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR
:
1926 if (match(Tok_LeftParen
) && match(Tok_Ident
))
1927 mutableSequentialIteratorClasses
.insert(previousLexeme(),
1928 location().fileName());
1929 match(Tok_RightParen
);
1931 case Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR
:
1933 if (match(Tok_LeftParen
) && match(Tok_Ident
))
1934 associativeIteratorClasses
.insert(previousLexeme(),
1935 location().fileName());
1936 match(Tok_RightParen
);
1938 case Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR
:
1940 if (match(Tok_LeftParen
) && match(Tok_Ident
))
1941 mutableAssociativeIteratorClasses
.insert(previousLexeme(),
1942 location().fileName());
1943 match(Tok_RightParen
);
1945 case Tok_Q_DECLARE_FLAGS
:
1947 if (match(Tok_LeftParen
) && match(Tok_Ident
)) {
1948 QString flagsType
= previousLexeme();
1949 if (match(Tok_Comma
) && match(Tok_Ident
)) {
1950 QString enumType
= previousLexeme();
1951 TypedefNode
*flagsNode
= new TypedefNode(parent
, flagsType
);
1952 flagsNode
->setAccess(access
);
1953 flagsNode
->setLocation(location());
1954 EnumNode
*enumNode
=
1955 static_cast<EnumNode
*>(parent
->findNode(enumType
,
1958 enumNode
->setFlagsType(flagsNode
);
1961 match(Tok_RightParen
);
1965 if (match(Tok_LeftParen
) && match(Tok_Ident
))
1966 moduleName
= previousLexeme();
1967 if (!moduleName
.startsWith("Qt"))
1968 moduleName
.prepend("Qt");
1969 match(Tok_RightParen
);
1972 if (!matchFunctionDecl(parent
, 0, 0, templateStuff
)) {
1973 while (tok
!= Tok_Eoi
&&
1974 (tokenizer
->braceDepth() > braceDepth0
||
1975 (!match(Tok_Semicolon
) &&
1976 tok
!= Tok_public
&& tok
!= Tok_protected
&&
1977 tok
!= Tok_private
)))
1981 templateStuff
.clear();
1987 This is called by parseSourceFile() to do the actual parsing
1990 bool CppCodeParser::matchDocsAndStuff()
1992 QSet
<QString
> topicCommandsAllowed
= topicCommands();
1993 QSet
<QString
> otherMetacommandsAllowed
= otherMetaCommands();
1994 QSet
<QString
> metacommandsAllowed
= topicCommandsAllowed
+
1995 otherMetacommandsAllowed
;
1997 while (tok
!= Tok_Eoi
) {
1998 if (tok
== Tok_Doc
) {
2000 lexeme() returns an entire qdoc comment.
2002 QString comment
= lexeme();
2003 Location
start_loc(location());
2006 Doc::trimCStyleComment(start_loc
,comment
);
2007 Location
end_loc(location());
2010 Doc parses the comment.
2012 Doc
doc(start_loc
,end_loc
,comment
,metacommandsAllowed
);
2017 QSet
<QString
> topicCommandsUsed
= topicCommandsAllowed
&
2018 doc
.metaCommandsUsed();
2021 There should be one topic command in the set,
2022 or none. If the set is empty, then the comment
2023 should be a function description.
2025 if (topicCommandsUsed
.count() > 0) {
2026 topic
= *topicCommandsUsed
.begin();
2027 args
= doc
.metaCommandArgs(topic
);
2033 if (topic
.isEmpty()) {
2034 QStringList parentPath
;
2035 FunctionNode
*clone
;
2036 FunctionNode
*func
= 0;
2038 if (matchFunctionDecl(0, &parentPath
, &clone
)) {
2039 foreach (const QString
&usedNamespace
, usedNamespaces
) {
2040 QStringList newPath
= usedNamespace
.split("::") + parentPath
;
2041 func
= tre
->findFunctionNode(newPath
, clone
);
2046 func
= tre
->findFunctionNode(parentPath
, clone
);
2049 func
->borrowParameterNames(clone
);
2056 doc
.location().warning(
2057 tr("Cannot tie this documentation to anything"),
2058 tr("I found a /*! ... */ comment, but there was no "
2059 "topic command (e.g., '\\%1', '\\%2') in the "
2060 "comment and no function definition following "
2062 .arg(COMMAND_FN
).arg(COMMAND_PAGE
));
2067 There is a topic command. Process it.
2070 if ((topic
== COMMAND_QMLPROPERTY
) ||
2071 (topic
== COMMAND_QMLATTACHEDPROPERTY
)) {
2073 Node
*node
= processTopicCommandGroup(nodeDoc
,topic
,args
);
2076 docs
.append(nodeDoc
);
2080 QStringList::ConstIterator a
= args
.begin();
2081 while (a
!= args
.end()) {
2083 Node
*node
= processTopicCommand(nodeDoc
,topic
,*a
);
2086 docs
.append(nodeDoc
);
2092 QStringList::ConstIterator a
= args
.begin();
2093 while (a
!= args
.end()) {
2095 Node
*node
= processTopicCommand(nodeDoc
, topic
, *a
);
2098 docs
.append(nodeDoc
);
2105 NodeList::Iterator n
= nodes
.begin();
2106 QList
<Doc
>::Iterator d
= docs
.begin();
2107 while (n
!= nodes
.end()) {
2108 processOtherMetaCommands(*d
, *n
);
2110 if ((*n
)->isInnerNode() &&
2111 ((InnerNode
*)*n
)->includes().isEmpty()) {
2112 InnerNode
*m
= static_cast<InnerNode
*>(*n
);
2113 while (m
->parent() != tre
->root())
2116 ((InnerNode
*)*n
)->addInclude((*n
)->name());
2118 ((InnerNode
*)*n
)->setIncludes(m
->includes());
2124 else if (tok
== Tok_using
) {
2128 QStringList parentPath
;
2129 FunctionNode
*clone
;
2130 FunctionNode
*node
= 0;
2132 if (matchFunctionDecl(0, &parentPath
, &clone
)) {
2134 The location of the definition is more interesting
2135 than that of the declaration. People equipped with
2136 a sophisticated text editor can respond to warnings
2137 concerning undocumented functions very quickly.
2139 Signals are implemented in uninteresting files
2142 node
= tre
->findFunctionNode(parentPath
, clone
);
2143 if (node
!= 0 && node
->metaness() != FunctionNode::Signal
)
2144 node
->setLocation(clone
->location());
2156 bool CppCodeParser::makeFunctionNode(const QString
& synopsis
,
2157 QStringList
*parentPathPtr
,
2158 FunctionNode
**funcPtr
,
2163 Tokenizer
*outerTokenizer
= tokenizer
;
2167 QByteArray latin1
= synopsis
.toLatin1();
2168 Tokenizer
stringTokenizer(loc
, latin1
);
2169 stringTokenizer
.setParsingFnOrMacro(true);
2170 tokenizer
= &stringTokenizer
;
2173 bool ok
= matchFunctionDecl(root
, parentPathPtr
, funcPtr
, QString(), type
, attached
);
2174 // potential memory leak with funcPtr
2176 tokenizer
= outerTokenizer
;
2182 Create a new FunctionNode for a QML method or signal, as
2183 specified by \a type, as a child of \a parent. \a sig is
2184 the complete signature, and if \a attached is true, the
2185 method or signal is "attached". \a qdoctag is the text of
2188 FunctionNode
* CppCodeParser::makeFunctionNode(const Doc
& doc
,
2196 FunctionNode
* fn
= 0;
2197 if (!makeFunctionNode(sig
,&pp
,&fn
,parent
,type
,attached
) &&
2198 !makeFunctionNode("void "+sig
,&pp
,&fn
,parent
,type
,attached
)) {
2199 doc
.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag
));
2206 void CppCodeParser::parseQiteratorDotH(const Location
&location
,
2207 const QString
&filePath
)
2209 QFile
file(filePath
);
2210 if (!file
.open(QFile::ReadOnly
))
2213 QString text
= file
.readAll();
2215 text
.replace("\\\n", "");
2216 QStringList lines
= text
.split("\n");
2217 lines
= lines
.filter("Q_DECLARE");
2218 lines
.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), "");
2220 if (lines
.size() == 4) {
2221 sequentialIteratorDefinition
= lines
[0];
2222 mutableSequentialIteratorDefinition
= lines
[1];
2223 associativeIteratorDefinition
= lines
[2];
2224 mutableAssociativeIteratorDefinition
= lines
[3];
2227 location
.warning(tr("The qiterator.h hack failed"));
2231 void CppCodeParser::instantiateIteratorMacro(const QString
&container
,
2232 const QString
&includeFile
,
2233 const QString
¯oDef
,
2236 QString resultingCode
= macroDef
;
2237 resultingCode
.replace(QRegExp("\\bC\\b"), container
);
2238 resultingCode
.replace(QRegExp("\\s*##\\s*"), "");
2240 Location
loc(includeFile
); // hack to get the include file for free
2241 QByteArray latin1
= resultingCode
.toLatin1();
2242 Tokenizer
stringTokenizer(loc
, latin1
);
2243 tokenizer
= &stringTokenizer
;
2245 matchDeclList(tre
->root());
2248 void CppCodeParser::createExampleFileNodes(FakeNode
*fake
)
2250 QString examplePath
= fake
->name();
2252 // we can assume that this file always exists
2253 QString proFileName
= examplePath
+ "/" +
2254 examplePath
.split("/").last() + ".pro";
2256 QString userFriendlyFilePath
;
2257 QString fullPath
= Config::findFile(fake
->doc().location(),
2261 userFriendlyFilePath
);
2263 if (fullPath
.isEmpty()) {
2264 QString tmp
= proFileName
;
2265 proFileName
= examplePath
+ "/" + "qbuild.pro";
2266 userFriendlyFilePath
.clear();
2267 fullPath
= Config::findFile(fake
->doc().location(),
2271 userFriendlyFilePath
);
2272 if (fullPath
.isEmpty()) {
2273 fake
->doc().location().warning(
2274 tr("Cannot find file '%1' or '%2'").arg(tmp
).arg(proFileName
));
2279 int sizeOfBoringPartOfName
= fullPath
.size() - proFileName
.size();
2280 fullPath
.truncate(fullPath
.lastIndexOf('/'));
2282 QStringList exampleFiles
= Config::getFilesHere(fullPath
,exampleNameFilter
);
2283 QString imagesPath
= fullPath
+ "/images";
2284 QStringList imageFiles
= Config::getFilesHere(imagesPath
,exampleImageFilter
);
2287 qDebug() << "examplePath:" << examplePath
;
2288 qDebug() << " exampleFiles" << exampleFiles
;
2289 qDebug() << "imagesPath:" << imagesPath
;
2290 qDebug() << "fullPath:" << fullPath
;
2291 qDebug() << " imageFiles" << imageFiles
;
2294 if (!exampleFiles
.isEmpty()) {
2295 // move main.cpp and to the end, if it exists
2297 QMutableStringListIterator
i(exampleFiles
);
2299 while (i
.hasPrevious()) {
2300 QString fileName
= i
.previous();
2301 if (fileName
.endsWith("/main.cpp")) {
2305 else if (fileName
.contains("/qrc_") || fileName
.contains("/moc_")
2306 || fileName
.contains("/ui_"))
2309 if (!mainCpp
.isEmpty())
2310 exampleFiles
.append(mainCpp
);
2312 // add any qmake Qt resource files and qmake project files
2313 exampleFiles
+= Config::getFilesHere(fullPath
, "*.qrc *.pro");
2316 foreach (const QString
&exampleFile
, exampleFiles
)
2317 (void) new FakeNode(fake
,
2318 exampleFile
.mid(sizeOfBoringPartOfName
),
2320 foreach (const QString
&imageFile
, imageFiles
) {
2322 imageFile
.mid(sizeOfBoringPartOfName
),