diff options
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 203 | ||||
-rw-r--r-- | src/qdoc/clangcodeparser.h | 6 | ||||
-rw-r--r-- | src/qdoc/codeparser.h | 3 | ||||
-rw-r--r-- | src/qdoc/cppcodemarker.cpp | 2 | ||||
-rw-r--r-- | src/qdoc/cppcodeparser.cpp | 622 | ||||
-rw-r--r-- | src/qdoc/cppcodeparser.h | 23 | ||||
-rw-r--r-- | src/qdoc/generator.cpp | 2 | ||||
-rw-r--r-- | src/qdoc/node.cpp | 82 | ||||
-rw-r--r-- | src/qdoc/node.h | 8 | ||||
-rw-r--r-- | src/qdoc/puredocparser.cpp | 18 | ||||
-rw-r--r-- | src/qdoc/qdocdatabase.h | 1 | ||||
-rw-r--r-- | src/qdoc/tree.cpp | 24 | ||||
-rw-r--r-- | src/qdoc/tree.h | 1 |
13 files changed, 326 insertions, 669 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index 983b0b9ba..b6947c74f 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -475,8 +475,17 @@ CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocat case CXCursor_Constructor: case CXCursor_Destructor: case CXCursor_ConversionFunction: { - ignoreSignature = ignoredSymbol(functionName(cursor)); - *fnNode = ignoreSignature ? 0 : findFunctionNodeForCursor(qdb_, cursor); + ignoreSignature = false; + if (ignoredSymbol(functionName(cursor))) { + *fnNode = 0; + ignoreSignature = true; + } else { + *fnNode = findFunctionNodeForCursor(qdb_, cursor); + if (*fnNode && (*fnNode)->isFunction()) { + FunctionNode* fn = static_cast<FunctionNode*>(*fnNode); + readParameterNamesAndAttributes(fn, cursor); + } + } break; } default: @@ -800,7 +809,6 @@ void ClangVisitor::readParameterNamesAndAttributes(FunctionNode* fn, CXCursor cu fn->setParameters(pvect); } - void ClangVisitor::parseProperty(const QString& spelling, const Location& loc) { int lpIdx = spelling.indexOf(QChar('(')); @@ -1349,10 +1357,22 @@ void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QStrin } } else { - if (topic == "fn") + if (topic == COMMAND_FN) { node = parseFnArg(doc.location(), a->first); - else + } else if (topic == COMMAND_MACRO) { + node = parseMacroArg(doc.location(), a->first); + } else if (topic == COMMAND_QMLSIGNAL || + topic == COMMAND_QMLMETHOD || + topic == COMMAND_QMLATTACHEDSIGNAL || + topic == COMMAND_QMLATTACHEDMETHOD || + topic == COMMAND_JSSIGNAL || + topic == COMMAND_JSMETHOD || + topic == COMMAND_JSATTACHEDSIGNAL || + topic == COMMAND_JSATTACHEDMETHOD) { + node = parseOtherFuncArg(topic, doc.location(), a->first); + } else { node = processTopicCommand(nodeDoc, topic, *a); + } if (node != 0) { nodes.append(node); docs.append(nodeDoc); @@ -1389,15 +1409,142 @@ void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QStrin } /*! - Use clang to parse the function signature from an fn command. - \a fnArg is the string to parse. It is always a function decl. - \a location is used for reporting errors. + */ +Node* ClangCodeParser::parseOtherFuncArg(const QString& topic, + const Location& location, + const QString& funcArg) +{ + if (Generator::preparing() && !Generator::singleExec()) + return 0; + + QString funcName; + QString returnType; + + int leftParen = funcArg.indexOf(QChar('(')); + if (leftParen > 0) + funcName = funcArg.left(leftParen); + else + funcName = funcArg; + int firstBlank = funcName.indexOf(QChar(' ')); + if (firstBlank > 0) { + returnType = funcName.left(firstBlank); + funcName = funcName.right(funcName.length() - firstBlank - 1); + } + + QStringList colonSplit(funcName.split("::")); + if (colonSplit.size() < 2) { + QString msg = "Unrecognizable QML module/component qualifier for " + funcArg; + location.warning(tr(msg.toLatin1().data())); + return 0; + } + QString moduleName; + QString elementName; + if (colonSplit.size() > 2) { + moduleName = colonSplit[0]; + elementName = colonSplit[1]; + } else { + elementName = colonSplit[0]; + } + funcName = colonSplit.last(); + + Aggregate *aggregate = qdb_->findQmlType(moduleName, elementName); + bool attached = false; + if (!aggregate) + aggregate = qdb_->findQmlBasicType(moduleName, elementName); + if (!aggregate) + return 0; + + Node::NodeType nodeType = Node::QmlMethod; + if (topic == COMMAND_QMLSIGNAL || topic == COMMAND_JSSIGNAL) { + nodeType = Node::QmlSignal; + } else if (topic == COMMAND_QMLATTACHEDSIGNAL || topic == COMMAND_JSATTACHEDSIGNAL) { + nodeType = Node::QmlSignal; + attached = true; + } else if (topic == COMMAND_QMLATTACHEDMETHOD || topic == COMMAND_JSATTACHEDMETHOD) { + attached = true; + } else { + Q_ASSERT(topic == COMMAND_QMLMETHOD || topic == COMMAND_JSMETHOD); + } + + QString params; + QStringList leftParenSplit = funcArg.split('('); + if (leftParenSplit.size() > 1) { + QStringList rightParenSplit = leftParenSplit[1].split(')'); + if (rightParenSplit.size() > 0) + params = rightParenSplit[0]; + } + FunctionNode *funcNode = static_cast<FunctionNode*>(new FunctionNode(nodeType, aggregate, funcName, attached)); + funcNode->setAccess(Node::Public); + funcNode->setLocation(location); + funcNode->setReturnType(returnType); + funcNode->setParameters(params); + return funcNode; +} + +/*! + Parse the macroArg ad hoc, without using clang and without + using the old qdoc C++ parser. + */ +Node* ClangCodeParser::parseMacroArg(const Location& location, const QString& macroArg) +{ + if (Generator::preparing() && !Generator::singleExec()) + return 0; + FunctionNode* newMacroNode = 0; + QStringList leftParenSplit = macroArg.split('('); + if (leftParenSplit.size() > 0) { + QString macroName; + FunctionNode* oldMacroNode = 0; + QStringList blankSplit = leftParenSplit[0].split(' '); + if (blankSplit.size() > 0) { + macroName = blankSplit.last(); + oldMacroNode = static_cast<FunctionNode*>(qdb_->findMacroNode(macroName)); + } + QString returnType; + if (blankSplit.size() > 1) { + blankSplit.removeLast(); + returnType = blankSplit.join(' '); + } + QString params; + if (leftParenSplit.size() > 1) { + const QString &afterParen = leftParenSplit.at(1); + int rightParen = afterParen.indexOf(')'); + if (rightParen >= 0) + params = afterParen.left(rightParen); + } + int i = 0; + while (i < macroName.length() && !macroName.at(i).isLetter()) + i++; + if (i > 0) { + returnType += QChar(' ') + macroName.left(i); + macroName = macroName.mid(i); + } + newMacroNode = static_cast<FunctionNode*>(new FunctionNode(qdb_->primaryTreeRoot(), macroName)); + newMacroNode->setAccess(Node::Public); + newMacroNode->setLocation(location); + if (params.isEmpty()) + newMacroNode->setMetaness(FunctionNode::MacroWithoutParams); + else + newMacroNode->setMetaness(FunctionNode::MacroWithParams); + newMacroNode->setReturnType(returnType); + newMacroNode->setParameters(params); + if (oldMacroNode && newMacroNode->compare(oldMacroNode)) { + location.warning(ClangCodeParser::tr("\\macro %1 documented more than once").arg(macroArg)); + oldMacroNode->doc().location().warning(tr("(The previous doc is here)")); + } + } + return newMacroNode; + } + +/*! + Use clang to parse the function signature from a function + command. \a location is used for reporting errors. \a fnArg + is the string to parse. It is always a function decl. */ Node* ClangCodeParser::parseFnArg(const Location& location, const QString& fnArg) { Node* fnNode = 0; if (Generator::preparing() && !Generator::singleExec()) - return 0; + return fnNode; /* If the \fn command begins with a tag, then don't try to parse the \fn command with clang. Use the tag to search @@ -1409,8 +1556,42 @@ Node* ClangCodeParser::parseFnArg(const Location& location, const QString& fnArg if (end > 1) { QString tag = fnArg.left(end + 1); fnNode = qdb_->findFunctionNodeForTag(tag); - if (!fnNode) + if (!fnNode) { location.error(ClangCodeParser::tr("tag \\fn %1 not used in any include file in current module").arg(tag)); + } else { + /* + The function node was found. Use the formal + parameter names from the \FN command, because + they will be the names used in the documentation. + */ + FunctionNode* fn = static_cast<FunctionNode*>(fnNode); + QStringList leftParenSplit = fnArg.split('('); + if (leftParenSplit.size() > 1) { + QStringList rightParenSplit = leftParenSplit[1].split(')'); + if (rightParenSplit.size() > 0) { + QString params = rightParenSplit[0]; + if (!params.isEmpty()) { + QStringList commaSplit = params.split(','); + QVector<Parameter>& pvect = fn->parameters(); + if (pvect.size() == commaSplit.size()) { + for (int i = 0; i < pvect.size(); ++i) { + QStringList blankSplit = commaSplit[i].split(' '); + if (blankSplit.size() > 0) { + QString pName = blankSplit.last(); + int i = 0; + while (i < pName.length() && !pName.at(i).isLetter()) + i++; + if (i > 0) + pName = pName.mid(i); + if (!pName.isEmpty() && pName != pvect[i].name()) + pvect[i].setName(pName); + } + } + } + } + } + } + } } return fnNode; } @@ -1447,7 +1628,7 @@ Node* ClangCodeParser::parseFnArg(const Location& location, const QString& fnArg location.error(ClangCodeParser::tr("clang could not parse \\fn %1").arg(fnArg)); clang_disposeTranslationUnit(tu); clang_disposeIndex(index); - return 0; + return fnNode; } else { /* Always visit the tu if one is constructed, because diff --git a/src/qdoc/clangcodeparser.h b/src/qdoc/clangcodeparser.h index 7c3f750ce..7d9cf31cf 100644 --- a/src/qdoc/clangcodeparser.h +++ b/src/qdoc/clangcodeparser.h @@ -55,7 +55,11 @@ public: virtual void parseHeaderFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE; virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE; virtual void precompileHeaders() Q_DECL_OVERRIDE; - Node* parseFnArg(const Location& location, const QString& fnArg); + virtual Node *parseFnArg(const Location &location, const QString &fnArg) Q_DECL_OVERRIDE; + virtual Node *parseMacroArg(const Location &location, const QString ¯oArg) Q_DECL_OVERRIDE; + virtual Node *parseOtherFuncArg(const QString &topic, + const Location &location, + const QString &funcArg) Q_DECL_OVERRIDE; private: void getDefaultArgs(); diff --git a/src/qdoc/codeparser.h b/src/qdoc/codeparser.h index bab433757..6659e3eed 100644 --- a/src/qdoc/codeparser.h +++ b/src/qdoc/codeparser.h @@ -54,6 +54,9 @@ public: virtual void parseHeaderFile(const Location& location, const QString& filePath); virtual void parseSourceFile(const Location& location, const QString& filePath) = 0; virtual void precompileHeaders() { } + virtual Node *parseFnArg(const Location &, const QString &) { return 0; } + virtual Node *parseMacroArg(const Location &, const QString &) { return 0; } + virtual Node *parseOtherFuncArg(const QString &, const Location &, const QString &) { return 0; } bool isParsingH() const; bool isParsingCpp() const; diff --git a/src/qdoc/cppcodemarker.cpp b/src/qdoc/cppcodemarker.cpp index 8234f8676..8488260fd 100644 --- a/src/qdoc/cppcodemarker.cpp +++ b/src/qdoc/cppcodemarker.cpp @@ -389,7 +389,7 @@ QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary) QString CppCodeMarker::markedUpName(const Node *node) { QString name = linkTag(node, taggedNode(node)); - if (node->type() == Node::Function) + if (node->isFunction() && !node->isMacro()) name += "()"; return name; } diff --git a/src/qdoc/cppcodeparser.cpp b/src/qdoc/cppcodeparser.cpp index 07d0f8d4a..47cb455eb 100644 --- a/src/qdoc/cppcodeparser.cpp +++ b/src/qdoc/cppcodeparser.cpp @@ -46,7 +46,6 @@ QT_BEGIN_NAMESPACE /* qmake ignore Q_OBJECT */ static bool inMacroCommand_ = false; -static bool parsingHeaderFile_ = false; QStringList CppCodeParser::exampleFiles; QStringList CppCodeParser::exampleDirs; QSet<QString> CppCodeParser::excludeDirs; @@ -201,63 +200,6 @@ const QSet<QString>& CppCodeParser::topicCommands() } /*! - - */ -Node* CppCodeParser::processFnCommand(const ArgLocPair& arg, const Doc& doc) -{ - ExtraFuncData extra; - QStringList parentPath; - FunctionNode *func = 0; - FunctionNode *clone = 0; - - if (!makeFunctionNode(arg.first, &parentPath, &clone, extra) && - !makeFunctionNode("void " + arg.first, &parentPath, &clone, extra)) { - doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN)); - } - else { - func = qdb_->findFunctionNode(parentPath, clone); - if (func == 0) { - if (parentPath.isEmpty() && !lastPath_.isEmpty()) - func = qdb_->findFunctionNode(lastPath_, clone); - } - /* - If the node was not found, then search for it in the - open C++ namespaces. We don't expect this search to - be necessary often. Nor do we expect it to succeed - very often. - */ - if (func == 0) - func = qdb_->findNodeInOpenNamespace(parentPath, clone); - if (func) { - lastPath_ = parentPath; - } else if (isWorthWarningAbout(doc)) { - if (clone && clone->isSpecialMemberFunction()) { - ClassNode* cn = qdb_->findClassNode(parentPath); - if (cn) { - cn->addChild(clone); - clone->setImplicit(true); - return clone; - } - } - doc.location().warning(tr("Cannot find '%1' in '\\%2' %3") - .arg(clone->name() + "(...)") - .arg(COMMAND_FN) - .arg(arg.first), - tr("I cannot find any function of that name with the " - "specified signature. Make sure that the signature " - "is identical to the declaration, including 'const' " - "qualifiers.")); - } - if (func) { - func->borrowParameterNames(clone); - func->setParentPath(clone->parentPath()); - } - delete clone; - } - return func; -} - -/*! Process the topic \a command found in the \a doc with argument \a arg. */ Node* CppCodeParser::processTopicCommand(const Doc& doc, @@ -266,44 +208,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, { ExtraFuncData extra; if (command == COMMAND_FN) { - return processFnCommand(arg, doc); - } - else if (command == COMMAND_MACRO) { - QStringList parentPath; - FunctionNode *func = 0; - - extra.root = qdb_->primaryTreeRoot(); - extra.isMacro = true; - if (makeFunctionNode(arg.first, &parentPath, &func, extra)) { - if (!parentPath.isEmpty()) { - doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO)); - delete func; - func = 0; - } - else { - func->setMetaness(FunctionNode::MacroWithParams); - QVector<Parameter> params = func->parameters(); - for (int i = 0; i < params.size(); ++i) { - Parameter ¶m = params[i]; - if (param.name().isEmpty() && !param.dataType().isEmpty() - && param.dataType() != "...") - param = Parameter("", param.dataType()); - } - func->setParameters(params); - } - return func; - } - else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg.first)) { - func = new FunctionNode(qdb_->primaryTreeRoot(), arg.first); - func->setAccess(Node::Public); - func->setLocation(doc.startLocation()); - func->setMetaness(FunctionNode::MacroWithoutParams); - } - else { - doc.location().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO)); - - } - return func; + Q_UNREACHABLE(); } else if (nodeTypeMap.contains(command)) { /* @@ -465,50 +370,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, (command == COMMAND_JSMETHOD) || (command == COMMAND_JSATTACHEDSIGNAL) || (command == COMMAND_JSATTACHEDMETHOD)) { - QString module; - QString name; - QString type; - if (splitQmlMethodArg(arg.first, type, module, name, doc.location())) { - Aggregate* aggregate = qdb_->findQmlType(module, name); - if (!aggregate) - aggregate = qdb_->findQmlBasicType(module, name); - if (aggregate) { - bool attached = false; - Node::NodeType nodeType = Node::QmlMethod; - if ((command == COMMAND_QMLSIGNAL) || - (command == COMMAND_JSSIGNAL)) - nodeType = Node::QmlSignal; - else if ((command == COMMAND_QMLATTACHEDSIGNAL) || - (command == COMMAND_JSATTACHEDSIGNAL)) { - nodeType = Node::QmlSignal; - attached = true; - } - else if ((command == COMMAND_QMLMETHOD) || - (command == COMMAND_JSMETHOD)) { - // do nothing - } - else if ((command == COMMAND_QMLATTACHEDMETHOD) || - (command == COMMAND_JSATTACHEDMETHOD)) - attached = true; - else - return 0; // never get here. - FunctionNode* fn = makeFunctionNode(doc, - arg.first, - aggregate, - nodeType, - attached, - command); - if (fn) { - fn->setLocation(doc.startLocation()); - if ((command == COMMAND_JSSIGNAL) || - (command == COMMAND_JSMETHOD) || - (command == COMMAND_JSATTACHEDSIGNAL) || - (command == COMMAND_JSATTACHEDMETHOD)) - fn->setGenus(Node::JS); - } - return fn; - } - } + Q_UNREACHABLE(); } return 0; } @@ -596,57 +458,6 @@ bool CppCodeParser::splitQmlPropertyArg(const QString& arg, } /*! - A QML signal or method argument has the form... - - <type> <QML-type>::<name>(<param>, <param>, ...) - <type> <QML-module>::<QML-type>::<name>(<param>, <param>, ...) - - This function splits the \a{arg}ument into one of those - two forms, sets \a type, \a module, and \a qmlTypeName, - and returns true. If the argument doesn't match either - form, an error message is emitted and false is returned. - - \note The two QML types \e{Component} and \e{QtObject} never - have a module qualifier. - */ -bool CppCodeParser::splitQmlMethodArg(const QString& arg, - QString& type, - QString& module, - QString& qmlTypeName, - const Location& location) -{ - QString name; - int leftParen = arg.indexOf(QChar('(')); - if (leftParen > 0) - name = arg.left(leftParen); - else - name = arg; - int firstBlank = name.indexOf(QChar(' ')); - if (firstBlank > 0) { - type = name.left(firstBlank); - name = name.right(name.length() - firstBlank - 1); - } - else - type.clear(); - - QStringList colonSplit(name.split("::")); - if (colonSplit.size() > 1) { - if (colonSplit.size() > 2) { - module = colonSplit[0]; - qmlTypeName = colonSplit[1]; - } - else { - module.clear(); - qmlTypeName = colonSplit[0]; - } - return true; - } - QString msg = "Unrecognizable QML module/component qualifier for " + arg; - location.warning(tr(msg.toLatin1().data())); - return false; -} - -/*! Process the topic \a command group found in the \a doc with arguments \a args. Currently, this function is called only for \e{qmlproperty} @@ -1309,376 +1120,6 @@ bool CppCodeParser::matchParameter(QVector<Parameter>& pvect, bool& isQPrivateSi } /*! - If the current token is any of several function modifiers, - return that token value after reading the next token. If it - is not one of the function modieifer tokens, return -1 but - don\t read the next token. - */ -int CppCodeParser::matchFunctionModifier() -{ - switch (tok) { - case Tok_friend: - case Tok_inline: - case Tok_explicit: - case Tok_static: - case Tok_QT_DEPRECATED: - readToken(); - return tok; - case Tok_QT_COMPAT: - case Tok_QT_COMPAT_CONSTRUCTOR: - case Tok_QT_MOC_COMPAT: - case Tok_QT3_SUPPORT: - case Tok_QT3_SUPPORT_CONSTRUCTOR: - case Tok_QT3_MOC_SUPPORT: - readToken(); - return Tok_QT_COMPAT; - default: - break; - } - return -1; -} - -bool CppCodeParser::matchFunctionDecl(Aggregate *parent, - QStringList *parentPathPtr, - FunctionNode **funcPtr, - const QString &templateStuff, - ExtraFuncData& extra) -{ - CodeChunk returnType; - QStringList parentPath; - QString name; - - bool matched_QT_DEPRECATED = false; - bool matched_friend = false; - bool matched_static = false; - bool matched_inline = false; - bool matched_explicit = false; - bool matched_compat = false; - - int token = tok; - while (token != -1) { - switch (token) { - case Tok_friend: - matched_friend = true; - break; - case Tok_inline: - matched_inline = true; - break; - case Tok_explicit: - matched_explicit = true; - break; - case Tok_static: - matched_static = true; - break; - case Tok_QT_DEPRECATED: - matched_QT_DEPRECATED = true; - Q_FALLTHROUGH(); // no break here. - case Tok_QT_COMPAT: - matched_compat = true; - break; - } - token = matchFunctionModifier(); - } - - FunctionNode::Virtualness virtuality = FunctionNode::NonVirtual; - if (match(Tok_virtual)) { - virtuality = FunctionNode::NormalVirtual; - if (!matched_compat) - matched_compat = matchCompat(); - } - - if (!matchDataType(&returnType)) { - if (tokenizer->parsingFnOrMacro() - && (match(Tok_Q_DECLARE_FLAGS) || - match(Tok_Q_PROPERTY) || - match(Tok_Q_PRIVATE_PROPERTY))) - returnType = CodeChunk(previousLexeme()); - else { - return false; - } - } - - if (returnType.toString() == "QBool") - returnType = CodeChunk("bool"); - - if (!matched_compat) - matched_compat = matchCompat(); - - if (tok == Tok_operator && - (returnType.toString().isEmpty() || - returnType.toString().endsWith("::"))) { - // 'QString::operator const char *()' - parentPath = returnType.toString().split(sep); - parentPath.removeAll(QString()); - returnType = CodeChunk(); - readToken(); - - CodeChunk restOfName; - if (tok != Tok_Tilde && matchDataType(&restOfName)) { - name = "operator " + restOfName.toString(); - } - else { - name = previousLexeme() + lexeme(); - readToken(); - while (tok != Tok_LeftParen && tok != Tok_Eoi) { - name += lexeme(); - readToken(); - } - } - if (tok != Tok_LeftParen) { - return false; - } - } - else if (tok == Tok_LeftParen) { - // constructor or destructor - parentPath = returnType.toString().split(sep); - if (!parentPath.isEmpty()) { - name = parentPath.last(); - parentPath.erase(parentPath.end() - 1); - } - returnType = CodeChunk(); - } - else { - while (match(Tok_Ident)) { - name = previousLexeme(); - /* - This is a hack to let QML module identifiers through. - */ - matchModuleQualifier(name); - matchTemplateAngles(); - - if (match(Tok_Gulbrandsen)) - parentPath.append(name); - else - break; - } - - if (tok == Tok_operator) { - name = lexeme(); - readToken(); - while (tok != Tok_Eoi) { - name += lexeme(); - readToken(); - if (tok == Tok_LeftParen) - break; - } - } - if (parent && (tok == Tok_Semicolon || - tok == Tok_LeftBracket || - tok == Tok_Colon || tok == Tok_Equal) - && access != Node::Private) { - if (tok == Tok_LeftBracket) { - returnType.appendHotspot(); - - int bracketDepth0 = tokenizer->bracketDepth(); - while ((tokenizer->bracketDepth() >= bracketDepth0 && - tok != Tok_Eoi) || - tok == Tok_RightBracket) { - returnType.append(lexeme()); - readToken(); - } - if (tok != Tok_Semicolon) { - return false; - } - } - else if (tok == Tok_Colon || tok == Tok_Equal) { - returnType.appendHotspot(); - - while (tok != Tok_Semicolon && tok != Tok_Eoi) { - returnType.append(lexeme()); - readToken(); - } - if (tok != Tok_Semicolon) { - return false; - } - } - - VariableNode *var = new VariableNode(parent, name); - var->setAccess(access); - if (parsingHeaderFile_) - var->setLocation(declLoc()); - else - var->setLocation(location()); - var->setLeftType(returnType.left()); - var->setRightType(returnType.right()); - if (matched_compat) - var->setStatus(Node::Compat); - var->setStatic(matched_static); - return false; - } - if (tok != Tok_LeftParen) - return false; - } - readToken(); - - // A left paren was seen. Parse the parameters - bool isQPrivateSignal = false; - QVector<Parameter> pvect; - if (tok != Tok_RightParen) { - do { - if (!matchParameter(pvect, isQPrivateSignal)) - return false; - } while (match(Tok_Comma)); - } - // The parameters must end with a right paren - if (!match(Tok_RightParen)) - return false; - - // look for const - bool matchedConst = match(Tok_const); - bool matchedRef = match(Tok_Ampersand); - bool matchedRefRef = false; - if (match(Tok_Ampersand)) { - matchedRef = false; - matchedRefRef = true; - } - bool matchFinal = match(Tok_final); - bool matchOverride = match(Tok_override); - - bool isDeleted = false; - bool isDefaulted = false; - if (match(Tok_Equal)) { - if (match(Tok_Number)) // look for 0 indicating pure virtual - virtuality = FunctionNode::PureVirtual; - else if (match(Tok_delete)) - isDeleted = true; - else if (match(Tok_default)) - isDefaulted = true; - } - // look for colon indicating ctors which must be skipped - if (match(Tok_Colon)) { - while (tok != Tok_LeftBrace && tok != Tok_Eoi) - readToken(); - } - - // If no ';' expect a body, which must be skipped. - bool body_expected = false; - bool body_present = false; - if (!match(Tok_Semicolon) && tok != Tok_Eoi) { - body_expected = true; - int nesting = tokenizer->braceDepth(); - if (!match(Tok_LeftBrace)) - return false; - // skip the body - while (tokenizer->braceDepth() >= nesting && tok != Tok_Eoi) - readToken(); - body_present = true; - match(Tok_RightBrace); - } - - FunctionNode *func = 0; - bool createFunctionNode = false; - if (parsingHeaderFile_) { - if (matched_friend) { - if (matched_inline) { - // nothing yet - } - if (body_present) { - if (body_expected) { - // nothing yet - } - createFunctionNode = true; - if (parent && parent->parent()) - parent = parent->parent(); - else - return false; - } - } - else - createFunctionNode = true; - } - else - createFunctionNode = true; - - if (createFunctionNode) { - func = new FunctionNode(extra.type, parent, name, extra.isAttached); - if (matched_friend) - access = Node::Public; - func->setAccess(access); - if (parsingHeaderFile_) - func->setLocation(declLoc()); - else - func->setLocation(location()); - func->setReturnType(returnType.toString()); - func->setParentPath(parentPath); - func->setTemplateStuff(templateStuff); - if (matched_compat) - func->setStatus(Node::Compat); - if (matched_QT_DEPRECATED) - func->setStatus(Node::Deprecated); - if (matched_explicit) { /* What can be done? */ } - if (!pvect.isEmpty()) { - func->setParameters(pvect); - } - func->setMetaness(metaness_); - if ((parent && (name == parent->name())) || - (!parentPath.isEmpty() && name == parentPath.at(parentPath.size()-1))) { - FunctionNode::Metaness m = FunctionNode::Ctor; - if (!pvect.isEmpty()) { - for (int i=0; i<pvect.size(); i++) { - const Parameter& p = pvect.at(i); - if (p.dataType().contains(name)) { - if (p.dataType().endsWith(QLatin1String("&&"))) { - m = FunctionNode::MCtor; - break; - } - if (p.dataType().endsWith(QLatin1String("&"))) { - m = FunctionNode::CCtor; - break; - } - } - } - } - func->setMetaness(m); - } - else if (name.startsWith(QLatin1Char('~'))) - func->setMetaness(FunctionNode::Dtor); - else if (name == QLatin1String("operator=")) { - FunctionNode::Metaness m = FunctionNode::Plain; - if (pvect.size() == 1) { - if (parent) { - if (pvect.at(0).dataType().contains(parent->name())) { - if (pvect.at(0).dataType().endsWith(QLatin1String("&&"))) { - m = FunctionNode::MAssign; - } - else if (pvect.at(0).dataType().endsWith(QLatin1String("&"))) { - m = FunctionNode::CAssign; - } - } - } - else if (!parentPath.isEmpty()) { - if (pvect.at(0).dataType().contains(parentPath.at(parentPath.size()-1))) { - if (pvect.at(0).dataType().endsWith(QLatin1String("&&"))) { - m = FunctionNode::MAssign; - } - else if (pvect.at(0).dataType().endsWith(QLatin1String("&"))) { - m = FunctionNode::CAssign; - } - } - } - } - func->setMetaness(m); - } - func->setStatic(matched_static); - func->setConst(matchedConst); - func->setVirtualness(virtuality); - func->setIsDeleted(isDeleted); - func->setIsDefaulted(isDefaulted); - func->setFinal(matchFinal); - func->setOverride(matchOverride); - if (isQPrivateSignal) - func->setPrivateSignal(); - func->setRef(matchedRef); - func->setRefRef(matchedRefRef); - } - if (parentPathPtr != 0) - *parentPathPtr = parentPath; - if (funcPtr != 0) - *funcPtr = func; - return true; -} - -/*! Match a C++ \c using clause. Return \c true if the match is successful. Otherwise false. @@ -1752,37 +1193,6 @@ bool CppCodeParser::matchUsingDecl(Aggregate* parent) } /*! - This function uses a Tokenizer to parse the function \a signature - in an attempt to match it to the signature of a child node of \a root. - If a match is found, \a funcPtr is set to point to the matching node - and true is returned. - */ -bool CppCodeParser::makeFunctionNode(const QString& signature, - QStringList* parentPathPtr, - FunctionNode** funcPtr, - ExtraFuncData& extra) -{ - Tokenizer* outerTokenizer = tokenizer; - int outerTok = tok; - - QByteArray latin1 = signature.toLatin1(); - Location tmpLoc(signature); // FIXME give a proper location with a filename. - Tokenizer stringTokenizer(tokenizer ? location() : tmpLoc, latin1); - stringTokenizer.setParsingFnOrMacro(true); - tokenizer = &stringTokenizer; - readToken(); - - inMacroCommand_ = extra.isMacro; - bool ok = matchFunctionDecl(extra.root, parentPathPtr, funcPtr, QString(), extra); - inMacroCommand_ = false; - // potential memory leak with funcPtr - - tokenizer = outerTokenizer; - tok = outerTok; - return ok; -} - -/*! This function uses a Tokenizer to parse the \a parameters of a function into the parameter vector \a {pvect}. */ @@ -1810,34 +1220,6 @@ bool CppCodeParser::parseParameters(const QString& parameters, return true; } -/*! - Create a new FunctionNode for a QML method or signal, as - specified by \a type, as a child of \a parent. \a sig is - the complete signature, and if \a attached is true, the - method or signal is "attached". \a qdoctag is the text of - the \a type. - - \a parent is the QML class node. The QML module and QML - type names have already been consumed to find \a parent. - What remains in \a sig is the method signature. The method - must be a child of \a parent. - */ -FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc, - const QString& sig, - Aggregate* parent, - Node::NodeType type, - bool attached, - QString qdoctag) -{ - QStringList pp; - FunctionNode* fn = 0; - ExtraFuncData extra(parent, type, attached); - if (!makeFunctionNode(sig, &pp, &fn, extra) && !makeFunctionNode("void " + sig, &pp, &fn, extra)) { - doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag)); - } - return fn; -} - void CppCodeParser::createExampleFileNodes(DocumentNode *dn) { QString examplePath = dn->name(); diff --git a/src/qdoc/cppcodeparser.h b/src/qdoc/cppcodeparser.h index 61c68853a..ec79b0e35 100644 --- a/src/qdoc/cppcodeparser.h +++ b/src/qdoc/cppcodeparser.h @@ -72,7 +72,6 @@ public: protected: const QSet<QString>& topicCommands(); const QSet<QString>& otherMetaCommands(); - Node* processFnCommand(const ArgLocPair& arg, const Doc& doc); virtual Node* processTopicCommand(const Doc& doc, const QString& command, const ArgLocPair& arg); @@ -88,11 +87,6 @@ protected: QString& element, QString& name, const Location& location); - bool splitQmlMethodArg(const QString& arg, - QString& type, - QString& module, - QString& element, - const Location& location); virtual void processOtherMetaCommand(const Doc& doc, const QString& command, const ArgLocPair& argLocPair, @@ -114,24 +108,9 @@ protected: bool matchTemplateAngles(CodeChunk *type = 0); bool matchDataType(CodeChunk *type, QString *var = 0, bool qProp = false); bool matchParameter(QVector<Parameter>& pvect, bool& isQPrivateSignal); - bool matchFunctionDecl(Aggregate *parent, - QStringList *parentPathPtr, - FunctionNode **funcPtr, - const QString &templateStuff, - ExtraFuncData& extra); bool matchUsingDecl(Aggregate* parent); - bool makeFunctionNode(const QString &synopsis, - QStringList *parentPathPtr, - FunctionNode **funcPtr, - ExtraFuncData& params); - FunctionNode* makeFunctionNode(const Doc& doc, - const QString& sig, - Aggregate* parent, - Node::NodeType type, - bool attached, - QString qdoctag); void createExampleFileNodes(DocumentNode *dn); - int matchFunctionModifier(); + protected: QMap<QString, Node::NodeType> nodeTypeMap; Tokenizer *tokenizer; diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp index 4965bc368..06664169e 100644 --- a/src/qdoc/generator.cpp +++ b/src/qdoc/generator.cpp @@ -2168,7 +2168,7 @@ void Generator::initializeTextOutput() void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList) { - if (node->type() == Node::Function) { + if (node->isFunction() && !node->isMacro()) { const FunctionNode *func = static_cast<const FunctionNode *>(node); if (func->overloadNumber() == 0) { QString alternateName; diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp index a4fca58c6..feb83ea2a 100644 --- a/src/qdoc/node.cpp +++ b/src/qdoc/node.cpp @@ -186,11 +186,12 @@ void Node::removeRelates() /*! Returns this node's name member. Appends "()" to the returned - name, if this node is a function node. + name if this node is a function node, but not if it is a macro + because macro names normally appear without parentheses. */ QString Node::plainName() const { - if (type() == Node::Function) + if (isFunction() && !isMacro()) return name_ + QLatin1String("()"); return name_; } @@ -2220,7 +2221,31 @@ void FunctionNode::addParameter(const Parameter& parameter) } /*! + Split the parameters \a t and store them in this function's + Parameter vector. */ +void FunctionNode::setParameters(const QString &t) +{ + clearParams(); + if (!t.isEmpty()) { + QStringList commaSplit = t.split(','); + foreach (QString s, commaSplit) { + QStringList blankSplit = s.split(' '); + QString pName = blankSplit.last(); + blankSplit.removeLast(); + QString pType = blankSplit.join(' '); + int i = 0; + while (i < pName.length() && !pName.at(i).isLetter()) + i++; + if (i > 0) { + pType += QChar(' ') + pName.left(i); + pName = pName.mid(i); + } + addParameter(Parameter(pType, pName)); + } + } +} + void FunctionNode::borrowParameterNames(const FunctionNode *source) { QVector<Parameter>::Iterator t = parameters_.begin(); @@ -2320,17 +2345,22 @@ QString FunctionNode::signature(bool values, bool noReturnType) const QString s; if (!noReturnType && !returnType().isEmpty()) s = returnType() + QLatin1Char(' '); - s += name() + QLatin1Char('('); - QStringList reconstructedParameters = reconstructParameters(values); - int p = reconstructedParameters.size(); - if (p > 0) { - for (int i=0; i<p; i++) { - s += reconstructedParameters[i]; - if (i < (p-1)) - s += ", "; + s += name(); + if (!isMacroWithoutParams()) { + s += QLatin1Char('('); + QStringList reconstructedParameters = reconstructParameters(values); + int p = reconstructedParameters.size(); + if (p > 0) { + for (int i=0; i<p; i++) { + s += reconstructedParameters[i]; + if (i < (p-1)) + s += ", "; + } } + s += QLatin1Char(')'); + if (isMacro()) + return s; } - s += QLatin1Char(')'); if (isConst()) s += " const"; if (isRef()) @@ -2365,6 +2395,36 @@ void FunctionNode::debug() const } /*! + Compares this FunctionNode to the FunctionNode pointed to + by \a fn. Returns true if they describe the same function. + */ +bool FunctionNode::compare(const FunctionNode *fn) const +{ + if (!fn) + return false; + if (type() != fn->type()) + return false; + if (parent() != fn->parent()) + return false; + if (returnType_ != fn->returnType()) + return false; + if (isConst() != fn->isConst()) + return false; + if (isAttached() != fn->isAttached()) + return false; + const QVector<Parameter>& p = fn->parameters(); + if (parameters().size() != p.size()) + return false; + if (!p.isEmpty()) { + for (int i = 0; i < p.size(); ++i) { + if (parameters()[i].dataType() != p[i].dataType()) + return false; + } + } + return true; +} + +/*! \class PropertyNode This class describes one instance of using the Q_PROPERTY macro. diff --git a/src/qdoc/node.h b/src/qdoc/node.h index 46752a5fc..3f2e0a0e7 100644 --- a/src/qdoc/node.h +++ b/src/qdoc/node.h @@ -239,6 +239,7 @@ public: virtual bool isImplicit() const { return false; } virtual bool isInCollective() const { return false; } virtual bool isSharedCommentNode() const { return false; } + virtual bool isMacro() const { return false; } virtual void addMember(Node* ) { } virtual bool hasMembers() const { return false; } virtual bool hasNamespaces() const { return false; } @@ -935,6 +936,7 @@ public: void setReimplemented(bool b); void addParameter(const Parameter& parameter); inline void setParameters(const QVector<Parameter>& parameters); + void setParameters(const QString &t); void borrowParameterNames(const FunctionNode* source); void setReimplementedFrom(FunctionNode* from) { reimplementedFrom_ = from; } @@ -949,7 +951,9 @@ public: bool isSomeCtor() const { return isCtor() || isCCtor() || isMCtor(); } bool isMacroWithParams() const { return (metaness_ == MacroWithParams); } bool isMacroWithoutParams() const { return (metaness_ == MacroWithoutParams); } - bool isMacro() const { return (isMacroWithParams() || isMacroWithoutParams()); } + bool isMacro() const Q_DECL_OVERRIDE { + return (isMacroWithParams() || isMacroWithoutParams()); + } bool isSignal() const { return (metaness_ == Signal); } bool isSlot() const { return (metaness_ == Slot); } bool isCtor() const { return (metaness_ == Ctor); } @@ -982,6 +986,7 @@ public: virtual bool isJsMethod() const Q_DECL_OVERRIDE { return (type() == Node::QmlMethod) && (genus() == Node::JS); } + QVector<Parameter> ¶meters() { return parameters_; } const QVector<Parameter>& parameters() const { return parameters_; } void clearParams() { parameters_.clear(); } QStringList parameterNames() const; @@ -1042,6 +1047,7 @@ public: virtual bool hasTag(const QString& t) const Q_DECL_OVERRIDE { return (tag_ == t); } void setTag(const QString& t) { tag_ = t; } const QString &tag() const { return tag_; } + bool compare(const FunctionNode *fn) const; private: void addAssociatedProperty(PropertyNode* property); diff --git a/src/qdoc/puredocparser.cpp b/src/qdoc/puredocparser.cpp index 636c54d26..50c6a7bba 100644 --- a/src/qdoc/puredocparser.cpp +++ b/src/qdoc/puredocparser.cpp @@ -184,7 +184,23 @@ bool PureDocParser::processQdocComments() ArgList::ConstIterator a = args.cbegin(); while (a != args.cend()) { Doc nodeDoc = doc; - Node* node = processTopicCommand(nodeDoc,topic,*a); + Node *node = 0; + if (topic == COMMAND_FN) { + node = parserForLanguage("Clang")->parseFnArg(doc.location(), a->first); + } else if (topic == COMMAND_MACRO) { + node = parserForLanguage("Clang")->parseMacroArg(doc.location(), a->first); + } else if (topic == COMMAND_QMLSIGNAL || + topic == COMMAND_QMLMETHOD || + topic == COMMAND_QMLATTACHEDSIGNAL || + topic == COMMAND_QMLATTACHEDMETHOD || + topic == COMMAND_JSSIGNAL || + topic == COMMAND_JSMETHOD || + topic == COMMAND_JSATTACHEDSIGNAL || + topic == COMMAND_JSATTACHEDMETHOD) { + node = parseOtherFuncArg(topic, doc.location(), a->first); + } else { + node = processTopicCommand(nodeDoc, topic, *a); + } if (node != 0) { nodes.append(node); docs.append(nodeDoc); diff --git a/src/qdoc/qdocdatabase.h b/src/qdoc/qdocdatabase.h index 53a18a870..363f743f1 100644 --- a/src/qdoc/qdocdatabase.h +++ b/src/qdoc/qdocdatabase.h @@ -350,6 +350,7 @@ class QDocDatabase return forest_.getCollectionNode(name, genus); } Node *findFunctionNodeForTag(QString tag) { return primaryTree()->findFunctionNodeForTag(tag); } + Node* findMacroNode(const QString &t) { return primaryTree()->findMacroNode(t); } private: const Node* findNodeForTarget(QStringList& targetPath, diff --git a/src/qdoc/tree.cpp b/src/qdoc/tree.cpp index 6364c2560..4d288350c 100644 --- a/src/qdoc/tree.cpp +++ b/src/qdoc/tree.cpp @@ -1548,4 +1548,28 @@ Node* Tree::findFunctionNodeForTag(const QString &tag, Aggregate* parent) return 0; } +/*! + There should only be one macro node for macro name \a t. + The macro node is not built until the \macro command is seen. + */ +Node *Tree::findMacroNode(const QString &t, const Aggregate *parent) +{ + if (!parent) + parent = root(); + const NodeList &children = parent->childNodes(); + for (Node *n : children) { + if (n && (n->isMacro() || n->isFunction()) && n->name() == t) + return n; + } + for (Node *n : children) { + if (n && n->isAggregate()) { + Aggregate *a = static_cast<Aggregate*>(n); + n = findMacroNode(t, a); + if (n) + return n; + } + } + return 0; +} + QT_END_NAMESPACE diff --git a/src/qdoc/tree.h b/src/qdoc/tree.h index 711fac598..1aac6d664 100644 --- a/src/qdoc/tree.h +++ b/src/qdoc/tree.h @@ -217,6 +217,7 @@ class Tree TargetList* getTargetList(const QString& module); QStringList getTargetListKeys() { return targetListMap_->keys(); } Node* findFunctionNodeForTag(const QString &tag, Aggregate* parent = 0); + Node *findMacroNode(const QString &t, const Aggregate *parent = 0); public: const QString& camelCaseModuleName() const { return camelCaseModuleName_; } |