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