--- a/tools/qdoc3/cppcodeparser.cpp Tue Jan 26 12:42:25 2010 +0200
+++ b/tools/qdoc3/cppcodeparser.cpp Tue Feb 02 00:43:10 2010 +0200
@@ -91,7 +91,9 @@
#define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty")
#define COMMAND_QMLINHERITS Doc::alias("inherits")
#define COMMAND_QMLSIGNAL Doc::alias("qmlsignal")
+#define COMMAND_QMLATTACHEDSIGNAL Doc::alias("qmlattachedsignal")
#define COMMAND_QMLMETHOD Doc::alias("qmlmethod")
+#define COMMAND_QMLATTACHEDMETHOD Doc::alias("qmlattachedmethod")
#define COMMAND_QMLDEFAULT Doc::alias("default")
#endif
@@ -195,8 +197,14 @@
*/
CppCodeParser::~CppCodeParser()
{
+ // nothing.
}
+/*!
+ The constructor initializes a map of special node types
+ for identifying important nodes. And it initializes
+ some filters for identifying certain kinds of files.
+ */
void CppCodeParser::initializeParser(const Config &config)
{
CodeParser::initializeParser(config);
@@ -218,24 +226,46 @@
exampleNameFilter = exampleFilePatterns.join(" ");
else
exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui";
+
+ QStringList exampleImagePatterns = config.getStringList(
+ CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
+
+ if (!exampleImagePatterns.isEmpty())
+ exampleImageFilter = exampleImagePatterns.join(" ");
+ else
+ exampleImageFilter = "*.png";
}
+/*!
+ Clear the map of common node types and call
+ the same function in the base class.
+ */
void CppCodeParser::terminateParser()
{
nodeTypeMap.clear();
CodeParser::terminateParser();
}
+/*!
+ Returns "Cpp".
+ */
QString CppCodeParser::language()
{
return "Cpp";
}
+/*!
+ Returns a list of extensions for header files.
+ */
QString CppCodeParser::headerFileNameFilter()
{
return "*.ch *.h *.h++ *.hh *.hpp *.hxx";
}
+/*!
+ Returns a list of extensions for source files, i.e. not
+ header files.
+ */
QString CppCodeParser::sourceFileNameFilter()
{
return "*.c++ *.cc *.cpp *.cxx";
@@ -297,6 +327,12 @@
fclose(in);
}
+/*!
+ This is called after all the header files have been parsed.
+ I think the most important thing it does is resolve class
+ inheritance links in the tree. But it also initializes a
+ bunch of stuff.
+ */
void CppCodeParser::doneParsingHeaderFiles(Tree *tree)
{
tree->resolveInheritance();
@@ -343,6 +379,12 @@
mutableAssociativeIteratorClasses.clear();
}
+/*!
+ This is called after all the source files (i.e., not the
+ header files) have been parsed. It traverses the tree to
+ resolve property links, normalize overload signatures, and
+ do other housekeeping of the tree.
+ */
void CppCodeParser::doneParsingSourceFiles(Tree *tree)
{
tree->root()->makeUndocumentedChildrenInternal();
@@ -351,6 +393,13 @@
tree->resolveProperties();
}
+/*!
+ This function searches the \a tree to find a FunctionNode
+ for a function with the signature \a synopsis. If the
+ \a relative node is provided, the search begins there. If
+ \a fuzzy is true, base classes are searched. The function
+ node is returned, if found.
+ */
const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis,
Tree *tree,
Node *relative,
@@ -485,7 +534,9 @@
<< COMMAND_QMLPROPERTY
<< COMMAND_QMLATTACHEDPROPERTY
<< COMMAND_QMLSIGNAL
- << COMMAND_QMLMETHOD;
+ << COMMAND_QMLATTACHEDSIGNAL
+ << COMMAND_QMLMETHOD
+ << COMMAND_QMLATTACHEDMETHOD;
#else
<< COMMAND_VARIABLE;
#endif
@@ -678,18 +729,26 @@
return new QmlClassNode(tre->root(), names[0], classNode);
}
else if ((command == COMMAND_QMLSIGNAL) ||
- (command == COMMAND_QMLMETHOD)) {
+ (command == COMMAND_QMLMETHOD) ||
+ (command == COMMAND_QMLATTACHEDSIGNAL) ||
+ (command == COMMAND_QMLATTACHEDMETHOD)) {
QString element;
- QString name;
+ QString type;
QmlClassNode* qmlClass = 0;
- if (splitQmlArg(doc,arg,element,name)) {
+ if (splitQmlMethodArg(doc,arg,type,element)) {
Node* n = tre->findNode(QStringList(element),Node::Fake);
if (n && n->subType() == Node::QmlClass) {
qmlClass = static_cast<QmlClassNode*>(n);
if (command == COMMAND_QMLSIGNAL)
- return new QmlSignalNode(qmlClass,name);
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,false,COMMAND_QMLSIGNAL);
+ else if (command == COMMAND_QMLATTACHEDSIGNAL)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,true,COMMAND_QMLATTACHEDSIGNAL);
+ else if (command == COMMAND_QMLMETHOD)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,false,COMMAND_QMLMETHOD);
+ else if (command == COMMAND_QMLATTACHEDMETHOD)
+ return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,true,COMMAND_QMLATTACHEDMETHOD);
else
- return new QmlMethodNode(qmlClass,name);
+ return 0; // never get here.
}
}
}
@@ -705,15 +764,15 @@
<type> <element>::<name>
This function splits the argument into those three
- parts, sets \a type, \a element, and \a property,
+ parts, sets \a type, \a element, and \a name,
and returns true. If any of the parts isn't found,
- a debug message is output and false is returned.
+ a qdoc warning is output and false is returned.
*/
bool CppCodeParser::splitQmlPropertyArg(const Doc& doc,
const QString& arg,
QString& type,
QString& element,
- QString& property)
+ QString& name)
{
QStringList blankSplit = arg.split(" ");
if (blankSplit.size() > 1) {
@@ -721,40 +780,47 @@
QStringList colonSplit(blankSplit[1].split("::"));
if (colonSplit.size() > 1) {
element = colonSplit[0];
- property = colonSplit[1];
+ name = colonSplit[1];
return true;
}
else
- doc.location().warning(tr("Missing QML element name or property name"));
+ doc.location().warning(tr("Missing parent QML element name"));
}
else
- doc.location().warning(tr("Missing QML property type or property path"));
+ doc.location().warning(tr("Missing property type"));
return false;
}
/*!
A QML signal or method argument has the form...
- <element>::<name>
+ <type> <element>::<name>(<param>, <param>, ...)
This function splits the argument into those two
parts, sets \a element, and \a name, and returns
true. If either of the parts isn't found, a debug
message is output and false is returned.
*/
-bool CppCodeParser::splitQmlArg(const Doc& doc,
- const QString& arg,
- QString& element,
- QString& name)
+bool CppCodeParser::splitQmlMethodArg(const Doc& doc,
+ const QString& arg,
+ QString& type,
+ QString& element)
{
QStringList colonSplit(arg.split("::"));
if (colonSplit.size() > 1) {
- element = colonSplit[0];
- name = colonSplit[1];
+ QStringList blankSplit = colonSplit[0].split(" ");
+ if (blankSplit.size() > 1) {
+ type = blankSplit[0];
+ element = blankSplit[1];
+ }
+ else {
+ type = QString("");
+ element = colonSplit[0];
+ }
return true;
}
else
- doc.location().warning(tr("Missing QML element name or signal/method name"));
+ doc.location().warning(tr("Missing parent QML element or method signature"));
return false;
}
@@ -787,14 +853,26 @@
}
}
if (qmlPropGroup) {
- new QmlPropertyNode(qmlPropGroup,property,type,attached);
+ const ClassNode *correspondingClass = static_cast<const QmlClassNode*>(qmlPropGroup->parent())->classNode();
+ PropertyNode *correspondingProperty = 0;
+ if (correspondingClass)
+ correspondingProperty = static_cast<PropertyNode*>((Node*)correspondingClass->findNode(property, Node::Property));
+ QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached);
+ if (correspondingProperty) {
+ bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');
+ qmlPropNode->setWritable(writableList || correspondingProperty->isWritable());
+ }
++arg;
while (arg != args.end()) {
if (splitQmlPropertyArg(doc,(*arg),type,element,property)) {
- new QmlPropertyNode(qmlPropGroup,
- property,
- type,
- attached);
+ QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup,
+ property,
+ type,
+ attached);
+ if (correspondingProperty) {
+ bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*');
+ qmlPropNode->setWritable(writableList || correspondingProperty->isWritable());
+ }
}
++arg;
}
@@ -1241,7 +1319,9 @@
bool CppCodeParser::matchFunctionDecl(InnerNode *parent,
QStringList *parentPathPtr,
FunctionNode **funcPtr,
- const QString &templateStuff)
+ const QString &templateStuff,
+ Node::Type type,
+ bool attached)
{
CodeChunk returnType;
QStringList parentPath;
@@ -1271,8 +1351,9 @@
if (tokenizer->parsingFnOrMacro()
&& (match(Tok_Q_DECLARE_FLAGS) || match(Tok_Q_PROPERTY)))
returnType = CodeChunk(previousLexeme());
- else
+ else {
return false;
+ }
}
if (returnType.toString() == "QBool")
@@ -1302,8 +1383,9 @@
readToken();
}
}
- if (tok != Tok_LeftParen)
+ if (tok != Tok_LeftParen) {
return false;
+ }
}
else if (tok == Tok_LeftParen) {
// constructor or destructor
@@ -1349,8 +1431,9 @@
returnType.append(lexeme());
readToken();
}
- if (tok != Tok_Semicolon)
+ if (tok != Tok_Semicolon) {
return false;
+ }
}
else if (tok == Tok_Colon) {
returnType.appendHotspot();
@@ -1359,8 +1442,9 @@
returnType.append(lexeme());
readToken();
}
- if (tok != Tok_Semicolon)
+ if (tok != Tok_Semicolon) {
return false;
+ }
}
VariableNode *var = new VariableNode(parent, name);
@@ -1373,12 +1457,13 @@
var->setStatic(sta);
return false;
}
- if (tok != Tok_LeftParen)
+ if (tok != Tok_LeftParen) {
return false;
+ }
}
readToken();
- FunctionNode *func = new FunctionNode(parent, name);
+ FunctionNode *func = new FunctionNode(type, parent, name, attached);
func->setAccess(access);
func->setLocation(location());
func->setReturnType(returnType.toString());
@@ -1399,12 +1484,14 @@
if (tok != Tok_RightParen) {
do {
- if (!matchParameter(func))
+ if (!matchParameter(func)) {
return false;
+ }
} while (match(Tok_Comma));
}
- if (!match(Tok_RightParen))
+ if (!match(Tok_RightParen)) {
return false;
+ }
func->setConst(match(Tok_const));
@@ -1420,8 +1507,9 @@
if (!match(Tok_Semicolon) && tok != Tok_Eoi) {
int braceDepth0 = tokenizer->braceDepth();
- if (!match(Tok_LeftBrace))
+ if (!match(Tok_LeftBrace)) {
return false;
+ }
while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi)
readToken();
match(Tok_RightBrace);
@@ -1739,15 +1827,15 @@
if (key == "READ")
tre->addPropertyFunction(property, value, PropertyNode::Getter);
- else if (key == "WRITE")
+ else if (key == "WRITE") {
tre->addPropertyFunction(property, value, PropertyNode::Setter);
- else if (key == "STORED")
+ property->setWritable(true);
+ } else if (key == "STORED")
property->setStored(value.toLower() == "true");
else if (key == "DESIGNABLE")
property->setDesignable(value.toLower() == "true");
else if (key == "RESET")
tre->addPropertyFunction(property, value, PropertyNode::Resetter);
-
else if (key == "NOTIFY") {
tre->addPropertyFunction(property, value, PropertyNode::Notifier);
}
@@ -2068,7 +2156,9 @@
bool CppCodeParser::makeFunctionNode(const QString& synopsis,
QStringList *parentPathPtr,
FunctionNode **funcPtr,
- InnerNode *root)
+ InnerNode *root,
+ Node::Type type,
+ bool attached)
{
Tokenizer *outerTokenizer = tokenizer;
int outerTok = tok;
@@ -2080,13 +2170,37 @@
tokenizer = &stringTokenizer;
readToken();
- bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr);
+ bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr, QString(), type, attached);
// potential memory leak with funcPtr
tokenizer = outerTokenizer;
tok = outerTok;
+ return ok;
+}
- return ok;
+/*!
+ Create a new FunctionNode for a QML method or signal, as
+ specified by \a type, as a child of \a parent. \a sig is
+ the complete signature, and if \a attached is true, the
+ method or signal is "attached". \a qdoctag is the text of
+ the \a type.
+ */
+FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,
+ const QString& sig,
+ InnerNode* parent,
+ Node::Type type,
+ bool attached,
+ QString qdoctag)
+{
+ QStringList pp;
+ FunctionNode* fn = 0;
+ if (!makeFunctionNode(sig,&pp,&fn,parent,type,attached) &&
+ !makeFunctionNode("void "+sig,&pp,&fn,parent,type,attached)) {
+ doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag));
+ }
+ if (fn)
+ return fn;
+ return 0;
}
void CppCodeParser::parseQiteratorDotH(const Location &location,
@@ -2145,6 +2259,7 @@
exampleDirs,
proFileName,
userFriendlyFilePath);
+
if (fullPath.isEmpty()) {
QString tmp = proFileName;
proFileName = examplePath + "/" + "qbuild.pro";
@@ -2164,8 +2279,18 @@
int sizeOfBoringPartOfName = fullPath.size() - proFileName.size();
fullPath.truncate(fullPath.lastIndexOf('/'));
- QStringList exampleFiles = Config::getFilesHere(fullPath,
- exampleNameFilter);
+ QStringList exampleFiles = Config::getFilesHere(fullPath,exampleNameFilter);
+ QString imagesPath = fullPath + "/images";
+ QStringList imageFiles = Config::getFilesHere(imagesPath,exampleImageFilter);
+
+#if 0
+ qDebug() << "examplePath:" << examplePath;
+ qDebug() << " exampleFiles" << exampleFiles;
+ qDebug() << "imagesPath:" << imagesPath;
+ qDebug() << "fullPath:" << fullPath;
+ qDebug() << " imageFiles" << imageFiles;
+#endif
+
if (!exampleFiles.isEmpty()) {
// move main.cpp and to the end, if it exists
QString mainCpp;
@@ -2192,6 +2317,11 @@
(void) new FakeNode(fake,
exampleFile.mid(sizeOfBoringPartOfName),
Node::File);
+ foreach (const QString &imageFile, imageFiles) {
+ new FakeNode(fake,
+ imageFile.mid(sizeOfBoringPartOfName),
+ Node::Image);
+ }
}
QT_END_NAMESPACE