/****************************************************************************+ −
**+ −
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the tools applications of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, this file may be used under the terms of the GNU Lesser+ −
** General Public License version 2.1 as published by the Free Software+ −
** Foundation and appearing in the file LICENSE.LGPL included in the+ −
** packaging of this file. Please review the following information to+ −
** ensure the GNU Lesser General Public License version 2.1 requirements+ −
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.+ −
**+ −
** In addition, as a special exception, Nokia gives you certain additional+ −
** rights. These rights are described in the Nokia Qt LGPL Exception+ −
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
+ −
/*+ −
cppcodeparser.cpp+ −
*/+ −
+ −
#include <QtCore>+ −
#include <qfile.h>+ −
+ −
#include <stdio.h>+ −
#include <errno.h>+ −
+ −
#include "codechunk.h"+ −
#include "config.h"+ −
#include "cppcodeparser.h"+ −
#include "tokenizer.h"+ −
#include "tree.h"+ −
+ −
QT_BEGIN_NAMESPACE+ −
+ −
/* qmake ignore Q_OBJECT */+ −
+ −
#define COMMAND_CLASS Doc::alias("class")+ −
#define COMMAND_CONTENTSPAGE Doc::alias("contentspage")+ −
#define COMMAND_ENUM Doc::alias("enum")+ −
#define COMMAND_EXAMPLE Doc::alias("example")+ −
#define COMMAND_EXTERNALPAGE Doc::alias("externalpage")+ −
#define COMMAND_FILE Doc::alias("file") // ### don't document+ −
#define COMMAND_FN Doc::alias("fn")+ −
#define COMMAND_GROUP Doc::alias("group")+ −
#define COMMAND_HEADERFILE Doc::alias("headerfile")+ −
#define COMMAND_INDEXPAGE Doc::alias("indexpage")+ −
#define COMMAND_INHEADERFILE Doc::alias("inheaderfile") // ### don't document+ −
#define COMMAND_MACRO Doc::alias("macro")+ −
#define COMMAND_MODULE Doc::alias("module") // ### don't document+ −
#define COMMAND_NAMESPACE Doc::alias("namespace")+ −
#define COMMAND_OVERLOAD Doc::alias("overload")+ −
#define COMMAND_NEXTPAGE Doc::alias("nextpage")+ −
#define COMMAND_PAGE Doc::alias("page")+ −
#define COMMAND_PREVIOUSPAGE Doc::alias("previouspage")+ −
#define COMMAND_PROPERTY Doc::alias("property")+ −
#define COMMAND_REIMP Doc::alias("reimp")+ −
#define COMMAND_RELATES Doc::alias("relates")+ −
#define COMMAND_SERVICE Doc::alias("service")+ −
#define COMMAND_STARTPAGE Doc::alias("startpage")+ −
#define COMMAND_TYPEDEF Doc::alias("typedef")+ −
#define COMMAND_VARIABLE Doc::alias("variable")+ −
+ −
#ifdef QDOC_QML+ −
#define COMMAND_QMLCLASS Doc::alias("qmlclass")+ −
#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty")+ −
#define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty")+ −
#define COMMAND_QMLINHERITS Doc::alias("inherits")+ −
#define COMMAND_QMLSIGNAL Doc::alias("qmlsignal")+ −
#define COMMAND_QMLATTACHEDSIGNAL Doc::alias("qmlattachedsignal")+ −
#define COMMAND_QMLMETHOD Doc::alias("qmlmethod")+ −
#define COMMAND_QMLATTACHEDMETHOD Doc::alias("qmlattachedmethod")+ −
#define COMMAND_QMLDEFAULT Doc::alias("default")+ −
#endif+ −
+ −
QStringList CppCodeParser::exampleFiles;+ −
QStringList CppCodeParser::exampleDirs;+ −
+ −
static void extractPageLinkAndDesc(const QString &arg,+ −
QString *link,+ −
QString *desc)+ −
{+ −
QRegExp bracedRegExp("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?");+ −
+ −
if (bracedRegExp.exactMatch(arg)) {+ −
*link = bracedRegExp.cap(1);+ −
*desc = bracedRegExp.cap(2);+ −
if (desc->isEmpty())+ −
*desc = *link;+ −
}+ −
else {+ −
int spaceAt = arg.indexOf(" ");+ −
if (arg.contains(".html") && spaceAt != -1) {+ −
*link = arg.left(spaceAt).trimmed();+ −
*desc = arg.mid(spaceAt).trimmed();+ −
}+ −
else {+ −
*link = arg;+ −
*desc = arg;+ −
}+ −
}+ −
}+ −
+ −
static void setLink(Node *node, Node::LinkType linkType, const QString &arg)+ −
{+ −
QString link;+ −
QString desc;+ −
extractPageLinkAndDesc(arg, &link, &desc);+ −
node->setLink(linkType, link, desc);+ −
}+ −
+ −
/*+ −
This is used for fuzzy matching only, which in turn is only used+ −
for Qt Jambi.+ −
*/+ −
static QString cleanType(const QString &type, const Tree *tree)+ −
{+ −
QString result = type;+ −
result.replace("qlonglong", "long long");+ −
result.replace("qulonglong", "unsigned long long");+ −
result.replace("qreal", "double");+ −
result.replace(QRegExp("\\bu(int|short|char|long)\\b"), "unsigned \\1");+ −
result.replace("QRgb", "unsigned int");+ −
result.replace(" >", ">");+ −
result.remove(" const[]");+ −
result.replace("QStringList<QString>", "QStringList");+ −
result.replace("qint8", "char");+ −
result.replace("qint16", "short");+ −
result.replace("qint32", "int");+ −
result.replace("qint64", "long long");+ −
result.replace("quint8", "unsigned char");+ −
result.replace("quint16", "unsigned short");+ −
result.replace("quint32", "unsigned int");+ −
result.replace("quint64", "unsigned long long");+ −
+ −
if (result.contains("QFlags")) {+ −
QRegExp regExp("QFlags<(((?:[^<>]+::)*)([^<>:]+))>");+ −
int pos = 0;+ −
while ((pos = result.indexOf(regExp, pos)) != -1) {+ −
// we assume that the path for the associated enum+ −
// is the same as for the flag typedef+ −
QStringList path = regExp.cap(2).split("::",+ −
QString::SkipEmptyParts);+ −
const EnumNode *enume = static_cast<const EnumNode *>(+ −
tree->findNode(QStringList(path) << regExp.cap(3),+ −
Node::Enum));+ −
if (enume && enume->flagsType())+ −
result.replace(pos, regExp.matchedLength(),+ −
(QStringList(path) << enume->flagsType()->name()).join("::"));+ −
++pos;+ −
}+ −
}+ −
if (result.contains("::")) {+ −
// remove needless (and needful) class prefixes+ −
QRegExp regExp("[A-Za-z0-9_]+::");+ −
result.replace(regExp, "");+ −
}+ −
return result;+ −
}+ −
+ −
/*!+ −
The constructor initializes some regular expressions+ −
and calls reset().+ −
*/+ −
CppCodeParser::CppCodeParser()+ −
: varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")+ −
{+ −
reset(0);+ −
}+ −
+ −
/*!+ −
The destructor is trivial.+ −
*/+ −
CppCodeParser::~CppCodeParser()+ −
{+ −
// nothing.+ −
}+ −
+ −
/*!+ −
The constructor initializes a map of special node types+ −
for identifying important nodes. And it initializes+ −
some filters for identifying certain kinds of files.+ −
*/+ −
void CppCodeParser::initializeParser(const Config &config)+ −
{+ −
CodeParser::initializeParser(config);+ −
+ −
nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);+ −
nodeTypeMap.insert(COMMAND_CLASS, Node::Class);+ −
nodeTypeMap.insert(COMMAND_SERVICE, Node::Class);+ −
nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);+ −
nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);+ −
nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);+ −
nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);+ −
+ −
exampleFiles = config.getStringList(CONFIG_EXAMPLES);+ −
exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);+ −
QStringList exampleFilePatterns = config.getStringList(+ −
CONFIG_EXAMPLES + Config::dot + CONFIG_FILEEXTENSIONS);+ −
+ −
if (!exampleFilePatterns.isEmpty())+ −
exampleNameFilter = exampleFilePatterns.join(" ");+ −
else+ −
exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";+ −
+ −
QStringList exampleImagePatterns = config.getStringList(+ −
CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);+ −
+ −
if (!exampleImagePatterns.isEmpty())+ −
exampleImageFilter = exampleImagePatterns.join(" ");+ −
else+ −
exampleImageFilter = "*.png";+ −
}+ −
+ −
/*!+ −
Clear the map of common node types and call+ −
the same function in the base class.+ −
*/+ −
void CppCodeParser::terminateParser()+ −
{+ −
nodeTypeMap.clear();+ −
CodeParser::terminateParser();+ −
}+ −
+ −
/*!+ −
Returns "Cpp".+ −
*/+ −
QString CppCodeParser::language()+ −
{+ −
return "Cpp";+ −
}+ −
+ −
/*!+ −
Returns a list of extensions for header files.+ −
*/+ −
QString CppCodeParser::headerFileNameFilter()+ −
{+ −
return "*.ch *.h *.h++ *.hh *.hpp *.hxx";+ −
}+ −
+ −
/*!+ −
Returns a list of extensions for source files, i.e. not+ −
header files.+ −
*/+ −
QString CppCodeParser::sourceFileNameFilter()+ −
{+ −
return "*.c++ *.cc *.cpp *.cxx";+ −
}+ −
+ −
/*!+ −
Parse the C++ header file identified by \a filePath+ −
and add the parsed contents to the big \a tree. The+ −
\a location is used for reporting errors.+ −
*/+ −
void CppCodeParser::parseHeaderFile(const Location& location,+ −
const QString& filePath,+ −
Tree *tree)+ −
{+ −
FILE *in = fopen(QFile::encodeName(filePath), "r");+ −
if (!in) {+ −
location.error(tr("Cannot open C++ header file '%1'").arg(filePath));+ −
return;+ −
}+ −
+ −
reset(tree);+ −
Location fileLocation(filePath);+ −
Tokenizer fileTokenizer(fileLocation, in);+ −
tokenizer = &fileTokenizer;+ −
readToken();+ −
matchDeclList(tree->root());+ −
if (!fileTokenizer.version().isEmpty())+ −
tree->setVersion(fileTokenizer.version());+ −
fclose(in);+ −
+ −
if (fileLocation.fileName() == "qiterator.h")+ −
parseQiteratorDotH(location, filePath);+ −
}+ −
+ −
/*!+ −
Get ready to parse the C++ cpp file identified by \a filePath+ −
and add its parsed contents to the big \a tree. \a location is+ −
used for reporting errors.+ −
+ −
Call matchDocsAndStuff() to do all the parsing and tree building.+ −
*/+ −
void CppCodeParser::parseSourceFile(const Location& location,+ −
const QString& filePath,+ −
Tree *tree)+ −
{+ −
FILE *in = fopen(QFile::encodeName(filePath), "r");+ −
if (!in) {+ −
location.error(tr("Cannot open C++ source file '%1' (%2)").arg(filePath).arg(strerror(errno)));+ −
return;+ −
}+ −
+ −
reset(tree);+ −
Location fileLocation(filePath);+ −
Tokenizer fileTokenizer(fileLocation, in);+ −
tokenizer = &fileTokenizer;+ −
readToken();+ −
usedNamespaces.clear();+ −
matchDocsAndStuff();+ −
fclose(in);+ −
}+ −
+ −
/*!+ −
This is called after all the header files have been parsed.+ −
I think the most important thing it does is resolve class+ −
inheritance links in the tree. But it also initializes a+ −
bunch of stuff.+ −
*/+ −
void CppCodeParser::doneParsingHeaderFiles(Tree *tree)+ −
{+ −
tree->resolveInheritance();+ −
+ −
QMapIterator<QString, QString> i(sequentialIteratorClasses);+ −
while (i.hasNext()) {+ −
i.next();+ −
instantiateIteratorMacro(i.key(),+ −
i.value(),+ −
sequentialIteratorDefinition,+ −
tree);+ −
}+ −
i = mutableSequentialIteratorClasses;+ −
while (i.hasNext()) {+ −
i.next();+ −
instantiateIteratorMacro(i.key(),+ −
i.value(),+ −
mutableSequentialIteratorDefinition,+ −
tree);+ −
}+ −
i = associativeIteratorClasses;+ −
while (i.hasNext()) {+ −
i.next();+ −
instantiateIteratorMacro(i.key(),+ −
i.value(),+ −
associativeIteratorDefinition,+ −
tree);+ −
}+ −
i = mutableAssociativeIteratorClasses;+ −
while (i.hasNext()) {+ −
i.next();+ −
instantiateIteratorMacro(i.key(),+ −
i.value(),+ −
mutableAssociativeIteratorDefinition,+ −
tree);+ −
}+ −
sequentialIteratorDefinition.clear();+ −
mutableSequentialIteratorDefinition.clear();+ −
associativeIteratorDefinition.clear();+ −
mutableAssociativeIteratorDefinition.clear();+ −
sequentialIteratorClasses.clear();+ −
mutableSequentialIteratorClasses.clear();+ −
associativeIteratorClasses.clear();+ −
mutableAssociativeIteratorClasses.clear();+ −
}+ −
+ −
/*!+ −
This is called after all the source files (i.e., not the+ −
header files) have been parsed. It traverses the tree to+ −
resolve property links, normalize overload signatures, and+ −
do other housekeeping of the tree.+ −
*/+ −
void CppCodeParser::doneParsingSourceFiles(Tree *tree)+ −
{+ −
tree->root()->makeUndocumentedChildrenInternal();+ −
tree->root()->normalizeOverloads();+ −
tree->fixInheritance();+ −
tree->resolveProperties();+ −
}+ −
+ −
/*!+ −
This function searches the \a tree to find a FunctionNode+ −
for a function with the signature \a synopsis. If the+ −
\a relative node is provided, the search begins there. If+ −
\a fuzzy is true, base classes are searched. The function+ −
node is returned, if found.+ −
*/+ −
const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis,+ −
Tree *tree,+ −
Node *relative,+ −
bool fuzzy)+ −
{+ −
QStringList parentPath;+ −
FunctionNode *clone;+ −
FunctionNode *func = 0;+ −
int flags = fuzzy ? int(Tree::SearchBaseClasses) : 0;+ −
+ −
reset(tree);+ −
if (makeFunctionNode(synopsis, &parentPath, &clone)) {+ −
func = tree->findFunctionNode(parentPath, clone, relative, flags);+ −
+ −
/*+ −
This is necessary because Roberto's parser resolves typedefs.+ −
*/+ −
if (!func && fuzzy) {+ −
func = tre->findFunctionNode(parentPath ++ −
QStringList(clone->name()),+ −
relative,+ −
flags);+ −
if (!func && clone->name().contains('_')) {+ −
QStringList path = parentPath;+ −
path << clone->name().split('_');+ −
func = tre->findFunctionNode(path, relative, flags);+ −
}+ −
+ −
if (func) {+ −
NodeList overloads = func->parent()->overloads(func->name());+ −
NodeList candidates;+ −
for (int i = 0; i < overloads.count(); ++i) {+ −
FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));+ −
if (overload->status() != Node::Compat+ −
&& overload->parameters().count() == clone->parameters().count()+ −
&& !overload->isConst() == !clone->isConst())+ −
candidates << overload;+ −
}+ −
if (candidates.count() == 0)+ −
return 0;+ −
+ −
/*+ −
There's only one function with the correct number+ −
of parameters. That must be the one.+ −
*/+ −
if (candidates.count() == 1)+ −
return static_cast<FunctionNode *>(candidates.first());+ −
+ −
overloads = candidates;+ −
candidates.clear();+ −
for (int i = 0; i < overloads.count(); ++i) {+ −
FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));+ −
QList<Parameter> params1 = overload->parameters();+ −
QList<Parameter> params2 = clone->parameters();+ −
+ −
int j;+ −
for (j = 0; j < params1.count(); ++j) {+ −
if (!params2.at(j).name().startsWith(params1.at(j).name()))+ −
break;+ −
}+ −
if (j == params1.count())+ −
candidates << overload;+ −
}+ −
+ −
/*+ −
There are several functions with the correct+ −
parameter count, but only one has the correct+ −
parameter names.+ −
*/+ −
if (candidates.count() == 1)+ −
return static_cast<FunctionNode *>(candidates.first());+ −
+ −
candidates.clear();+ −
for (int i = 0; i < overloads.count(); ++i) {+ −
FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i));+ −
QList<Parameter> params1 = overload->parameters();+ −
QList<Parameter> params2 = clone->parameters();+ −
+ −
int j;+ −
for (j = 0; j < params1.count(); ++j) {+ −
if (params1.at(j).rightType() != params2.at(j).rightType())+ −
break;+ −
+ −
if (cleanType(params1.at(j).leftType(), tree)+ −
!= cleanType(params2.at(j).leftType(), tree))+ −
break;+ −
}+ −
if (j == params1.count())+ −
candidates << overload;+ −
}+ −
+ −
+ −
/*+ −
There are several functions with the correct+ −
parameter count, but only one has the correct+ −
types, loosely compared.+ −
*/+ −
if (candidates.count() == 1)+ −
return static_cast<FunctionNode *>(candidates.first());+ −
+ −
return 0;+ −
}+ −
}+ −
delete clone;+ −
}+ −
return func;+ −
}+ −
+ −
/*!+ −
Returns the set of strings reopresenting the topic commands.+ −
*/+ −
QSet<QString> CppCodeParser::topicCommands()+ −
{+ −
return QSet<QString>() << COMMAND_CLASS+ −
<< COMMAND_ENUM+ −
<< COMMAND_EXAMPLE+ −
<< COMMAND_EXTERNALPAGE+ −
<< COMMAND_FILE+ −
<< COMMAND_FN+ −
<< COMMAND_GROUP+ −
<< COMMAND_HEADERFILE+ −
<< COMMAND_MACRO+ −
<< COMMAND_MODULE+ −
<< COMMAND_NAMESPACE+ −
<< COMMAND_PAGE+ −
<< COMMAND_PROPERTY+ −
<< COMMAND_SERVICE+ −
<< COMMAND_TYPEDEF+ −
#ifdef QDOC_QML+ −
<< COMMAND_VARIABLE+ −
<< COMMAND_QMLCLASS+ −
<< COMMAND_QMLPROPERTY+ −
<< COMMAND_QMLATTACHEDPROPERTY+ −
<< COMMAND_QMLSIGNAL+ −
<< COMMAND_QMLATTACHEDSIGNAL+ −
<< COMMAND_QMLMETHOD+ −
<< COMMAND_QMLATTACHEDMETHOD;+ −
#else+ −
<< COMMAND_VARIABLE;+ −
#endif+ −
}+ −
+ −
/*!+ −
Process the topic \a command in context \a doc with argument \a arg. + −
*/+ −
Node *CppCodeParser::processTopicCommand(const Doc& doc,+ −
const QString& command,+ −
const QString& arg)+ −
{+ −
if (command == COMMAND_FN) {+ −
QStringList parentPath;+ −
FunctionNode *func = 0;+ −
FunctionNode *clone = 0;+ −
+ −
if (!makeFunctionNode(arg, &parentPath, &clone) &&+ −
!makeFunctionNode("void " + arg, &parentPath, &clone)) {+ −
doc.location().warning(tr("Invalid syntax in '\\%1'")+ −
.arg(COMMAND_FN));+ −
}+ −
else {+ −
if (!usedNamespaces.isEmpty()) {+ −
foreach (const QString &usedNamespace, usedNamespaces) {+ −
QStringList newPath = usedNamespace.split("::") + parentPath;+ −
func = tre->findFunctionNode(newPath, clone);+ −
if (func)+ −
break;+ −
}+ −
}+ −
// Search the root namespace if no match was found.+ −
if (func == 0)+ −
func = tre->findFunctionNode(parentPath, clone);+ −
+ −
if (func == 0) {+ −
if (parentPath.isEmpty() && !lastPath.isEmpty())+ −
func = tre->findFunctionNode(lastPath, clone);+ −
if (func == 0) {+ −
doc.location().warning(tr("Cannot find '%1' in '\\%2'")+ −
.arg(clone->name() + "(...)")+ −
.arg(COMMAND_FN),+ −
tr("I cannot find any function of that name with the "+ −
"specified signature. Make sure that the signature "+ −
"is identical to the declaration, including 'const' "+ −
"qualifiers."));+ −
}+ −
else {+ −
doc.location().warning(tr("Missing '%1::' for '%2' in '\\%3'")+ −
.arg(lastPath.join("::"))+ −
.arg(clone->name() + "()")+ −
.arg(COMMAND_FN));+ −
}+ −
}+ −
else {+ −
lastPath = parentPath;+ −
}+ −
if (func) {+ −
func->borrowParameterNames(clone);+ −
func->setParentPath(clone->parentPath());+ −
}+ −
delete clone;+ −
}+ −
return func;+ −
}+ −
else if (command == COMMAND_MACRO) {+ −
QStringList parentPath;+ −
FunctionNode *func = 0;+ −
+ −
if (makeFunctionNode(arg, &parentPath, &func, tre->root())) {+ −
if (!parentPath.isEmpty()) {+ −
doc.location().warning(tr("Invalid syntax in '\\%1'")+ −
.arg(COMMAND_MACRO));+ −
delete func;+ −
func = 0;+ −
}+ −
else {+ −
func->setMetaness(FunctionNode::MacroWithParams);+ −
QList<Parameter> params = func->parameters();+ −
for (int i = 0; i < params.size(); ++i) {+ −
Parameter ¶m = params[i];+ −
if (param.name().isEmpty() && !param.leftType().isEmpty()+ −
&& param.leftType() != "...")+ −
param = Parameter("", "", param.leftType());+ −
}+ −
func->setParameters(params);+ −
}+ −
return func;+ −
}+ −
else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg)) {+ −
func = new FunctionNode(tre->root(), arg);+ −
func->setAccess(Node::Public);+ −
func->setLocation(doc.location());+ −
func->setMetaness(FunctionNode::MacroWithoutParams);+ −
}+ −
else {+ −
doc.location().warning(tr("Invalid syntax in '\\%1'")+ −
.arg(COMMAND_MACRO));+ −
+ −
}+ −
return func;+ −
}+ −
else if (nodeTypeMap.contains(command)) {+ −
/*+ −
The command was neither "fn" nor "macro" .+ −
*/+ −
// ### split(" ") hack is there to support header file syntax+ −
QStringList paths = arg.split(" ");+ −
QStringList path = paths[0].split("::");+ −
Node *node = 0;+ −
if (!usedNamespaces.isEmpty()) {+ −
foreach (const QString &usedNamespace, usedNamespaces) {+ −
QStringList newPath = usedNamespace.split("::") + path;+ −
node = tre->findNode(newPath, nodeTypeMap[command]);+ −
if (node) {+ −
path = newPath;+ −
break;+ −
}+ −
}+ −
}+ −
// Search the root namespace if no match was found.+ −
if (node == 0)+ −
node = tre->findNode(path, nodeTypeMap[command]);+ −
+ −
if (node == 0) {+ −
doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")+ −
.arg(arg).arg(command));+ −
lastPath = path;+ −
+ −
}+ −
else if (command == COMMAND_SERVICE) {+ −
// If the command is "\service", then we need to tag the+ −
// class with the actual service name.+ −
QStringList args = arg.split(" ");+ −
if (args.size() > 1) {+ −
ClassNode *cnode = static_cast<ClassNode *>(node);+ −
cnode->setServiceName(args[1]);+ −
cnode->setHideFromMainList(true);+ −
}+ −
}+ −
else if (node->isInnerNode()) {+ −
if (path.size() > 1) {+ −
path.pop_back();+ −
usedNamespaces.insert(path.join("::"));+ −
}+ −
}+ −
+ −
if (command == COMMAND_CLASS) {+ −
if (paths.size() > 1) {+ −
if (!paths[1].endsWith(".h")) {+ −
ClassNode*cnode = static_cast<ClassNode*>(node);+ −
cnode->setQmlElement(paths[1]);+ −
}+ −
}+ −
}+ −
return node;+ −
}+ −
else if (command == COMMAND_EXAMPLE) {+ −
FakeNode *fake = new FakeNode(tre->root(), arg, Node::Example);+ −
createExampleFileNodes(fake);+ −
return fake;+ −
}+ −
else if (command == COMMAND_EXTERNALPAGE) {+ −
return new FakeNode(tre->root(), arg, Node::ExternalPage);+ −
}+ −
else if (command == COMMAND_FILE) {+ −
return new FakeNode(tre->root(), arg, Node::File);+ −
}+ −
else if (command == COMMAND_GROUP) {+ −
return new FakeNode(tre->root(), arg, Node::Group);+ −
}+ −
else if (command == COMMAND_HEADERFILE) {+ −
return new FakeNode(tre->root(), arg, Node::HeaderFile);+ −
}+ −
else if (command == COMMAND_MODULE) {+ −
return new FakeNode(tre->root(), arg, Node::Module);+ −
}+ −
else if (command == COMMAND_PAGE) {+ −
return new FakeNode(tre->root(), arg, Node::Page);+ −
}+ −
#ifdef QDOC_QML+ −
else if (command == COMMAND_QMLCLASS) {+ −
const ClassNode* classNode = 0;+ −
QStringList names = arg.split(" ");+ −
if (names.size() > 1) {+ −
Node* n = tre->findNode(names[1].split("::"),Node::Class);+ −
if (n)+ −
classNode = static_cast<const ClassNode*>(n);+ −
}+ −
return new QmlClassNode(tre->root(), names[0], classNode);+ −
}+ −
else if ((command == COMMAND_QMLSIGNAL) ||+ −
(command == COMMAND_QMLMETHOD) ||+ −
(command == COMMAND_QMLATTACHEDSIGNAL) ||+ −
(command == COMMAND_QMLATTACHEDMETHOD)) {+ −
QString element;+ −
QString type;+ −
QmlClassNode* qmlClass = 0;+ −
if (splitQmlMethodArg(doc,arg,type,element)) {+ −
Node* n = tre->findNode(QStringList(element),Node::Fake);+ −
if (n && n->subType() == Node::QmlClass) {+ −
qmlClass = static_cast<QmlClassNode*>(n);+ −
if (command == COMMAND_QMLSIGNAL)+ −
return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,false,COMMAND_QMLSIGNAL);+ −
else if (command == COMMAND_QMLATTACHEDSIGNAL)+ −
return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,true,COMMAND_QMLATTACHEDSIGNAL);+ −
else if (command == COMMAND_QMLMETHOD)+ −
return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,false,COMMAND_QMLMETHOD);+ −
else if (command == COMMAND_QMLATTACHEDMETHOD)+ −
return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,true,COMMAND_QMLATTACHEDMETHOD);+ −
else+ −
return 0; // never get here.+ −
}+ −
}+ −
}+ −
#endif+ −
return 0;+ −
}+ −
+ −
#ifdef QDOC_QML+ −
+ −
/*!+ −
A QML property argument has the form...+ −
+ −
<type> <element>::<name>+ −
+ −
This function splits the argument into those three+ −
parts, sets \a type, \a element, and \a name,+ −
and returns true. If any of the parts isn't found,+ −
a qdoc warning is output and false is returned.+ −
*/+ −
bool CppCodeParser::splitQmlPropertyArg(const Doc& doc,+ −
const QString& arg,+ −
QString& type,+ −
QString& element,+ −
QString& name)+ −
{+ −
QStringList blankSplit = arg.split(" ");+ −
if (blankSplit.size() > 1) {+ −
type = blankSplit[0];+ −
QStringList colonSplit(blankSplit[1].split("::"));+ −
if (colonSplit.size() > 1) {+ −
element = colonSplit[0];+ −
name = colonSplit[1];+ −
return true;+ −
}+ −
else+ −
doc.location().warning(tr("Missing parent QML element name"));+ −
}+ −
else+ −
doc.location().warning(tr("Missing property type"));+ −
return false;+ −
}+ −
+ −
/*!+ −
A QML signal or method argument has the form...+ −
+ −
<type> <element>::<name>(<param>, <param>, ...)+ −
+ −
This function splits the argument into those two+ −
parts, sets \a element, and \a name, and returns+ −
true. If either of the parts isn't found, a debug+ −
message is output and false is returned.+ −
*/+ −
bool CppCodeParser::splitQmlMethodArg(const Doc& doc,+ −
const QString& arg,+ −
QString& type,+ −
QString& element)+ −
{+ −
QStringList colonSplit(arg.split("::"));+ −
if (colonSplit.size() > 1) {+ −
QStringList blankSplit = colonSplit[0].split(" ");+ −
if (blankSplit.size() > 1) {+ −
type = blankSplit[0];+ −
element = blankSplit[1];+ −
}+ −
else {+ −
type = QString("");+ −
element = colonSplit[0];+ −
}+ −
return true;+ −
}+ −
else+ −
doc.location().warning(tr("Missing parent QML element or method signature"));+ −
return false;+ −
}+ −
+ −
/*!+ −
Process the topic \a command group with arguments \a args.+ −
+ −
Currently, this function is called only for \e{qmlproperty}+ −
and \e{qmlattachedproperty}.+ −
*/+ −
Node *CppCodeParser::processTopicCommandGroup(const Doc& doc,+ −
const QString& command,+ −
const QStringList& args)+ −
{+ −
QmlPropGroupNode* qmlPropGroup = 0;+ −
if ((command == COMMAND_QMLPROPERTY) ||+ −
(command == COMMAND_QMLATTACHEDPROPERTY)) {+ −
QString type;+ −
QString element;+ −
QString property;+ −
bool attached = (command == COMMAND_QMLATTACHEDPROPERTY);+ −
QStringList::ConstIterator arg = args.begin();+ −
if (splitQmlPropertyArg(doc,(*arg),type,element,property)) {+ −
Node* n = tre->findNode(QStringList(element),Node::Fake);+ −
if (n && n->subType() == Node::QmlClass) {+ −
QmlClassNode* qmlClass = static_cast<QmlClassNode*>(n);+ −
if (qmlClass)+ −
qmlPropGroup = new QmlPropGroupNode(qmlClass,+ −
property,+ −
attached);+ −
}+ −
}+ −
if (qmlPropGroup) {+ −
const ClassNode *correspondingClass = static_cast<const QmlClassNode*>(qmlPropGroup->parent())->classNode();+ −
PropertyNode *correspondingProperty = 0;+ −
if (correspondingClass)+ −
correspondingProperty = static_cast<PropertyNode*>((Node*)correspondingClass->findNode(property, Node::Property));+ −
QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached);+ −
if (correspondingProperty) {+ −
bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');+ −
qmlPropNode->setWritable(writableList || correspondingProperty->isWritable());+ −
}+ −
++arg;+ −
while (arg != args.end()) {+ −
if (splitQmlPropertyArg(doc,(*arg),type,element,property)) {+ −
QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup,+ −
property,+ −
type,+ −
attached);+ −
if (correspondingProperty) {+ −
bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');+ −
qmlPropNode->setWritable(writableList || correspondingProperty->isWritable());+ −
}+ −
}+ −
++arg;+ −
}+ −
}+ −
}+ −
return qmlPropGroup;+ −
}+ −
#endif+ −
+ −
/*!+ −
Returns the set of strings representing the common metacommands+ −
plus some other metacommands.+ −
*/+ −
QSet<QString> CppCodeParser::otherMetaCommands()+ −
{+ −
return commonMetaCommands() << COMMAND_INHEADERFILE+ −
<< COMMAND_OVERLOAD+ −
<< COMMAND_REIMP+ −
<< COMMAND_RELATES+ −
<< COMMAND_CONTENTSPAGE+ −
<< COMMAND_NEXTPAGE+ −
<< COMMAND_PREVIOUSPAGE+ −
<< COMMAND_INDEXPAGE+ −
#ifdef QDOC_QML + −
<< COMMAND_STARTPAGE+ −
<< COMMAND_QMLINHERITS+ −
<< COMMAND_QMLDEFAULT;+ −
#else + −
<< COMMAND_STARTPAGE;+ −
#endif + −
}+ −
+ −
/*!+ −
Process the metacommand \a command in the context of the+ −
\a node associated with the topic command and the \a doc.+ −
\a arg is the argument to the metacommand.+ −
*/+ −
void CppCodeParser::processOtherMetaCommand(const Doc& doc,+ −
const QString& command,+ −
const QString& arg,+ −
Node *node)+ −
{+ −
if (command == COMMAND_INHEADERFILE) {+ −
if (node != 0 && node->isInnerNode()) {+ −
((InnerNode *) node)->addInclude(arg);+ −
}+ −
else {+ −
doc.location().warning(tr("Ignored '\\%1'")+ −
.arg(COMMAND_INHEADERFILE));+ −
}+ −
}+ −
else if (command == COMMAND_OVERLOAD) {+ −
if (node != 0 && node->type() == Node::Function) {+ −
((FunctionNode *) node)->setOverload(true);+ −
}+ −
else {+ −
doc.location().warning(tr("Ignored '\\%1'")+ −
.arg(COMMAND_OVERLOAD));+ −
}+ −
}+ −
else if (command == COMMAND_REIMP) {+ −
if (node != 0 && node->type() == Node::Function) {+ −
FunctionNode *func = (FunctionNode *) node;+ −
const FunctionNode *from = func->reimplementedFrom();+ −
if (from == 0) {+ −
doc.location().warning(+ −
tr("Cannot find base function for '\\%1' in %2()")+ −
.arg(COMMAND_REIMP).arg(node->name()),+ −
tr("The function either doesn't exist in any base class "+ −
"with the same signature or it exists but isn't virtual."));+ −
}+ −
/*+ −
Ideally, we would enable this check to warn whenever+ −
\reimp is used incorrectly, and only make the node+ −
internal if the function is a reimplementation of+ −
another function in a base class.+ −
*/+ −
else if (from->access() == Node::Private+ −
|| from->parent()->access() == Node::Private) {+ −
doc.location().warning(tr("'\\%1' in %2() should be '\\internal' because its base function is private or internal")+ −
.arg(COMMAND_REIMP).arg(node->name()));+ −
}+ −
+ −
#if 0+ −
// Reimplemented functions now reported in separate sections.+ −
/*+ −
Note: Setting the access to Private hides the documentation,+ −
but setting the status to Internal makes the node available+ −
in the XML output when the WebXMLGenerator is used.+ −
*/+ −
func->setAccess(Node::Private);+ −
func->setStatus(Node::Internal);+ −
#endif+ −
func->setReimp(true);+ −
}+ −
else {+ −
doc.location().warning(tr("Ignored '\\%1' in %2")+ −
.arg(COMMAND_REIMP)+ −
.arg(node->name()));+ −
}+ −
}+ −
else if (command == COMMAND_RELATES) {+ −
InnerNode *pseudoParent;+ −
if (arg.startsWith("<") || arg.startsWith("\"")) {+ −
pseudoParent =+ −
static_cast<InnerNode *>(tre->findNode(QStringList(arg),+ −
Node::Fake));+ −
}+ −
else {+ −
QStringList newPath = arg.split("::");+ −
pseudoParent =+ −
static_cast<InnerNode*>(tre->findNode(QStringList(newPath),+ −
Node::Class));+ −
if (!pseudoParent)+ −
pseudoParent =+ −
static_cast<InnerNode*>(tre->findNode(QStringList(newPath),+ −
Node::Namespace));+ −
}+ −
if (!pseudoParent) {+ −
doc.location().warning(tr("Cannot find '%1' in '\\%2'")+ −
.arg(arg).arg(COMMAND_RELATES));+ −
}+ −
else {+ −
node->setRelates(pseudoParent);+ −
}+ −
}+ −
else if (command == COMMAND_CONTENTSPAGE) {+ −
setLink(node, Node::ContentsLink, arg);+ −
}+ −
else if (command == COMMAND_NEXTPAGE) {+ −
setLink(node, Node::NextLink, arg);+ −
}+ −
else if (command == COMMAND_PREVIOUSPAGE) {+ −
setLink(node, Node::PreviousLink, arg);+ −
}+ −
else if (command == COMMAND_INDEXPAGE) {+ −
setLink(node, Node::IndexLink, arg);+ −
}+ −
else if (command == COMMAND_STARTPAGE) {+ −
setLink(node, Node::StartLink, arg);+ −
}+ −
#ifdef QDOC_QML+ −
else if (command == COMMAND_QMLINHERITS) {+ −
setLink(node, Node::InheritsLink, arg);+ −
}+ −
else if (command == COMMAND_QMLDEFAULT) {+ −
QmlPropGroupNode* qpgn = static_cast<QmlPropGroupNode*>(node);+ −
qpgn->setDefault();+ −
}+ −
#endif+ −
else {+ −
processCommonMetaCommand(doc.location(),command,arg,node,tre);+ −
}+ −
}+ −
+ −
/*!+ −
The topic command has been processed resulting in the \a doc+ −
and \a node passed in here. Process the other meta commands,+ −
which are found in \a doc, in the context of the topic \a node.+ −
*/+ −
void CppCodeParser::processOtherMetaCommands(const Doc& doc, Node *node)+ −
{+ −
const QSet<QString> metaCommands = doc.metaCommandsUsed();+ −
QSet<QString>::ConstIterator cmd = metaCommands.begin();+ −
while (cmd != metaCommands.end()) {+ −
QStringList args = doc.metaCommandArgs(*cmd);+ −
QStringList::ConstIterator arg = args.begin();+ −
while (arg != args.end()) {+ −
processOtherMetaCommand(doc, *cmd, *arg, node);+ −
++arg;+ −
}+ −
++cmd;+ −
}+ −
}+ −
+ −
/*!+ −
Resets the C++ code parser to its default initialized state.+ −
*/+ −
void CppCodeParser::reset(Tree *tree)+ −
{+ −
tre = tree;+ −
tokenizer = 0;+ −
tok = 0;+ −
access = Node::Public;+ −
metaness = FunctionNode::Plain;+ −
lastPath.clear();+ −
moduleName = "";+ −
}+ −
+ −
/*!+ −
Get the next token from the file being parsed and store it+ −
in the token variable.+ −
*/+ −
void CppCodeParser::readToken()+ −
{+ −
tok = tokenizer->getToken();+ −
}+ −
+ −
/*!+ −
Return the current location in the file being parsed,+ −
i.e. the file name, line number, and column number.+ −
*/+ −
const Location& CppCodeParser::location()+ −
{+ −
return tokenizer->location();+ −
}+ −
+ −
/*!+ −
Return the previous string read from the file being parsed.+ −
*/+ −
QString CppCodeParser::previousLexeme()+ −
{+ −
return tokenizer->previousLexeme();+ −
}+ −
+ −
/*!+ −
Return the current string string from the file being parsed.+ −
*/+ −
QString CppCodeParser::lexeme()+ −
{+ −
return tokenizer->lexeme();+ −
}+ −
+ −
bool CppCodeParser::match(int target)+ −
{+ −
if (tok == target) {+ −
readToken();+ −
return true;+ −
}+ −
else+ −
return false;+ −
}+ −
+ −
/*!+ −
If the current token is one of the keyword thingees that+ −
are used in Qt, skip over it to the next token and return+ −
true. Otherwise just return false without reading the+ −
next token.+ −
*/+ −
bool CppCodeParser::matchCompat()+ −
{+ −
switch (tok) {+ −
case Tok_QT_COMPAT:+ −
case Tok_QT_COMPAT_CONSTRUCTOR:+ −
case Tok_QT_DEPRECATED:+ −
case Tok_QT_MOC_COMPAT:+ −
case Tok_QT3_SUPPORT:+ −
case Tok_QT3_SUPPORT_CONSTRUCTOR:+ −
case Tok_QT3_MOC_SUPPORT:+ −
readToken();+ −
return true;+ −
default:+ −
return false;+ −
}+ −
}+ −
+ −
bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)+ −
{+ −
bool matches = (tok == Tok_LeftAngle);+ −
if (matches) {+ −
int leftAngleDepth = 0;+ −
int parenAndBraceDepth = 0;+ −
do {+ −
if (tok == Tok_LeftAngle) {+ −
leftAngleDepth++;+ −
}+ −
else if (tok == Tok_RightAngle) {+ −
leftAngleDepth--;+ −
}+ −
else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) {+ −
++parenAndBraceDepth;+ −
}+ −
else if (tok == Tok_RightParen || tok == Tok_RightBrace) {+ −
if (--parenAndBraceDepth < 0)+ −
return false;+ −
}+ −
+ −
if (dataType != 0)+ −
dataType->append(lexeme());+ −
readToken();+ −
} while (leftAngleDepth > 0 && tok != Tok_Eoi);+ −
}+ −
return matches;+ −
}+ −
+ −
bool CppCodeParser::matchTemplateHeader()+ −
{+ −
readToken();+ −
return matchTemplateAngles();+ −
}+ −
+ −
bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)+ −
{+ −
/*+ −
This code is really hard to follow... sorry. The loop is there to match+ −
Alpha::Beta::Gamma::...::Omega.+ −
*/+ −
for (;;) {+ −
bool virgin = true;+ −
+ −
if (tok != Tok_Ident) {+ −
/*+ −
There is special processing for 'Foo::operator int()'+ −
and such elsewhere. This is the only case where we+ −
return something with a trailing gulbrandsen ('Foo::').+ −
*/+ −
if (tok == Tok_operator)+ −
return true;+ −
+ −
/*+ −
People may write 'const unsigned short' or+ −
'short unsigned const' or any other permutation.+ −
*/+ −
while (match(Tok_const) || match(Tok_volatile))+ −
dataType->append(previousLexeme());+ −
while (match(Tok_signed) || match(Tok_unsigned) ||+ −
match(Tok_short) || match(Tok_long) || match(Tok_int64)) {+ −
dataType->append(previousLexeme());+ −
virgin = false;+ −
}+ −
while (match(Tok_const) || match(Tok_volatile))+ −
dataType->append(previousLexeme());+ −
+ −
if (match(Tok_Tilde))+ −
dataType->append(previousLexeme());+ −
}+ −
+ −
if (virgin) {+ −
if (match(Tok_Ident))+ −
dataType->append(previousLexeme());+ −
else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||+ −
match(Tok_double) || match(Tok_Ellipsis))+ −
dataType->append(previousLexeme());+ −
else+ −
return false;+ −
}+ −
else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {+ −
dataType->append(previousLexeme());+ −
}+ −
+ −
matchTemplateAngles(dataType);+ −
+ −
while (match(Tok_const) || match(Tok_volatile))+ −
dataType->append(previousLexeme());+ −
+ −
if (match(Tok_Gulbrandsen))+ −
dataType->append(previousLexeme());+ −
else+ −
break;+ −
}+ −
+ −
while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) ||+ −
match(Tok_Caret))+ −
dataType->append(previousLexeme());+ −
+ −
if (match(Tok_LeftParenAster)) {+ −
/*+ −
A function pointer. This would be rather hard to handle without a+ −
tokenizer hack, because a type can be followed with a left parenthesis+ −
in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'+ −
as a single token.+ −
*/+ −
dataType->append(previousLexeme());+ −
dataType->appendHotspot();+ −
if (var != 0 && match(Tok_Ident))+ −
*var = previousLexeme();+ −
if (!match(Tok_RightParen) || tok != Tok_LeftParen)+ −
return false;+ −
dataType->append(previousLexeme());+ −
+ −
int parenDepth0 = tokenizer->parenDepth();+ −
while (tokenizer->parenDepth() >= parenDepth0 && tok != Tok_Eoi) {+ −
dataType->append(lexeme());+ −
readToken();+ −
}+ −
if (match(Tok_RightParen))+ −
dataType->append(previousLexeme());+ −
}+ −
else {+ −
/*+ −
The common case: Look for an optional identifier, then for+ −
some array brackets.+ −
*/+ −
dataType->appendHotspot();+ −
+ −
if (var != 0) {+ −
if (match(Tok_Ident)) {+ −
*var = previousLexeme();+ −
}+ −
else if (match(Tok_Comment)) {+ −
/*+ −
A neat hack: Commented-out parameter names are+ −
recognized by qdoc. It's impossible to illustrate+ −
here inside a C-style comment, because it requires+ −
an asterslash. It's also impossible to illustrate+ −
inside a C++-style comment, because the explanation+ −
does not fit on one line.+ −
*/+ −
if (varComment.exactMatch(previousLexeme()))+ −
*var = varComment.cap(1);+ −
}+ −
}+ −
+ −
if (tok == Tok_LeftBracket) {+ −
int bracketDepth0 = tokenizer->bracketDepth();+ −
while ((tokenizer->bracketDepth() >= bracketDepth0 &&+ −
tok != Tok_Eoi) ||+ −
tok == Tok_RightBracket) {+ −
dataType->append(lexeme());+ −
readToken();+ −
}+ −
}+ −
}+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchParameter(FunctionNode *func)+ −
{+ −
CodeChunk dataType;+ −
QString name;+ −
CodeChunk defaultValue;+ −
+ −
if (!matchDataType(&dataType, &name))+ −
return false;+ −
match(Tok_Comment);+ −
if (match(Tok_Equal)) {+ −
int parenDepth0 = tokenizer->parenDepth();+ −
+ −
while (tokenizer->parenDepth() >= parenDepth0 &&+ −
(tok != Tok_Comma ||+ −
tokenizer->parenDepth() > parenDepth0) &&+ −
tok != Tok_Eoi) {+ −
defaultValue.append(lexeme());+ −
readToken();+ −
}+ −
}+ −
func->addParameter(Parameter(dataType.toString(),+ −
"",+ −
name,+ −
defaultValue.toString())); // ###+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchFunctionDecl(InnerNode *parent,+ −
QStringList *parentPathPtr,+ −
FunctionNode **funcPtr,+ −
const QString &templateStuff,+ −
Node::Type type,+ −
bool attached)+ −
{+ −
CodeChunk returnType;+ −
QStringList parentPath;+ −
QString name;+ −
+ −
bool compat = false;+ −
+ −
if (match(Tok_friend))+ −
return false;+ −
match(Tok_explicit);+ −
if (matchCompat())+ −
compat = true;+ −
bool sta = false;+ −
if (match(Tok_static)) {+ −
sta = true;+ −
if (matchCompat())+ −
compat = true;+ −
}+ −
FunctionNode::Virtualness vir = FunctionNode::NonVirtual;+ −
if (match(Tok_virtual)) {+ −
vir = FunctionNode::ImpureVirtual;+ −
if (matchCompat())+ −
compat = true;+ −
}+ −
+ −
if (!matchDataType(&returnType)) {+ −
if (tokenizer->parsingFnOrMacro()+ −
&& (match(Tok_Q_DECLARE_FLAGS) || match(Tok_Q_PROPERTY)))+ −
returnType = CodeChunk(previousLexeme());+ −
else {+ −
return false;+ −
}+ −
}+ −
+ −
if (returnType.toString() == "QBool")+ −
returnType = CodeChunk("bool");+ −
+ −
if (matchCompat())+ −
compat = true;+ −
+ −
if (tok == Tok_operator &&+ −
(returnType.toString().isEmpty() ||+ −
returnType.toString().endsWith("::"))) {+ −
// 'QString::operator const char *()'+ −
parentPath = returnType.toString().split(sep);+ −
parentPath.removeAll(QString());+ −
returnType = CodeChunk();+ −
readToken();+ −
+ −
CodeChunk restOfName;+ −
if (tok != Tok_Tilde && matchDataType(&restOfName)) {+ −
name = "operator " + restOfName.toString();+ −
}+ −
else {+ −
name = previousLexeme() + lexeme();+ −
readToken();+ −
while (tok != Tok_LeftParen && tok != Tok_Eoi) {+ −
name += lexeme();+ −
readToken();+ −
}+ −
}+ −
if (tok != Tok_LeftParen) {+ −
return false;+ −
}+ −
}+ −
else if (tok == Tok_LeftParen) {+ −
// constructor or destructor+ −
parentPath = returnType.toString().split(sep);+ −
if (!parentPath.isEmpty()) {+ −
name = parentPath.last();+ −
parentPath.erase(parentPath.end() - 1);+ −
}+ −
returnType = CodeChunk();+ −
}+ −
else {+ −
while (match(Tok_Ident)) {+ −
name = previousLexeme();+ −
matchTemplateAngles();+ −
+ −
if (match(Tok_Gulbrandsen))+ −
parentPath.append(name);+ −
else+ −
break;+ −
}+ −
+ −
if (tok == Tok_operator) {+ −
name = lexeme();+ −
readToken();+ −
while (tok != Tok_Eoi) {+ −
name += lexeme();+ −
readToken();+ −
if (tok == Tok_LeftParen)+ −
break;+ −
}+ −
}+ −
if (parent && (tok == Tok_Semicolon ||+ −
tok == Tok_LeftBracket ||+ −
tok == Tok_Colon)+ −
&& access != Node::Private) {+ −
if (tok == Tok_LeftBracket) {+ −
returnType.appendHotspot();+ −
+ −
int bracketDepth0 = tokenizer->bracketDepth();+ −
while ((tokenizer->bracketDepth() >= bracketDepth0 &&+ −
tok != Tok_Eoi) ||+ −
tok == Tok_RightBracket) {+ −
returnType.append(lexeme());+ −
readToken();+ −
}+ −
if (tok != Tok_Semicolon) {+ −
return false;+ −
}+ −
}+ −
else if (tok == Tok_Colon) {+ −
returnType.appendHotspot();+ −
+ −
while (tok != Tok_Semicolon && tok != Tok_Eoi) {+ −
returnType.append(lexeme());+ −
readToken();+ −
}+ −
if (tok != Tok_Semicolon) {+ −
return false;+ −
}+ −
}+ −
+ −
VariableNode *var = new VariableNode(parent, name);+ −
var->setAccess(access);+ −
var->setLocation(location());+ −
var->setLeftType(returnType.left());+ −
var->setRightType(returnType.right());+ −
if (compat)+ −
var->setStatus(Node::Compat);+ −
var->setStatic(sta);+ −
return false;+ −
}+ −
if (tok != Tok_LeftParen) {+ −
return false;+ −
}+ −
}+ −
readToken();+ −
+ −
FunctionNode *func = new FunctionNode(type, parent, name, attached);+ −
func->setAccess(access);+ −
func->setLocation(location());+ −
func->setReturnType(returnType.toString());+ −
func->setParentPath(parentPath);+ −
func->setTemplateStuff(templateStuff);+ −
if (compat)+ −
func->setStatus(Node::Compat);+ −
+ −
func->setMetaness(metaness);+ −
if (parent) {+ −
if (name == parent->name()) {+ −
func->setMetaness(FunctionNode::Ctor);+ −
} else if (name.startsWith("~")) {+ −
func->setMetaness(FunctionNode::Dtor);+ −
}+ −
}+ −
func->setStatic(sta);+ −
+ −
if (tok != Tok_RightParen) {+ −
do {+ −
if (!matchParameter(func)) {+ −
return false;+ −
}+ −
} while (match(Tok_Comma));+ −
}+ −
if (!match(Tok_RightParen)) {+ −
return false;+ −
}+ −
+ −
func->setConst(match(Tok_const));+ −
+ −
if (match(Tok_Equal) && match(Tok_Number))+ −
vir = FunctionNode::PureVirtual;+ −
func->setVirtualness(vir);+ −
+ −
if (match(Tok_Colon)) {+ −
while (tok != Tok_LeftBrace && tok != Tok_Eoi)+ −
readToken();+ −
}+ −
+ −
if (!match(Tok_Semicolon) && tok != Tok_Eoi) {+ −
int braceDepth0 = tokenizer->braceDepth();+ −
+ −
if (!match(Tok_LeftBrace)) {+ −
return false;+ −
}+ −
while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi)+ −
readToken();+ −
match(Tok_RightBrace);+ −
}+ −
if (parentPathPtr != 0)+ −
*parentPathPtr = parentPath;+ −
if (funcPtr != 0)+ −
*funcPtr = func;+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchBaseSpecifier(ClassNode *classe, bool isClass)+ −
{+ −
Node::Access access;+ −
+ −
switch (tok) {+ −
case Tok_public:+ −
access = Node::Public;+ −
readToken();+ −
break;+ −
case Tok_protected:+ −
access = Node::Protected;+ −
readToken();+ −
break;+ −
case Tok_private:+ −
access = Node::Private;+ −
readToken();+ −
break;+ −
default:+ −
access = isClass ? Node::Private : Node::Public;+ −
}+ −
+ −
if (tok == Tok_virtual)+ −
readToken();+ −
+ −
CodeChunk baseClass;+ −
if (!matchDataType(&baseClass))+ −
return false;+ −
+ −
tre->addBaseClass(classe,+ −
access,+ −
baseClass.toPath(),+ −
baseClass.toString(),+ −
classe->parent());+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchBaseList(ClassNode *classe, bool isClass)+ −
{+ −
for (;;) {+ −
if (!matchBaseSpecifier(classe, isClass))+ −
return false;+ −
if (tok == Tok_LeftBrace)+ −
return true;+ −
if (!match(Tok_Comma))+ −
return false;+ −
}+ −
}+ −
+ −
/*!+ −
Parse a C++ class, union, or struct declarion.+ −
*/+ −
bool CppCodeParser::matchClassDecl(InnerNode *parent,+ −
const QString &templateStuff)+ −
{+ −
bool isClass = (tok == Tok_class);+ −
readToken();+ −
+ −
bool compat = matchCompat();+ −
+ −
if (tok != Tok_Ident)+ −
return false;+ −
while (tok == Tok_Ident)+ −
readToken();+ −
if (tok != Tok_Colon && tok != Tok_LeftBrace)+ −
return false;+ −
+ −
/*+ −
So far, so good. We have 'class Foo {' or 'class Foo :'.+ −
This is enough to recognize a class definition.+ −
*/+ −
ClassNode *classe = new ClassNode(parent, previousLexeme());+ −
classe->setAccess(access);+ −
classe->setLocation(location());+ −
if (compat)+ −
classe->setStatus(Node::Compat);+ −
if (!moduleName.isEmpty())+ −
classe->setModuleName(moduleName);+ −
classe->setTemplateStuff(templateStuff);+ −
+ −
if (match(Tok_Colon) && !matchBaseList(classe, isClass))+ −
return false;+ −
if (!match(Tok_LeftBrace))+ −
return false;+ −
+ −
Node::Access outerAccess = access;+ −
access = isClass ? Node::Private : Node::Public;+ −
FunctionNode::Metaness outerMetaness = metaness;+ −
metaness = FunctionNode::Plain;+ −
+ −
bool matches = (matchDeclList(classe) && match(Tok_RightBrace) &&+ −
match(Tok_Semicolon));+ −
access = outerAccess;+ −
metaness = outerMetaness;+ −
return matches;+ −
}+ −
+ −
bool CppCodeParser::matchNamespaceDecl(InnerNode *parent)+ −
{+ −
readToken(); // skip 'namespace'+ −
if (tok != Tok_Ident)+ −
return false;+ −
while (tok == Tok_Ident)+ −
readToken();+ −
if (tok != Tok_LeftBrace)+ −
return false;+ −
+ −
/*+ −
So far, so good. We have 'namespace Foo {'.+ −
*/+ −
QString namespaceName = previousLexeme();+ −
NamespaceNode *namespasse = 0;+ −
if (parent)+ −
namespasse = static_cast<NamespaceNode*>(parent->findNode(namespaceName, Node::Namespace));+ −
if (!namespasse) {+ −
namespasse = new NamespaceNode(parent, namespaceName);+ −
namespasse->setAccess(access);+ −
namespasse->setLocation(location());+ −
}+ −
+ −
readToken(); // skip '{'+ −
bool matched = matchDeclList(namespasse);+ −
+ −
return matched && match(Tok_RightBrace);+ −
}+ −
+ −
bool CppCodeParser::matchUsingDecl()+ −
{+ −
readToken(); // skip 'using'+ −
+ −
// 'namespace'+ −
if (tok != Tok_namespace)+ −
return false;+ −
+ −
readToken();+ −
// identifier+ −
if (tok != Tok_Ident)+ −
return false;+ −
+ −
QString name;+ −
while (tok == Tok_Ident) {+ −
name += lexeme();+ −
readToken();+ −
if (tok == Tok_Semicolon)+ −
break;+ −
else if (tok != Tok_Gulbrandsen)+ −
return false;+ −
name += "::";+ −
readToken();+ −
}+ −
+ −
/*+ −
So far, so good. We have 'using namespace Foo;'.+ −
*/+ −
usedNamespaces.insert(name);+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchEnumItem(InnerNode *parent, EnumNode *enume)+ −
{+ −
if (!match(Tok_Ident))+ −
return false;+ −
+ −
QString name = previousLexeme();+ −
CodeChunk val;+ −
+ −
if (match(Tok_Equal)) {+ −
while (tok != Tok_Comma && tok != Tok_RightBrace &&+ −
tok != Tok_Eoi) {+ −
val.append(lexeme());+ −
readToken();+ −
}+ −
}+ −
+ −
if (enume) {+ −
QString strVal = val.toString();+ −
if (strVal.isEmpty()) {+ −
if (enume->items().isEmpty()) {+ −
strVal = "0";+ −
}+ −
else {+ −
QString last = enume->items().last().value();+ −
bool ok;+ −
int n = last.toInt(&ok);+ −
if (ok) {+ −
if (last.startsWith("0") && last.size() > 1) {+ −
if (last.startsWith("0x") || last.startsWith("0X"))+ −
strVal = last.left(2) + QString::number(n + 1, 16);+ −
else+ −
strVal = "0" + QString::number(n + 1, 8);+ −
}+ −
else+ −
strVal = QString::number(n + 1);+ −
}+ −
}+ −
}+ −
+ −
enume->addItem(EnumItem(name, strVal));+ −
}+ −
else {+ −
VariableNode *var = new VariableNode(parent, name);+ −
var->setAccess(access);+ −
var->setLocation(location());+ −
var->setLeftType("const int");+ −
var->setStatic(true);+ −
}+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchEnumDecl(InnerNode *parent)+ −
{+ −
QString name;+ −
+ −
if (!match(Tok_enum))+ −
return false;+ −
if (match(Tok_Ident))+ −
name = previousLexeme();+ −
if (tok != Tok_LeftBrace)+ −
return false;+ −
+ −
EnumNode *enume = 0;+ −
+ −
if (!name.isEmpty()) {+ −
enume = new EnumNode(parent, name);+ −
enume->setAccess(access);+ −
enume->setLocation(location());+ −
}+ −
+ −
readToken();+ −
+ −
if (!matchEnumItem(parent, enume))+ −
return false;+ −
+ −
while (match(Tok_Comma)) {+ −
if (!matchEnumItem(parent, enume))+ −
return false;+ −
}+ −
return match(Tok_RightBrace) && match(Tok_Semicolon);+ −
}+ −
+ −
bool CppCodeParser::matchTypedefDecl(InnerNode *parent)+ −
{+ −
CodeChunk dataType;+ −
QString name;+ −
+ −
if (!match(Tok_typedef))+ −
return false;+ −
if (!matchDataType(&dataType, &name))+ −
return false;+ −
if (!match(Tok_Semicolon))+ −
return false;+ −
+ −
if (parent && !parent->findNode(name, Node::Typedef)) {+ −
TypedefNode *typedeffe = new TypedefNode(parent, name);+ −
typedeffe->setAccess(access);+ −
typedeffe->setLocation(location());+ −
}+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::matchProperty(InnerNode *parent)+ −
{+ −
if (!match(Tok_Q_PROPERTY) &&+ −
!match(Tok_Q_OVERRIDE) &&+ −
!match(Tok_QDOC_PROPERTY))+ −
return false;+ −
if (!match(Tok_LeftParen))+ −
return false;+ −
+ −
QString name;+ −
CodeChunk dataType;+ −
if (!matchDataType(&dataType, &name))+ −
return false;+ −
+ −
PropertyNode *property = new PropertyNode(parent, name);+ −
property->setAccess(Node::Public);+ −
property->setLocation(location());+ −
property->setDataType(dataType.toString());+ −
+ −
while (tok != Tok_RightParen && tok != Tok_Eoi) {+ −
if (!match(Tok_Ident))+ −
return false;+ −
QString key = previousLexeme();+ −
QString value;+ −
+ −
if (match(Tok_Ident)) {+ −
value = previousLexeme();+ −
}+ −
else if (match(Tok_LeftParen)) {+ −
int depth = 1;+ −
while (tok != Tok_Eoi) {+ −
if (tok == Tok_LeftParen) {+ −
readToken();+ −
++depth;+ −
} else if (tok == Tok_RightParen) {+ −
readToken();+ −
if (--depth == 0)+ −
break;+ −
} else {+ −
readToken();+ −
}+ −
}+ −
value = "?";+ −
}+ −
+ −
if (key == "READ")+ −
tre->addPropertyFunction(property, value, PropertyNode::Getter);+ −
else if (key == "WRITE") {+ −
tre->addPropertyFunction(property, value, PropertyNode::Setter);+ −
property->setWritable(true);+ −
} else if (key == "STORED")+ −
property->setStored(value.toLower() == "true");+ −
else if (key == "DESIGNABLE")+ −
property->setDesignable(value.toLower() == "true");+ −
else if (key == "RESET")+ −
tre->addPropertyFunction(property, value, PropertyNode::Resetter);+ −
else if (key == "NOTIFY") {+ −
tre->addPropertyFunction(property, value, PropertyNode::Notifier);+ −
}+ −
+ −
}+ −
match(Tok_RightParen);+ −
return true;+ −
}+ −
+ −
/*!+ −
Parse a C++ declaration.+ −
*/+ −
bool CppCodeParser::matchDeclList(InnerNode *parent)+ −
{+ −
QString templateStuff;+ −
int braceDepth0 = tokenizer->braceDepth();+ −
if (tok == Tok_RightBrace) // prevents failure on empty body+ −
braceDepth0++;+ −
+ −
while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi) {+ −
switch (tok) {+ −
case Tok_Colon:+ −
readToken();+ −
break;+ −
case Tok_class:+ −
case Tok_struct:+ −
case Tok_union:+ −
matchClassDecl(parent, templateStuff);+ −
break;+ −
case Tok_namespace:+ −
matchNamespaceDecl(parent);+ −
break;+ −
case Tok_using:+ −
matchUsingDecl();+ −
break;+ −
case Tok_template:+ −
templateStuff = matchTemplateHeader();+ −
continue;+ −
case Tok_enum:+ −
matchEnumDecl(parent);+ −
break;+ −
case Tok_typedef:+ −
matchTypedefDecl(parent);+ −
break;+ −
case Tok_private:+ −
readToken();+ −
access = Node::Private;+ −
metaness = FunctionNode::Plain;+ −
break;+ −
case Tok_protected:+ −
readToken();+ −
access = Node::Protected;+ −
metaness = FunctionNode::Plain;+ −
break;+ −
case Tok_public:+ −
readToken();+ −
access = Node::Public;+ −
metaness = FunctionNode::Plain;+ −
break;+ −
case Tok_signals:+ −
case Tok_Q_SIGNALS:+ −
readToken();+ −
access = Node::Public;+ −
metaness = FunctionNode::Signal;+ −
break;+ −
case Tok_slots:+ −
case Tok_Q_SLOTS:+ −
readToken();+ −
metaness = FunctionNode::Slot;+ −
break;+ −
case Tok_Q_OBJECT:+ −
readToken();+ −
break;+ −
case Tok_Q_OVERRIDE:+ −
case Tok_Q_PROPERTY:+ −
case Tok_QDOC_PROPERTY:+ −
matchProperty(parent);+ −
break;+ −
case Tok_Q_DECLARE_SEQUENTIAL_ITERATOR:+ −
readToken();+ −
if (match(Tok_LeftParen) && match(Tok_Ident))+ −
sequentialIteratorClasses.insert(previousLexeme(),+ −
location().fileName());+ −
match(Tok_RightParen);+ −
break;+ −
case Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR:+ −
readToken();+ −
if (match(Tok_LeftParen) && match(Tok_Ident))+ −
mutableSequentialIteratorClasses.insert(previousLexeme(),+ −
location().fileName());+ −
match(Tok_RightParen);+ −
break;+ −
case Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR:+ −
readToken();+ −
if (match(Tok_LeftParen) && match(Tok_Ident))+ −
associativeIteratorClasses.insert(previousLexeme(),+ −
location().fileName());+ −
match(Tok_RightParen);+ −
break;+ −
case Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR:+ −
readToken();+ −
if (match(Tok_LeftParen) && match(Tok_Ident))+ −
mutableAssociativeIteratorClasses.insert(previousLexeme(),+ −
location().fileName());+ −
match(Tok_RightParen);+ −
break;+ −
case Tok_Q_DECLARE_FLAGS:+ −
readToken();+ −
if (match(Tok_LeftParen) && match(Tok_Ident)) {+ −
QString flagsType = previousLexeme();+ −
if (match(Tok_Comma) && match(Tok_Ident)) {+ −
QString enumType = previousLexeme();+ −
TypedefNode *flagsNode = new TypedefNode(parent, flagsType);+ −
flagsNode->setAccess(access);+ −
flagsNode->setLocation(location());+ −
EnumNode *enumNode =+ −
static_cast<EnumNode*>(parent->findNode(enumType,+ −
Node::Enum));+ −
if (enumNode)+ −
enumNode->setFlagsType(flagsNode);+ −
}+ −
}+ −
match(Tok_RightParen);+ −
break;+ −
case Tok_QT_MODULE:+ −
readToken();+ −
if (match(Tok_LeftParen) && match(Tok_Ident))+ −
moduleName = previousLexeme();+ −
if (!moduleName.startsWith("Qt"))+ −
moduleName.prepend("Qt");+ −
match(Tok_RightParen);+ −
break;+ −
default:+ −
if (!matchFunctionDecl(parent, 0, 0, templateStuff)) {+ −
while (tok != Tok_Eoi &&+ −
(tokenizer->braceDepth() > braceDepth0 ||+ −
(!match(Tok_Semicolon) &&+ −
tok != Tok_public && tok != Tok_protected &&+ −
tok != Tok_private)))+ −
readToken();+ −
}+ −
}+ −
templateStuff.clear();+ −
}+ −
return true;+ −
}+ −
+ −
/*!+ −
This is called by parseSourceFile() to do the actual parsing+ −
and tree building.+ −
*/+ −
bool CppCodeParser::matchDocsAndStuff()+ −
{+ −
QSet<QString> topicCommandsAllowed = topicCommands();+ −
QSet<QString> otherMetacommandsAllowed = otherMetaCommands();+ −
QSet<QString> metacommandsAllowed = topicCommandsAllowed ++ −
otherMetacommandsAllowed;+ −
+ −
while (tok != Tok_Eoi) {+ −
if (tok == Tok_Doc) {+ −
/*+ −
lexeme() returns an entire qdoc comment.+ −
*/+ −
QString comment = lexeme();+ −
Location start_loc(location());+ −
readToken();+ −
+ −
Doc::trimCStyleComment(start_loc,comment);+ −
Location end_loc(location());+ −
+ −
/*+ −
Doc parses the comment.+ −
*/+ −
Doc doc(start_loc,end_loc,comment,metacommandsAllowed);+ −
+ −
QString topic;+ −
QStringList args;+ −
+ −
QSet<QString> topicCommandsUsed = topicCommandsAllowed &+ −
doc.metaCommandsUsed();+ −
+ −
/*+ −
There should be one topic command in the set,+ −
or none. If the set is empty, then the comment+ −
should be a function description.+ −
*/+ −
if (topicCommandsUsed.count() > 0) {+ −
topic = *topicCommandsUsed.begin();+ −
args = doc.metaCommandArgs(topic);+ −
}+ −
+ −
NodeList nodes;+ −
QList<Doc> docs;+ −
+ −
if (topic.isEmpty()) {+ −
QStringList parentPath;+ −
FunctionNode *clone;+ −
FunctionNode *func = 0;+ −
+ −
if (matchFunctionDecl(0, &parentPath, &clone)) {+ −
foreach (const QString &usedNamespace, usedNamespaces) {+ −
QStringList newPath = usedNamespace.split("::") + parentPath;+ −
func = tre->findFunctionNode(newPath, clone);+ −
if (func)+ −
break;+ −
}+ −
if (func == 0)+ −
func = tre->findFunctionNode(parentPath, clone);+ −
+ −
if (func) {+ −
func->borrowParameterNames(clone);+ −
nodes.append(func);+ −
docs.append(doc);+ −
}+ −
delete clone;+ −
}+ −
else {+ −
doc.location().warning(+ −
tr("Cannot tie this documentation to anything"),+ −
tr("I found a /*! ... */ comment, but there was no "+ −
"topic command (e.g., '\\%1', '\\%2') in the "+ −
"comment and no function definition following "+ −
"the comment.")+ −
.arg(COMMAND_FN).arg(COMMAND_PAGE));+ −
}+ −
}+ −
else {+ −
/*+ −
There is a topic command. Process it.+ −
*/+ −
#ifdef QDOC_QML+ −
if ((topic == COMMAND_QMLPROPERTY) ||+ −
(topic == COMMAND_QMLATTACHEDPROPERTY)) {+ −
Doc nodeDoc = doc;+ −
Node *node = processTopicCommandGroup(nodeDoc,topic,args);+ −
if (node != 0) {+ −
nodes.append(node);+ −
docs.append(nodeDoc);+ −
}+ −
}+ −
else {+ −
QStringList::ConstIterator a = args.begin();+ −
while (a != args.end()) {+ −
Doc nodeDoc = doc;+ −
Node *node = processTopicCommand(nodeDoc,topic,*a);+ −
if (node != 0) {+ −
nodes.append(node);+ −
docs.append(nodeDoc);+ −
}+ −
++a;+ −
}+ −
}+ −
#else+ −
QStringList::ConstIterator a = args.begin();+ −
while (a != args.end()) {+ −
Doc nodeDoc = doc;+ −
Node *node = processTopicCommand(nodeDoc, topic, *a);+ −
if (node != 0) {+ −
nodes.append(node);+ −
docs.append(nodeDoc);+ −
}+ −
++a;+ −
}+ −
#endif + −
}+ −
+ −
NodeList::Iterator n = nodes.begin();+ −
QList<Doc>::Iterator d = docs.begin();+ −
while (n != nodes.end()) {+ −
processOtherMetaCommands(*d, *n);+ −
(*n)->setDoc(*d);+ −
if ((*n)->isInnerNode() &&+ −
((InnerNode *)*n)->includes().isEmpty()) {+ −
InnerNode *m = static_cast<InnerNode *>(*n);+ −
while (m->parent() != tre->root())+ −
m = m->parent();+ −
if (m == *n)+ −
((InnerNode *)*n)->addInclude((*n)->name());+ −
else+ −
((InnerNode *)*n)->setIncludes(m->includes());+ −
}+ −
++d;+ −
++n;+ −
}+ −
}+ −
else if (tok == Tok_using) {+ −
matchUsingDecl();+ −
}+ −
else {+ −
QStringList parentPath;+ −
FunctionNode *clone;+ −
FunctionNode *node = 0;+ −
+ −
if (matchFunctionDecl(0, &parentPath, &clone)) {+ −
/*+ −
The location of the definition is more interesting+ −
than that of the declaration. People equipped with+ −
a sophisticated text editor can respond to warnings+ −
concerning undocumented functions very quickly.+ −
+ −
Signals are implemented in uninteresting files+ −
generated by moc.+ −
*/+ −
node = tre->findFunctionNode(parentPath, clone);+ −
if (node != 0 && node->metaness() != FunctionNode::Signal)+ −
node->setLocation(clone->location());+ −
delete clone;+ −
}+ −
else {+ −
if (tok != Tok_Doc)+ −
readToken();+ −
}+ −
}+ −
}+ −
return true;+ −
}+ −
+ −
bool CppCodeParser::makeFunctionNode(const QString& synopsis,+ −
QStringList *parentPathPtr,+ −
FunctionNode **funcPtr,+ −
InnerNode *root,+ −
Node::Type type,+ −
bool attached)+ −
{+ −
Tokenizer *outerTokenizer = tokenizer;+ −
int outerTok = tok;+ −
+ −
Location loc;+ −
QByteArray latin1 = synopsis.toLatin1();+ −
Tokenizer stringTokenizer(loc, latin1);+ −
stringTokenizer.setParsingFnOrMacro(true);+ −
tokenizer = &stringTokenizer;+ −
readToken();+ −
+ −
bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr, QString(), type, attached);+ −
// potential memory leak with funcPtr+ −
+ −
tokenizer = outerTokenizer;+ −
tok = outerTok;+ −
return ok;+ −
}+ −
+ −
/*!+ −
Create a new FunctionNode for a QML method or signal, as+ −
specified by \a type, as a child of \a parent. \a sig is+ −
the complete signature, and if \a attached is true, the+ −
method or signal is "attached". \a qdoctag is the text of+ −
the \a type.+ −
*/+ −
FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,+ −
const QString& sig,+ −
InnerNode* parent,+ −
Node::Type type,+ −
bool attached,+ −
QString qdoctag)+ −
{+ −
QStringList pp;+ −
FunctionNode* fn = 0;+ −
if (!makeFunctionNode(sig,&pp,&fn,parent,type,attached) &&+ −
!makeFunctionNode("void "+sig,&pp,&fn,parent,type,attached)) {+ −
doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag));+ −
}+ −
if (fn)+ −
return fn;+ −
return 0;+ −
}+ −
+ −
void CppCodeParser::parseQiteratorDotH(const Location &location,+ −
const QString &filePath)+ −
{+ −
QFile file(filePath);+ −
if (!file.open(QFile::ReadOnly))+ −
return;+ −
+ −
QString text = file.readAll();+ −
text.remove("\r");+ −
text.replace("\\\n", "");+ −
QStringList lines = text.split("\n");+ −
lines = lines.filter("Q_DECLARE");+ −
lines.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), "");+ −
+ −
if (lines.size() == 4) {+ −
sequentialIteratorDefinition = lines[0];+ −
mutableSequentialIteratorDefinition = lines[1];+ −
associativeIteratorDefinition = lines[2];+ −
mutableAssociativeIteratorDefinition = lines[3];+ −
}+ −
else {+ −
location.warning(tr("The qiterator.h hack failed"));+ −
}+ −
}+ −
+ −
void CppCodeParser::instantiateIteratorMacro(const QString &container,+ −
const QString &includeFile,+ −
const QString ¯oDef,+ −
Tree * /* tree */)+ −
{+ −
QString resultingCode = macroDef;+ −
resultingCode.replace(QRegExp("\\bC\\b"), container);+ −
resultingCode.replace(QRegExp("\\s*##\\s*"), "");+ −
+ −
Location loc(includeFile); // hack to get the include file for free+ −
QByteArray latin1 = resultingCode.toLatin1();+ −
Tokenizer stringTokenizer(loc, latin1);+ −
tokenizer = &stringTokenizer;+ −
readToken();+ −
matchDeclList(tre->root());+ −
}+ −
+ −
void CppCodeParser::createExampleFileNodes(FakeNode *fake)+ −
{+ −
QString examplePath = fake->name();+ −
+ −
// we can assume that this file always exists+ −
QString proFileName = examplePath + "/" ++ −
examplePath.split("/").last() + ".pro";+ −
+ −
QString userFriendlyFilePath;+ −
QString fullPath = Config::findFile(fake->doc().location(),+ −
exampleFiles,+ −
exampleDirs,+ −
proFileName,+ −
userFriendlyFilePath);+ −
+ −
if (fullPath.isEmpty()) {+ −
QString tmp = proFileName;+ −
proFileName = examplePath + "/" + "qbuild.pro";+ −
userFriendlyFilePath.clear();+ −
fullPath = Config::findFile(fake->doc().location(),+ −
exampleFiles,+ −
exampleDirs,+ −
proFileName,+ −
userFriendlyFilePath);+ −
if (fullPath.isEmpty()) {+ −
fake->doc().location().warning(+ −
tr("Cannot find file '%1' or '%2'").arg(tmp).arg(proFileName));+ −
return;+ −
}+ −
}+ −
+ −
int sizeOfBoringPartOfName = fullPath.size() - proFileName.size();+ −
fullPath.truncate(fullPath.lastIndexOf('/'));+ −
+ −
QStringList exampleFiles = Config::getFilesHere(fullPath,exampleNameFilter);+ −
QString imagesPath = fullPath + "/images";+ −
QStringList imageFiles = Config::getFilesHere(imagesPath,exampleImageFilter);+ −
+ −
#if 0 + −
qDebug() << "examplePath:" << examplePath;+ −
qDebug() << " exampleFiles" << exampleFiles;+ −
qDebug() << "imagesPath:" << imagesPath;+ −
qDebug() << "fullPath:" << fullPath;+ −
qDebug() << " imageFiles" << imageFiles;+ −
#endif + −
+ −
if (!exampleFiles.isEmpty()) {+ −
// move main.cpp and to the end, if it exists+ −
QString mainCpp;+ −
QMutableStringListIterator i(exampleFiles);+ −
i.toBack();+ −
while (i.hasPrevious()) {+ −
QString fileName = i.previous();+ −
if (fileName.endsWith("/main.cpp")) {+ −
mainCpp = fileName;+ −
i.remove();+ −
}+ −
else if (fileName.contains("/qrc_") || fileName.contains("/moc_")+ −
|| fileName.contains("/ui_"))+ −
i.remove();+ −
}+ −
if (!mainCpp.isEmpty())+ −
exampleFiles.append(mainCpp);+ −
+ −
// add any qmake Qt resource files and qmake project files+ −
exampleFiles += Config::getFilesHere(fullPath, "*.qrc *.pro");+ −
}+ −
+ −
foreach (const QString &exampleFile, exampleFiles)+ −
(void) new FakeNode(fake,+ −
exampleFile.mid(sizeOfBoringPartOfName),+ −
Node::File);+ −
foreach (const QString &imageFile, imageFiles) {+ −
new FakeNode(fake,+ −
imageFile.mid(sizeOfBoringPartOfName),+ −
Node::Image);+ −
}+ −
}+ −
+ −
QT_END_NAMESPACE+ −