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