summaryrefslogtreecommitdiffstats
path: root/src/tools/qdoc
diff options
context:
space:
mode:
authorMartin Smith <martin.smith@digia.com>2015-06-30 12:40:44 +0200
committerMartin Smith <martin.smith@digia.com>2015-07-07 12:59:00 +0000
commit68dec6461ec700e871f28c8672be1f28740910e5 (patch)
tree4a81c459b23b41ba2e6115a6e0804b0b5e1142d4 /src/tools/qdoc
parent6384c4e94041e588d1defbfd3b4dbbe2f611715c (diff)
qdoc: Parsing of inline friends
Some functions in qstring.h are marked with both friend and inline, and qdoc ignores them when it sees the friend keyword. Then, when qdoc finds the documentation for the functions in the cpp file, it reports an error that it can't associate the documentation with anything it saw in a .h file. This update corrects that problem. It also improves qdoc's parsing of other inline functions in other files. Change-Id: If94e403809af3ee3238eac0f2861b027197d6d3c Task-number: QTBUG-46531 Reviewed-by: Martin Smith <martin.smith@digia.com>
Diffstat (limited to 'src/tools/qdoc')
-rw-r--r--src/tools/qdoc/cppcodeparser.cpp236
-rw-r--r--src/tools/qdoc/cppcodeparser.h12
-rw-r--r--src/tools/qdoc/tokenizer.cpp2
-rw-r--r--src/tools/qdoc/tokenizer.h1
4 files changed, 177 insertions, 74 deletions
diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp
index ab79f8f320..1b18892e93 100644
--- a/src/tools/qdoc/cppcodeparser.cpp
+++ b/src/tools/qdoc/cppcodeparser.cpp
@@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE
/* qmake ignore Q_OBJECT */
static bool inMacroCommand_ = false;
+static bool parsingHeaderFile_ = false;
QStringList CppCodeParser::exampleFiles;
QStringList CppCodeParser::exampleDirs;
@@ -166,7 +167,9 @@ void CppCodeParser::parseHeaderFile(const Location& location, const QString& fil
Tokenizer fileTokenizer(fileLocation, in);
tokenizer = &fileTokenizer;
readToken();
+ parsingHeaderFile_ = true;
matchDeclList(qdb_->primaryTreeRoot());
+ parsingHeaderFile_ = false;
if (!fileTokenizer.version().isEmpty())
qdb_->setVersion(fileTokenizer.version());
in.close();
@@ -1076,8 +1079,7 @@ bool CppCodeParser::match(int target)
readToken();
return true;
}
- else
- return false;
+ return false;
}
/*!
@@ -1216,8 +1218,9 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
dataType->append(previousLexeme());
}
else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||
- match(Tok_double) || match(Tok_Ellipsis))
+ match(Tok_double) || match(Tok_Ellipsis)) {
dataType->append(previousLexeme());
+ }
else {
return false;
}
@@ -1303,36 +1306,71 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
return true;
}
-bool CppCodeParser::matchParameter(FunctionNode *func)
+/*!
+ Parse the next function parameter, if there is one, and
+ append it to parameter list \a p. Return true if a parameter
+ is parsed and appended to \a p. Otherwise return false.
+ */
+bool CppCodeParser::matchParameter(ParsedParameterList& pplist)
{
- CodeChunk dataType;
- QString name;
- CodeChunk defaultValue;
-
+ ParsedParameter pp;
if (match(Tok_QPrivateSignal)) {
- func->setPrivateSignal();
+ pp.qPrivateSignal_ = true;
+ pplist.append(pp);
return true;
}
- if (!matchDataType(&dataType, &name)) {
+ CodeChunk chunk;
+ if (!matchDataType(&chunk, &pp.name_)) {
return false;
}
+ pp.dataType_ = chunk.toString();
+ chunk.clear();
match(Tok_Comment);
if (match(Tok_Equal)) {
- int parenDepth0 = tokenizer->parenDepth();
-
- while (tokenizer->parenDepth() >= parenDepth0 &&
- (tok != Tok_Comma ||
- tokenizer->parenDepth() > parenDepth0) &&
+ int pdepth = tokenizer->parenDepth();
+ while (tokenizer->parenDepth() >= pdepth &&
+ (tok != Tok_Comma || (tokenizer->parenDepth() > pdepth)) &&
tok != Tok_Eoi) {
- defaultValue.append(lexeme());
+ chunk.append(lexeme());
readToken();
}
}
- func->addParameter(Parameter(dataType.toString(), "", name, defaultValue.toString())); // ###
+ pp.defaultValue_ = chunk.toString();
+ pplist.append(pp);
return true;
}
+/*!
+ 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,
@@ -1342,25 +1380,44 @@ bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
CodeChunk returnType;
QStringList parentPath;
QString name;
- bool compat = false;
- if (match(Tok_friend)) {
- return false;
- }
- match(Tok_explicit);
- if (matchCompat())
- compat = true;
- bool sta = false;
- if (match(Tok_static)) {
- sta = true;
- if (matchCompat())
- compat = true;
+ 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:
+ // no break here.
+ matched_QT_DEPRECATED = true;
+ case Tok_QT_COMPAT:
+ matched_compat = true;
+ break;
+ }
+ token = matchFunctionModifier();
}
- FunctionNode::Virtualness vir = FunctionNode::NonVirtual;
+
+ FunctionNode::Virtualness virtuality = FunctionNode::NonVirtual;
if (match(Tok_virtual)) {
- vir = FunctionNode::NormalVirtual;
- if (matchCompat())
- compat = true;
+ virtuality = FunctionNode::NormalVirtual;
+ if (!matched_compat)
+ matched_compat = matchCompat();
}
if (!matchDataType(&returnType)) {
@@ -1377,8 +1434,8 @@ bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
if (returnType.toString() == "QBool")
returnType = CodeChunk("bool");
- if (matchCompat())
- compat = true;
+ if (!matched_compat)
+ matched_compat = matchCompat();
if (tok == Tok_operator &&
(returnType.toString().isEmpty() ||
@@ -1417,12 +1474,10 @@ bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
else {
while (match(Tok_Ident)) {
name = previousLexeme();
-
/*
This is a hack to let QML module identifiers through.
*/
matchModuleQualifier(name);
-
matchTemplateAngles();
if (match(Tok_Gulbrandsen))
@@ -1476,68 +1531,105 @@ bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
var->setLocation(location());
var->setLeftType(returnType.left());
var->setRightType(returnType.right());
- if (compat)
+ if (matched_compat)
var->setStatus(Node::Compat);
- var->setStatic(sta);
+ var->setStatic(matched_static);
return false;
}
- if (tok != Tok_LeftParen) {
+ if (tok != Tok_LeftParen)
return false;
- }
}
readToken();
- FunctionNode *func = new FunctionNode(extra.type, parent, name, extra.isAttached);
- func->setAccess(access);
- func->setLocation(location());
- func->setReturnType(returnType.toString());
- func->setParentPath(parentPath);
- func->setTemplateStuff(templateStuff);
- if (compat)
- func->setStatus(Node::Compat);
-
- func->setMetaness(metaness_);
- if (parent) {
- if (name == parent->name()) {
- func->setMetaness(FunctionNode::Ctor);
- } else if (name.startsWith(QLatin1Char('~'))) {
- func->setMetaness(FunctionNode::Dtor);
- }
- }
- func->setStatic(sta);
-
+ // A left paren was seen. Parse the parameters
+ ParsedParameterList pplist;
if (tok != Tok_RightParen) {
do {
- if (!matchParameter(func)) {
+ if (!matchParameter(pplist))
return false;
- }
} while (match(Tok_Comma));
}
- if (!match(Tok_RightParen)) {
+ // The parameters must end with a right paren
+ if (!match(Tok_RightParen))
return false;
- }
- func->setConst(match(Tok_const));
+ // look for const
+ bool matchedConst = match(Tok_const);
+ // look for 0 indicating pure virtual
if (match(Tok_Equal) && match(Tok_Number))
- vir = FunctionNode::PureVirtual;
- func->setVirtualness(vir);
+ virtuality = FunctionNode::PureVirtual;
+ // 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) {
- int braceDepth0 = tokenizer->braceDepth();
-
- if (!match(Tok_LeftBrace)) {
+ body_expected = true;
+ int nesting = tokenizer->braceDepth();
+ if (!match(Tok_LeftBrace))
return false;
- }
- while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi)
+ // 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 (body_present) {
+ 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);
+ func->setAccess(access);
+ 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? */ }
+ func->setMetaness(metaness_);
+ if (parent) {
+ if (name == parent->name())
+ func->setMetaness(FunctionNode::Ctor);
+ else if (name.startsWith(QLatin1Char('~')))
+ func->setMetaness(FunctionNode::Dtor);
+ }
+ func->setStatic(matched_static);
+ func->setConst(matchedConst);
+ func->setVirtualness(virtuality);
+ if (!pplist.isEmpty()) {
+ foreach (const ParsedParameter& pp, pplist) {
+ if (pp.qPrivateSignal_)
+ func->setPrivateSignal();
+ else
+ func->addParameter(Parameter(pp.dataType_, "", pp.name_, pp.defaultValue_));
+ }
+ }
+ }
if (parentPathPtr != 0)
*parentPathPtr = parentPath;
if (funcPtr != 0)
diff --git a/src/tools/qdoc/cppcodeparser.h b/src/tools/qdoc/cppcodeparser.h
index 31964699a2..4fb428fc72 100644
--- a/src/tools/qdoc/cppcodeparser.h
+++ b/src/tools/qdoc/cppcodeparser.h
@@ -51,6 +51,15 @@ class CppCodeParser : public CodeParser
{
Q_DECLARE_TR_FUNCTIONS(QDoc::CppCodeParser)
+ struct ParsedParameter {
+ bool qPrivateSignal_;
+ QString dataType_;
+ QString name_;
+ QString defaultValue_;
+ ParsedParameter() : qPrivateSignal_(false) { }
+ };
+ typedef QList<ParsedParameter> ParsedParameterList;
+
struct ExtraFuncData {
Aggregate* root; // Used as the parent.
Node::NodeType type; // The node type: Function, etc.
@@ -116,7 +125,7 @@ protected:
bool matchTemplateAngles(CodeChunk *type = 0);
bool matchTemplateHeader();
bool matchDataType(CodeChunk *type, QString *var = 0);
- bool matchParameter(FunctionNode *func);
+ bool matchParameter(ParsedParameterList& pplist);
bool matchFunctionDecl(Aggregate *parent,
QStringList *parentPathPtr,
FunctionNode **funcPtr,
@@ -149,6 +158,7 @@ protected:
const QString &includeFile,
const QString &macroDef);
void createExampleFileNodes(DocumentNode *dn);
+ int matchFunctionModifier();
protected:
QMap<QString, Node::NodeType> nodeTypeMap;
diff --git a/src/tools/qdoc/tokenizer.cpp b/src/tools/qdoc/tokenizer.cpp
index 68c6fb7831..a85c3b00d8 100644
--- a/src/tools/qdoc/tokenizer.cpp
+++ b/src/tools/qdoc/tokenizer.cpp
@@ -186,7 +186,7 @@ int Tokenizer::getToken()
}
else if (strcmp(yyLex, kwords[i - 1]) == 0) {
int ret = (int) Tok_FirstKeyword + i - 1;
- if (ret != Tok_explicit && ret != Tok_inline && ret != Tok_typename)
+ if (ret != Tok_typename)
return ret;
break;
}
diff --git a/src/tools/qdoc/tokenizer.h b/src/tools/qdoc/tokenizer.h
index 2b79320567..41a3ffd931 100644
--- a/src/tools/qdoc/tokenizer.h
+++ b/src/tools/qdoc/tokenizer.h
@@ -107,6 +107,7 @@ public:
int braceDepth() const { return yyBraceDepth; }
int parenDepth() const { return yyParenDepth; }
int bracketDepth() const { return yyBracketDepth; }
+ Location& tokenLocation() { return yyTokLoc; }
static void initialize(const Config &config);
static void terminate();