summaryrefslogtreecommitdiff
path: root/src/qdoc/clangcodeparser.cpp
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/qdoc/clangcodeparser.cpp
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/qdoc/clangcodeparser.cpp')
-rw-r--r--src/qdoc/clangcodeparser.cpp203
1 files changed, 192 insertions, 11 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