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>
This commit is contained in:
parent
6384c4e940
commit
68dec6461e
@ -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;
|
||||
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();
|
||||
}
|
||||
match(Tok_explicit);
|
||||
if (matchCompat())
|
||||
compat = true;
|
||||
bool sta = false;
|
||||
if (match(Tok_static)) {
|
||||
sta = true;
|
||||
if (matchCompat())
|
||||
compat = true;
|
||||
}
|
||||
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)
|
||||
|
@ -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 ¯oDef);
|
||||
void createExampleFileNodes(DocumentNode *dn);
|
||||
int matchFunctionModifier();
|
||||
|
||||
protected:
|
||||
QMap<QString, Node::NodeType> nodeTypeMap;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user