qdoc: Missing links to qRegisterMetaType()

A serious problem in the design of qdoc is its
C++ parser. It is an ad hoc recursive descent
parser, which has not kept pace with Qt's use
of the more esoteric aspects of C++. Part of
the problem is that qdoc does not send files
through the preprocessor before processing
them. The bottom line is qdoc needs the C++
parser used in Qt Creator.

But that is a long-term solution. In the short
term, we have to introduce minor hacks like
this one to keep qdoc going until the parser
can be replaced.

The problem in this case is that qdoc doesn't
handle the QT_PREPEND_NAMESPACE macro in
function declarations. The solution is to let
qdoc ignore the macro and just use the macro's
parameter, which is what qdoc wants anyway.

Task-number: QTBUG-28953
Change-Id: I5b9efcc10fa8fb500a44854ee995c2e50e9e16b5
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
Reviewed-by: Jerome Pasion <jerome.pasion@digia.com>
This commit is contained in:
Martin Smith 2013-02-06 13:41:04 +01:00 committed by The Qt Project
parent 934afb5c57
commit 4866fcf008
2 changed files with 72 additions and 39 deletions

View File

@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE
/* qmake ignore Q_OBJECT */
static bool inMacroCommand_ = false;
QStringList CppCodeParser::exampleFiles;
QStringList CppCodeParser::exampleDirs;
@ -311,13 +312,14 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
const QString& command,
const ArgLocPair& arg)
{
ExtraFuncData extra;
if (command == COMMAND_FN) {
QStringList parentPath;
FunctionNode *func = 0;
FunctionNode *clone = 0;
if (!makeFunctionNode(arg.first, &parentPath, &clone) &&
!makeFunctionNode("void " + arg.first, &parentPath, &clone)) {
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 {
@ -368,7 +370,9 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
QStringList parentPath;
FunctionNode *func = 0;
if (makeFunctionNode(arg.first, &parentPath, &func, qdb_->treeRoot())) {
extra.root = qdb_->treeRoot();
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;
@ -1152,7 +1156,6 @@ bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
if (--parenAndBraceDepth < 0)
return false;
}
if (dataType != 0)
dataType->append(lexeme());
readToken();
@ -1161,6 +1164,9 @@ bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
return matches;
}
/*
This function is no longer used.
*/
bool CppCodeParser::matchTemplateHeader()
{
readToken();
@ -1204,13 +1210,27 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
}
if (virgin) {
if (match(Tok_Ident))
dataType->append(previousLexeme());
if (match(Tok_Ident)) {
/*
This is a hack until we replace this "parser"
with the real one used in Qt Creator.
*/
if (!inMacroCommand_ && lexeme() == "(" &&
((previousLexeme() == "QT_PREPEND_NAMESPACE") || (previousLexeme() == "NS"))) {
readToken();
readToken();
dataType->append(previousLexeme());
readToken();
}
else
dataType->append(previousLexeme());
}
else if (match(Tok_void) || match(Tok_int) || match(Tok_char) ||
match(Tok_double) || match(Tok_Ellipsis))
dataType->append(previousLexeme());
else
else {
return false;
}
}
else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
dataType->append(previousLexeme());
@ -1242,8 +1262,9 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var)
dataType->appendHotspot();
if (var != 0 && match(Tok_Ident))
*var = previousLexeme();
if (!match(Tok_RightParen) || tok != Tok_LeftParen)
if (!match(Tok_RightParen) || tok != Tok_LeftParen) {
return false;
}
dataType->append(previousLexeme());
int parenDepth0 = tokenizer->parenDepth();
@ -1298,8 +1319,9 @@ bool CppCodeParser::matchParameter(FunctionNode *func)
QString name;
CodeChunk defaultValue;
if (!matchDataType(&dataType, &name))
if (!matchDataType(&dataType, &name)) {
return false;
}
match(Tok_Comment);
if (match(Tok_Equal)) {
int parenDepth0 = tokenizer->parenDepth();
@ -1312,10 +1334,7 @@ bool CppCodeParser::matchParameter(FunctionNode *func)
readToken();
}
}
func->addParameter(Parameter(dataType.toString(),
"",
name,
defaultValue.toString())); // ###
func->addParameter(Parameter(dataType.toString(), "", name, defaultValue.toString())); // ###
return true;
}
@ -1323,16 +1342,16 @@ bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
QStringList *parentPathPtr,
FunctionNode **funcPtr,
const QString &templateStuff,
Node::Type type,
bool attached)
ExtraFuncData& extra)
{
CodeChunk returnType;
QStringList parentPath;
QString name;
bool compat = false;
if (match(Tok_friend))
if (match(Tok_friend)) {
return false;
}
match(Tok_explicit);
if (matchCompat())
compat = true;
@ -1473,7 +1492,7 @@ bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
}
readToken();
FunctionNode *func = new FunctionNode(type, parent, name, attached);
FunctionNode *func = new FunctionNode(extra.type, parent, name, extra.isAttached);
func->setAccess(access);
func->setLocation(location());
func->setReturnType(returnType.toString());
@ -1899,6 +1918,7 @@ bool CppCodeParser::matchProperty(InnerNode *parent)
*/
bool CppCodeParser::matchDeclList(InnerNode *parent)
{
ExtraFuncData extra;
QString templateStuff;
int braceDepth0 = tokenizer->braceDepth();
if (tok == Tok_RightBrace) // prevents failure on empty body
@ -1921,7 +1941,12 @@ bool CppCodeParser::matchDeclList(InnerNode *parent)
matchUsingDecl();
break;
case Tok_template:
templateStuff = matchTemplateHeader();
{
CodeChunk dataType;
readToken();
matchTemplateAngles(&dataType);
templateStuff = dataType.toString();
}
continue;
case Tok_enum:
matchEnumDecl(parent);
@ -2017,13 +2042,14 @@ bool CppCodeParser::matchDeclList(InnerNode *parent)
match(Tok_RightParen);
break;
default:
if (!matchFunctionDecl(parent, 0, 0, templateStuff)) {
if (!matchFunctionDecl(parent, 0, 0, templateStuff, extra)) {
while (tok != Tok_Eoi &&
(tokenizer->braceDepth() > braceDepth0 ||
(!match(Tok_Semicolon) &&
tok != Tok_public && tok != Tok_protected &&
tok != Tok_private)))
tok != Tok_private))) {
readToken();
}
}
}
templateStuff.clear();
@ -2037,6 +2063,7 @@ bool CppCodeParser::matchDeclList(InnerNode *parent)
*/
bool CppCodeParser::matchDocsAndStuff()
{
ExtraFuncData extra;
QSet<QString> topicCommandsAllowed = topicCommands();
QSet<QString> otherMetacommandsAllowed = otherMetaCommands();
QSet<QString> metacommandsAllowed = topicCommandsAllowed +
@ -2082,7 +2109,7 @@ bool CppCodeParser::matchDocsAndStuff()
FunctionNode *clone;
FunctionNode *func = 0;
if (matchFunctionDecl(0, &parentPath, &clone)) {
if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) {
foreach (const QString& usedNamespace_, activeNamespaces_) {
QStringList newPath = usedNamespace_.split("::") + parentPath;
func = qdb_->findFunctionNode(newPath, clone);
@ -2176,7 +2203,7 @@ bool CppCodeParser::matchDocsAndStuff()
FunctionNode *clone;
FunctionNode *node = 0;
if (matchFunctionDecl(0, &parentPath, &clone)) {
if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) {
/*
The location of the definition is more interesting
than that of the declaration. People equipped with
@ -2209,9 +2236,7 @@ bool CppCodeParser::matchDocsAndStuff()
bool CppCodeParser::makeFunctionNode(const QString& signature,
QStringList* parentPathPtr,
FunctionNode** funcPtr,
InnerNode* root,
Node::Type type,
bool attached)
ExtraFuncData& extra)
{
Tokenizer* outerTokenizer = tokenizer;
int outerTok = tok;
@ -2223,7 +2248,9 @@ bool CppCodeParser::makeFunctionNode(const QString& signature,
tokenizer = &stringTokenizer;
readToken();
bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr, QString(), type, attached);
inMacroCommand_ = extra.isMacro;
bool ok = matchFunctionDecl(extra.root, parentPathPtr, funcPtr, QString(), extra);
inMacroCommand_ = false;
// potential memory leak with funcPtr
tokenizer = outerTokenizer;
@ -2252,15 +2279,14 @@ FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,
{
QStringList pp;
FunctionNode* fn = 0;
if (!makeFunctionNode(sig,&pp,&fn,parent,type,attached) &&
!makeFunctionNode("void "+sig,&pp,&fn,parent,type,attached)) {
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::parseQiteratorDotH(const Location &location,
const QString &filePath)
void CppCodeParser::parseQiteratorDotH(const Location &location, const QString &filePath)
{
QFile file(filePath);
if (!file.open(QFile::ReadOnly))

View File

@ -59,6 +59,16 @@ class CppCodeParser : public CodeParser
{
Q_DECLARE_TR_FUNCTIONS(QDoc::CppCodeParser)
struct ExtraFuncData {
InnerNode* root; // Used as the parent.
Node::Type type; // The node type: Function, etc.
bool isAttached; // If true, the method is attached.
bool isMacro; // If true, we are parsing a macro signature.
ExtraFuncData() : root(0), type(Node::Function), isAttached(false), isMacro(false) { }
ExtraFuncData(InnerNode* r, Node::Type t, bool a)
: root(r), type(t), isAttached(a), isMacro(false) { }
};
public:
CppCodeParser();
~CppCodeParser();
@ -114,11 +124,10 @@ protected:
bool matchDataType(CodeChunk *type, QString *var = 0);
bool matchParameter(FunctionNode *func);
bool matchFunctionDecl(InnerNode *parent,
QStringList *parentPathPtr = 0,
FunctionNode **funcPtr = 0,
const QString &templateStuff = QString(),
Node::Type type = Node::Function,
bool attached = false);
QStringList *parentPathPtr,
FunctionNode **funcPtr,
const QString &templateStuff,
ExtraFuncData& extra);
bool matchBaseSpecifier(ClassNode *classe, bool isClass);
bool matchBaseList(ClassNode *classe, bool isClass);
bool matchClassDecl(InnerNode *parent,
@ -134,9 +143,7 @@ protected:
bool makeFunctionNode(const QString &synopsis,
QStringList *parentPathPtr,
FunctionNode **funcPtr,
InnerNode *root = 0,
Node::Type type = Node::Function,
bool attached = false);
ExtraFuncData& params);
FunctionNode* makeFunctionNode(const Doc& doc,
const QString& sig,
InnerNode* parent,