summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/qdoc/clangcodeparser.cpp231
-rw-r--r--src/qdoc/clangcodeparser.h1
-rw-r--r--src/qdoc/main.cpp2
-rw-r--r--src/qdoc/node.h6
-rw-r--r--src/qdoc/qdocdatabase.h1
-rw-r--r--src/qdoc/tree.cpp25
-rw-r--r--src/qdoc/tree.h1
7 files changed, 256 insertions, 11 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp
index 4b455414f..983b0b9ba 100644
--- a/src/qdoc/clangcodeparser.cpp
+++ b/src/qdoc/clangcodeparser.cpp
@@ -151,7 +151,6 @@ QString functionName(CXCursor cursor)
return name;
}
-
/*!
Find the node from the QDocDatabase \a qdb that corrseponds to the declaration
represented by the cursor \a cur, if it exists.
@@ -239,11 +238,85 @@ static Node *findNodeForCursor(QDocDatabase* qdb, CXCursor cur) {
}
}
+/*!
+ Find the function node from the QDocDatabase \a qdb that
+ corrseponds to the declaration represented by the cursor
+ \a cur, if it exists.
+ */
+static Node *findFunctionNodeForCursor(QDocDatabase* qdb, CXCursor cur) {
+ auto kind = clang_getCursorKind(cur);
+ if (clang_isInvalid(kind))
+ return nullptr;
+ if (kind == CXCursor_TranslationUnit)
+ return qdb->primaryTreeRoot();
+
+ Node *p = findNodeForCursor(qdb, clang_getCursorSemanticParent(cur));
+ if (!p || !p->isAggregate())
+ return nullptr;
+ auto parent = static_cast<Aggregate *>(p);
+
+ switch (kind) {
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_CXXMethod:
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ case CXCursor_ConversionFunction: {
+ NodeList candidates;
+ parent->findChildren(functionName(cur), candidates);
+ if (candidates.isEmpty())
+ return nullptr;
+ CXType funcType = clang_getCursorType(cur);
+ auto numArg = clang_getNumArgTypes(funcType);
+ bool isVariadic = clang_isFunctionTypeVariadic(funcType);
+ QVarLengthArray<QString, 20> args;
+ for (Node *candidate : qAsConst(candidates)) {
+ if (!candidate->isFunction())
+ continue;
+ auto fn = static_cast<FunctionNode*>(candidate);
+ const auto &funcParams = fn->parameters();
+ const int actualArg = numArg - fn->isPrivateSignal();
+ if (funcParams.count() != (actualArg + isVariadic))
+ continue;
+ if (fn->isConst() != bool(clang_CXXMethod_isConst(cur)))
+ continue;
+ if (isVariadic && funcParams.last().dataType() != QLatin1String("..."))
+ continue;
+ bool different = false;
+ for (int i = 0; i < actualArg; i++) {
+ if (args.size() <= i)
+ args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i))));
+ QString t1 = funcParams.at(i).dataType();
+ QString t2 = args.at(i);
+ auto p2 = parent;
+ while (p2 && t1 != t2) {
+ QString parentScope = p2->name() + QLatin1String("::");
+ t1 = t1.remove(parentScope);
+ t2 = t2.remove(parentScope);
+ p2 = p2->parent();
+ }
+ if (t1 != t2) {
+ different = true;
+ break;
+ }
+ }
+ if (!different)
+ return fn;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return nullptr;
+}
+
class ClangVisitor {
public:
ClangVisitor(QDocDatabase *qdb, const QSet<QString> &allHeaders)
- : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders)
- {}
+ : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders) { }
+
+ QDocDatabase* qdocDB() { return qdb_; }
CXChildVisitResult visitChildren(CXCursor cursor)
{
@@ -272,11 +345,26 @@ public:
return ret ? CXChildVisit_Break : CXChildVisit_Continue;
}
+ /*
+ Not sure about all the possibilities, when the cursor
+ location is not in the main file.
+ */
+ CXChildVisitResult visitFnArg(CXCursor cursor, Node** fnNode, bool &ignoreSignature)
+ {
+ auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
+ auto loc = clang_getCursorLocation(cur);
+ if (clang_Location_isFromMainFile(loc))
+ return visitFnSignature(cur, loc, fnNode, ignoreSignature);
+ return CXChildVisit_Continue;
+ });
+ return ret ? CXChildVisit_Break : CXChildVisit_Continue;
+ }
+
Node *nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc);
private:
- /*! \class SimpleLoc
- Represents a simple location in the main source file,
- which can be used as a key in a QMap
+ /*!
+ SimpleLoc represents a simple location in the main source file,
+ which can be used as a key in a QMap.
*/
struct SimpleLoc
{
@@ -338,6 +426,7 @@ private:
CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
+ CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node** fnNode, bool &ignoreSignature);
void parseProperty(const QString &spelling, const Location &loc);
void readParameterNamesAndAttributes(FunctionNode* fn, CXCursor cursor);
Aggregate *getSemanticParent(CXCursor cursor);
@@ -377,6 +466,25 @@ Aggregate *ClangVisitor::getSemanticParent(CXCursor cursor)
return parent_;
}
+CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation , Node** fnNode, bool &ignoreSignature)
+{
+ switch (clang_getCursorKind(cursor)) {
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_CXXMethod:
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ case CXCursor_ConversionFunction: {
+ ignoreSignature = ignoredSymbol(functionName(cursor));
+ *fnNode = ignoreSignature ? 0 : findFunctionNodeForCursor(qdb_, cursor);
+ break;
+ }
+ default:
+ break;
+ }
+ return CXChildVisit_Continue;
+}
+
CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
{
auto kind = clang_getCursorKind(cursor);
@@ -475,6 +583,19 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
CXType funcType = clang_getCursorType(cursor);
FunctionNode* fn = new FunctionNode(Node::Function, parent_, name, false);
+
+ CXSourceRange range = clang_Cursor_getCommentRange(cursor);
+ if (!clang_Range_isNull(range)) {
+ QString comment = getSpelling(range);
+ if (comment.startsWith("//!")) {
+ int tag = comment.indexOf(QChar('['));
+ if (tag > 0) {
+ int end = comment.indexOf(QChar(']'), tag);
+ if (end > 0)
+ fn->setTag(comment.mid(tag, 1 + end - tag));
+ }
+ }
+ }
fn->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
fn->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
if (kind == CXCursor_Constructor
@@ -1044,11 +1165,11 @@ void ClangCodeParser::buildPCH()
qWarning() << "(qdoc) Could not save PCH file for " << module << error;
pchName_.clear();
}
-
// Visit the header now, as token from pre-compiled header won't be visited later
CXCursor cur = clang_getTranslationUnitCursor(tu);
ClangVisitor visitor(qdb_, allHeaders_);
visitor.visitChildren(cur);
+
clang_disposeTranslationUnit(tu);
} else {
pchFileDir_->remove();
@@ -1105,7 +1226,7 @@ void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QStrin
CXErrorCode err = clang_parseTranslationUnit2(index_, filePath.toLocal8Bit(), args_.data(),
args_.size(), nullptr, 0, flags_, &tu);
if (err || !tu) {
- qWarning() << "(qdoc) Could not parse " << filePath << " error code:" << err;
+ qWarning() << "(qdoc) Could not parse source file" << filePath << " error code:" << err;
clang_disposeIndex(index_);
return;
}
@@ -1216,7 +1337,7 @@ void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QStrin
while (a != args.constEnd()) {
Doc nodeDoc = doc;
if ((count > 1) && (topic == COMMAND_FN)) {
- node = processFnCommand(*a, doc);
+ node = parseFnArg(doc.location(), a->first);
if (node != 0) {
if (scn == 0) {
scn = new SharedCommentNode(node->parent(), count);
@@ -1228,7 +1349,10 @@ void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QStrin
}
}
else {
- node = processTopicCommand(nodeDoc, topic, *a);
+ if (topic == "fn")
+ node = parseFnArg(doc.location(), a->first);
+ else
+ node = processTopicCommand(nodeDoc, topic, *a);
if (node != 0) {
nodes.append(node);
docs.append(nodeDoc);
@@ -1264,4 +1388,91 @@ void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QStrin
clang_disposeIndex(index_);
}
+/*!
+ 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::parseFnArg(const Location& location, const QString& fnArg)
+{
+ Node* fnNode = 0;
+ if (Generator::preparing() && !Generator::singleExec())
+ return 0;
+ /*
+ If the \fn command begins with a tag, then don't try to
+ parse the \fn command with clang. Use the tag to search
+ for the correct function node. It is an error if it can
+ not be found. Return 0 in that case.
+ */
+ if (fnArg.startsWith('[')) {
+ int end = fnArg.indexOf(QChar(']', 0));
+ if (end > 1) {
+ QString tag = fnArg.left(end + 1);
+ fnNode = qdb_->findFunctionNodeForTag(tag);
+ if (!fnNode)
+ location.error(ClangCodeParser::tr("tag \\fn %1 not used in any include file in current module").arg(tag));
+ }
+ return fnNode;
+ }
+ CXTranslationUnit_Flags flags = (CXTranslationUnit_Flags) (CXTranslationUnit_Incomplete |
+ CXTranslationUnit_SkipFunctionBodies |
+ CXTranslationUnit_KeepGoing);
+ CXIndex index = clang_createIndex(1, 0);
+
+ std::vector<const char *> args(std::begin(defaultArgs_), std::end(defaultArgs_));
+ // Add the defines from the qdocconf file.
+ for (const auto &p : qAsConst(defines_))
+ args.push_back(p.constData());
+ if (!pchName_.isEmpty()) {
+ args.push_back("-w");
+ args.push_back("-include-pch");
+ args.push_back(pchName_.constData());
+ }
+ CXTranslationUnit tu;
+ QByteArray fn = fnArg.toUtf8();
+ if (!fn.endsWith(";"))
+ fn += "{ }";
+ const char *dummyFileName = "/fn_dummyfile.cpp";
+ CXUnsavedFile unsavedFile { dummyFileName, fn.constData(),
+ static_cast<unsigned long>(fn.size()) };
+ CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName,
+ args.data(),
+ args.size(),
+ &unsavedFile,
+ 1,
+ flags,
+ &tu);
+
+ if (err || !tu) {
+ location.error(ClangCodeParser::tr("clang could not parse \\fn %1").arg(fnArg));
+ clang_disposeTranslationUnit(tu);
+ clang_disposeIndex(index);
+ return 0;
+ } else {
+ /*
+ Always visit the tu if one is constructed, because
+ it might be possible to find the correct node, even
+ if clang detected diagnostics. Only bother to report
+ the diagnostics if they stop us finding the node.
+ */
+ CXCursor cur = clang_getTranslationUnitCursor(tu);
+ ClangVisitor visitor(qdb_, allHeaders_);
+ bool ignoreSignature = false;
+ visitor.visitFnArg(cur, &fnNode, ignoreSignature);
+ if (fnNode == 0) {
+ unsigned diagnosticCount = clang_getNumDiagnostics(tu);
+ if (diagnosticCount > 0) {
+ location.warning(ClangCodeParser::tr("clang found diagnostics parsing \\fn %1").arg(fnArg));
+ for (unsigned i = 0; i < diagnosticCount; ++i) {
+ CXDiagnostic diagnostic = clang_getDiagnostic(tu, i);
+ location.report(tr(" %1").arg(fromCXString(clang_formatDiagnostic(diagnostic, 0))));
+ }
+ }
+ }
+ }
+ clang_disposeTranslationUnit(tu);
+ clang_disposeIndex(index);
+ return fnNode;
+}
+
QT_END_NAMESPACE
diff --git a/src/qdoc/clangcodeparser.h b/src/qdoc/clangcodeparser.h
index e89228055..7c3f750ce 100644
--- a/src/qdoc/clangcodeparser.h
+++ b/src/qdoc/clangcodeparser.h
@@ -55,6 +55,7 @@ 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);
private:
void getDefaultArgs();
diff --git a/src/qdoc/main.cpp b/src/qdoc/main.cpp
index 8bdf0039d..3534abc0e 100644
--- a/src/qdoc/main.cpp
+++ b/src/qdoc/main.cpp
@@ -250,7 +250,6 @@ static void processQdocconfFile(const QString &fileName)
config.load(fileName);
QString project = config.getString(CONFIG_PROJECT);
QString moduleHeader = config.getString(CONFIG_MODULEHEADER);
- //qDebug() << "Start project:" << project;
/*
Add the defines to the configuration variables.
*/
@@ -273,6 +272,7 @@ static void processQdocconfFile(const QString &fileName)
phase += "prepare phase ";
else if (Generator::generating())
phase += "generate phase ";
+
QString msg = "Running qdoc for " + config.getString(CONFIG_PROJECT) + phase;
Location::logToStdErr(msg);
diff --git a/src/qdoc/node.h b/src/qdoc/node.h
index 89223260b..46752a5fc 100644
--- a/src/qdoc/node.h
+++ b/src/qdoc/node.h
@@ -281,6 +281,7 @@ public:
virtual void setObsoleteLink(const QString& ) { };
virtual void setQtVariable(const QString& ) { }
virtual QString qtVariable() const { return QString(); }
+ virtual bool hasTag(const QString& ) const { return false; }
const QMap<LinkType, QPair<QString,QString> >& links() const { return linkMap_; }
void setLink(LinkType linkType, const QString &link, const QString &desc);
@@ -1038,6 +1039,10 @@ public:
const SharedCommentNode* collective() const { return collective_; }
void setCollectiveNode(SharedCommentNode* t) Q_DECL_OVERRIDE { collective_ = t; }
+ 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_; }
+
private:
void addAssociatedProperty(PropertyNode* property);
@@ -1066,6 +1071,7 @@ private:
const FunctionNode* reimplementedFrom_;
PropNodeList associatedProperties_;
SharedCommentNode* collective_;
+ QString tag_;
};
class PropertyNode : public LeafNode
diff --git a/src/qdoc/qdocdatabase.h b/src/qdoc/qdocdatabase.h
index 0af094878..53a18a870 100644
--- a/src/qdoc/qdocdatabase.h
+++ b/src/qdoc/qdocdatabase.h
@@ -349,6 +349,7 @@ class QDocDatabase
const CollectionNode* getCollectionNode(const QString& name, Node::Genus genus) {
return forest_.getCollectionNode(name, genus);
}
+ Node *findFunctionNodeForTag(QString tag) { return primaryTree()->findFunctionNodeForTag(tag); }
private:
const Node* findNodeForTarget(QStringList& targetPath,
diff --git a/src/qdoc/tree.cpp b/src/qdoc/tree.cpp
index adc9f75b2..6364c2560 100644
--- a/src/qdoc/tree.cpp
+++ b/src/qdoc/tree.cpp
@@ -1523,4 +1523,29 @@ TargetList* Tree::getTargetList(const QString& module)
return targetListMap_->value(module);
}
+/*!
+ Search this tree recursively from \a parent to find a function
+ node with the specified \a tag. If no function node is found
+ with the required \a tag, return 0.
+ */
+Node* Tree::findFunctionNodeForTag(const QString &tag, Aggregate* parent)
+{
+ if (!parent)
+ parent = root();
+ const NodeList& children = parent->childNodes();
+ for (Node *n : children) {
+ if (n && n->isFunction() && n->hasTag(tag))
+ return n;
+ }
+ for (Node *n : children) {
+ if (n && n->isAggregate()) {
+ Aggregate* a = static_cast<Aggregate*>(n);
+ n = findFunctionNodeForTag(tag, a);
+ if (n)
+ return n;
+ }
+ }
+ return 0;
+}
+
QT_END_NAMESPACE
diff --git a/src/qdoc/tree.h b/src/qdoc/tree.h
index 0cb07aff7..711fac598 100644
--- a/src/qdoc/tree.h
+++ b/src/qdoc/tree.h
@@ -216,6 +216,7 @@ class Tree
bool broken);
TargetList* getTargetList(const QString& module);
QStringList getTargetListKeys() { return targetListMap_->keys(); }
+ Node* findFunctionNodeForTag(const QString &tag, Aggregate* parent = 0);
public:
const QString& camelCaseModuleName() const { return camelCaseModuleName_; }