summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Smith <martin.smith@qt.io>2017-08-03 11:02:51 +0200
committerMartin Smith <martin.smith@qt.io>2017-09-08 10:05:15 +0000
commitcceda3d0bc93996afb26cd569255b041765f4d5b (patch)
tree918147fa36da90707d10bfa34bfb9a6812869241 /src
parent771e5010359607f47c1ca149ffd8d75203742186 (diff)
downloadqttools-cceda3d0bc93996afb26cd569255b041765f4d5b.tar.gz
qdoc: This ends use of qdoc's old C++ parser
This change replaces the last uses of qdoc's old, ad hoc C++ parser. Clang is now used for parsing all C++ code. \macro, \qmlxxx, and \jsxxx commands are parsed by simple pattern matching functions using QString::split(). Change-Id: If6f95b0487d1dd3206373bc55ec8e6b8b9c55b1e Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qdoc/clangcodeparser.cpp203
-rw-r--r--src/qdoc/clangcodeparser.h6
-rw-r--r--src/qdoc/codeparser.h3
-rw-r--r--src/qdoc/cppcodemarker.cpp2
-rw-r--r--src/qdoc/cppcodeparser.cpp622
-rw-r--r--src/qdoc/cppcodeparser.h23
-rw-r--r--src/qdoc/generator.cpp2
-rw-r--r--src/qdoc/node.cpp82
-rw-r--r--src/qdoc/node.h8
-rw-r--r--src/qdoc/puredocparser.cpp18
-rw-r--r--src/qdoc/qdocdatabase.h1
-rw-r--r--src/qdoc/tree.cpp24
-rw-r--r--src/qdoc/tree.h1
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 &macroArg) 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 &param = 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> &parameters() { 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_; }