qdoc: Show correct method signatures for QML types

The return type was not printed, for \qmlmethod commands used
in QML files. The parameter types and default values weren't
being printed either. This update corrects those problems.

Change-Id: I68301fb2040c9dc5f5650e7dd3fee9c42197e3f7
Task-number: QTBUG-44064
Reviewed-by: Caroline Chao <caroline.chao@theqtcompany.com>
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
This commit is contained in:
Martin Smith 2015-03-11 14:58:54 +01:00
parent 53207e820c
commit 70f92f8cee
5 changed files with 203 additions and 0 deletions

View File

@ -76,6 +76,7 @@ public:
void appendHotspot();
bool isEmpty() const { return s.isEmpty(); }
void clear() { s.clear(); }
QString toString() const;
QStringList toPath() const;
QString left() const { return s.left(hotspot == -1 ? s.length() : hotspot); }

View File

@ -3349,6 +3349,7 @@ void HtmlGenerator::generateSectionInheritedList(const Section& section, const N
}
}
// generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);
void HtmlGenerator::generateSynopsis(const Node *node,
const Node *relative,
CodeMarker *marker,

View File

@ -1863,6 +1863,7 @@ void FunctionNode::setReimp(bool r)
}
/*!
Append \a parameter to the parameter list.
*/
void FunctionNode::addParameter(const Parameter& parameter)
{

View File

@ -904,6 +904,7 @@ public:
}
int overloadNumber() const;
const QList<Parameter>& parameters() const { return params; }
void clearParams() { params.clear(); }
QStringList parameterNames() const;
QString rawParameters(bool names = false, bool values = false) const;
const FunctionNode* reimplementedFrom() const { return rf; }

View File

@ -42,6 +42,7 @@
#include "codeparser.h"
#include "qmlvisitor.h"
#include "qdocdatabase.h"
#include "tokenizer.h"
QT_BEGIN_NAMESPACE
@ -146,6 +147,29 @@ QQmlJS::AST::SourceLocation QmlDocVisitor::precedingComment(quint32 offset) cons
return QQmlJS::AST::SourceLocation();
}
class QmlSignatureParser
{
public:
QmlSignatureParser(FunctionNode* func, const QString& signature, const Location& loc);
void readToken() { tok_ = tokenizer_->getToken(); }
QString lexeme() { return tokenizer_->lexeme(); }
QString previousLexeme() { return tokenizer_->previousLexeme(); }
bool match(int target);
bool matchDataType(CodeChunk* dataType, QString* var);
bool matchParameter();
bool matchFunctionDecl();
private:
QString signature_;
QStringList names_;
QString funcName_;
Tokenizer* tokenizer_;
int tok_;
FunctionNode* func_;
const Location& location_;
};
/*!
Finds the nearest unused qdoc comment above the QML entity
represented by the \a node and processes the qdoc commands
@ -216,6 +240,13 @@ bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Nod
else
qDebug() << " FAILED TO PARSE QML OR JS PROPERTY:" << topic << args;
}
else if ((topic == COMMAND_QMLMETHOD) || (topic == COMMAND_QMLATTACHEDMETHOD) ||
(topic == COMMAND_JSMETHOD) || (topic == COMMAND_JSATTACHEDMETHOD)) {
if (node->isFunction()) {
FunctionNode* fn = static_cast<FunctionNode*>(node);
QmlSignatureParser qsp(fn, args, doc.location());
}
}
}
}
for (int i=0; i<nodes.size(); ++i)
@ -232,6 +263,174 @@ bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Nod
return false;
}
QmlSignatureParser::QmlSignatureParser(FunctionNode* func, const QString& signature, const Location& loc)
: signature_(signature), func_(func), location_(loc)
{
QByteArray latin1 = signature.toLatin1();
Tokenizer stringTokenizer(location_, latin1);
stringTokenizer.setParsingFnOrMacro(true);
tokenizer_ = &stringTokenizer;
readToken();
matchFunctionDecl();
}
/*!
If the current token matches \a target, read the next
token and return true. Otherwise, don't read the next
token, and return false.
*/
bool QmlSignatureParser::match(int target)
{
if (tok_ == target) {
readToken();
return true;
}
return false;
}
/*!
Parse a QML data type into \a dataType and an optional
variable name into \a var.
*/
bool QmlSignatureParser::matchDataType(CodeChunk* dataType, QString* var)
{
/*
This code is really hard to follow... sorry. The loop is there to match
Alpha::Beta::Gamma::...::Omega.
*/
for (;;) {
bool virgin = true;
if (tok_ != Tok_Ident) {
while (match(Tok_signed) ||
match(Tok_unsigned) ||
match(Tok_short) ||
match(Tok_long) ||
match(Tok_int64)) {
dataType->append(previousLexeme());
virgin = false;
}
}
if (virgin) {
if (match(Tok_Ident)) {
dataType->append(previousLexeme());
}
else if (match(Tok_void) ||
match(Tok_int) ||
match(Tok_char) ||
match(Tok_double) ||
match(Tok_Ellipsis))
dataType->append(previousLexeme());
else
return false;
}
else if (match(Tok_int) ||
match(Tok_char) ||
match(Tok_double)) {
dataType->append(previousLexeme());
}
if (match(Tok_Gulbrandsen))
dataType->append(previousLexeme());
else
break;
}
while (match(Tok_Ampersand) ||
match(Tok_Aster) ||
match(Tok_const) ||
match(Tok_Caret))
dataType->append(previousLexeme());
/*
The usual case: Look for an optional identifier, then for
some array brackets.
*/
dataType->appendHotspot();
if ((var != 0) && match(Tok_Ident))
*var = previousLexeme();
if (tok_ == Tok_LeftBracket) {
int bracketDepth0 = tokenizer_->bracketDepth();
while ((tokenizer_->bracketDepth() >= bracketDepth0 && tok_ != Tok_Eoi) ||
tok_ == Tok_RightBracket) {
dataType->append(lexeme());
readToken();
}
}
return true;
}
bool QmlSignatureParser::matchParameter()
{
QString name;
CodeChunk dataType;
CodeChunk defaultValue;
bool result = matchDataType(&dataType, &name);
if (name.isEmpty()) {
name = dataType.toString();
dataType.clear();
}
if (!result)
return false;
if (match(Tok_Equal)) {
int parenDepth0 = tokenizer_->parenDepth();
while (tokenizer_->parenDepth() >= parenDepth0 &&
(tok_ != Tok_Comma ||
tokenizer_->parenDepth() > parenDepth0) &&
tok_ != Tok_Eoi) {
defaultValue.append(lexeme());
readToken();
}
}
func_->addParameter(Parameter(dataType.toString(), "", name, defaultValue.toString()));
return true;
}
bool QmlSignatureParser::matchFunctionDecl()
{
CodeChunk returnType;
int firstBlank = signature_.indexOf(QChar(' '));
int leftParen = signature_.indexOf(QChar('('));
if ((firstBlank > 0) && (leftParen - firstBlank) > 1) {
if (!matchDataType(&returnType, 0))
return false;
}
while (match(Tok_Ident)) {
names_.append(previousLexeme());
if (!match(Tok_Gulbrandsen)) {
funcName_ = previousLexeme();
names_.pop_back();
break;
}
}
if (tok_ != Tok_LeftParen)
return false;
readToken();
func_->setLocation(location_);
func_->setReturnType(returnType.toString());
if (tok_ != Tok_RightParen) {
func_->clearParams();
do {
if (!matchParameter())
return false;
} while (match(Tok_Comma));
}
if (!match(Tok_RightParen))
return false;
return true;
}
/*!
A QML property argument has the form...