qdoc: Sanitize anchors in URLs for functions

When QDoc constructs the full path to a documentation node, it
must construct a clean anchor reference. Intra-page links use
a different code path, so the links e.g. from Member Functions
list to their detailed descriptions always work, but using the
link command to link to functions with certain characters (such
as 'operator==') failed because the node name was used as-is and
not sanitized ('operator-eq-eq').

This change moves HtmlGenerator::cleanRef() static function to
its parent class, Generator, and takes it into use in
Generator::fullDocumentLocation().

Change-Id: Ic939ffa3ae0f4495ef2a30eff0d4a1de65ea3e8f
Task-number: QTBUG-45629
Reviewed-by: Martin Smith <martin.smith@digia.com>
This commit is contained in:
Topi Reinio 2015-04-17 14:57:21 +02:00 committed by Topi Reiniö
parent 4dd896785b
commit 21c90bcc98
4 changed files with 58 additions and 58 deletions

View File

@ -419,6 +419,59 @@ QString Generator::fileName(const Node* node) const
return name; return name;
} }
QString Generator::cleanRef(const QString& ref)
{
QString clean;
if (ref.isEmpty())
return clean;
clean.reserve(ref.size() + 20);
const QChar c = ref[0];
const uint u = c.unicode();
if ((u >= 'a' && u <= 'z') ||
(u >= 'A' && u <= 'Z') ||
(u >= '0' && u <= '9')) {
clean += c;
} else if (u == '~') {
clean += "dtor.";
} else if (u == '_') {
clean += "underscore.";
} else {
clean += QLatin1Char('A');
}
for (int i = 1; i < (int) ref.length(); i++) {
const QChar c = ref[i];
const uint u = c.unicode();
if ((u >= 'a' && u <= 'z') ||
(u >= 'A' && u <= 'Z') ||
(u >= '0' && u <= '9') || u == '-' ||
u == '_' || u == ':' || u == '.') {
clean += c;
} else if (c.isSpace()) {
clean += QLatin1Char('-');
} else if (u == '!') {
clean += "-not";
} else if (u == '&') {
clean += "-and";
} else if (u == '<') {
clean += "-lt";
} else if (u == '=') {
clean += "-eq";
} else if (u == '>') {
clean += "-gt";
} else if (u == '#') {
clean += QLatin1Char('#');
} else {
clean += QLatin1Char('-');
clean += QString::number((int)u, 16);
}
}
return clean;
}
QMap<QString, QString>& Generator::formattingLeftMap() QMap<QString, QString>& Generator::formattingLeftMap()
{ {
return fmtLeftMaps[format()]; return fmtLeftMaps[format()];
@ -521,10 +574,10 @@ QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
return fullDocumentLocation(functionNode->associatedProperty()); return fullDocumentLocation(functionNode->associatedProperty());
else if (functionNode->overloadNumber() > 1) else if (functionNode->overloadNumber() > 1)
anchorRef = QLatin1Char('#') + functionNode->name() anchorRef = QLatin1Char('#') + cleanRef(functionNode->name())
+ QLatin1Char('-') + QString::number(functionNode->overloadNumber()); + QLatin1Char('-') + QString::number(functionNode->overloadNumber());
else else
anchorRef = QLatin1Char('#') + functionNode->name(); anchorRef = QLatin1Char('#') + cleanRef(functionNode->name());
break; break;
} }
/* /*

View File

@ -103,6 +103,7 @@ public:
static bool useOutputSubdirs() { return useOutputSubdirs_; } static bool useOutputSubdirs() { return useOutputSubdirs_; }
static void setQmlTypeContext(QmlTypeNode* t) { qmlTypeContext_ = t; } static void setQmlTypeContext(QmlTypeNode* t) { qmlTypeContext_ = t; }
static QmlTypeNode* qmlTypeContext() { return qmlTypeContext_; } static QmlTypeNode* qmlTypeContext() { return qmlTypeContext_; }
static QString cleanRef(const QString& ref);
protected: protected:
virtual void beginSubPage(const InnerNode* node, const QString& fileName); virtual void beginSubPage(const InnerNode* node, const QString& fileName);

View File

@ -3355,7 +3355,7 @@ void HtmlGenerator::generateSectionInheritedList(const Section& section, const N
out() << section.pluralMember; out() << section.pluralMember;
} }
out() << " inherited from <a href=\"" << fileName((*p).first) out() << " inherited from <a href=\"" << fileName((*p).first)
<< '#' << HtmlGenerator::cleanRef(section.name.toLower()) << "\">" << '#' << Generator::cleanRef(section.name.toLower()) << "\">"
<< protectEnc((*p).first->plainFullName(relative)) << protectEnc((*p).first->plainFullName(relative))
<< "</a></li>\n"; << "</a></li>\n";
++p; ++p;
@ -3610,62 +3610,9 @@ void HtmlGenerator::generateLink(const Atom* atom, CodeMarker* marker)
} }
} }
QString HtmlGenerator::cleanRef(const QString& ref)
{
QString clean;
if (ref.isEmpty())
return clean;
clean.reserve(ref.size() + 20);
const QChar c = ref[0];
const uint u = c.unicode();
if ((u >= 'a' && u <= 'z') ||
(u >= 'A' && u <= 'Z') ||
(u >= '0' && u <= '9')) {
clean += c;
} else if (u == '~') {
clean += "dtor.";
} else if (u == '_') {
clean += "underscore.";
} else {
clean += QLatin1Char('A');
}
for (int i = 1; i < (int) ref.length(); i++) {
const QChar c = ref[i];
const uint u = c.unicode();
if ((u >= 'a' && u <= 'z') ||
(u >= 'A' && u <= 'Z') ||
(u >= '0' && u <= '9') || u == '-' ||
u == '_' || u == ':' || u == '.') {
clean += c;
} else if (c.isSpace()) {
clean += QLatin1Char('-');
} else if (u == '!') {
clean += "-not";
} else if (u == '&') {
clean += "-and";
} else if (u == '<') {
clean += "-lt";
} else if (u == '=') {
clean += "-eq";
} else if (u == '>') {
clean += "-gt";
} else if (u == '#') {
clean += QLatin1Char('#');
} else {
clean += QLatin1Char('-');
clean += QString::number((int)u, 16);
}
}
return clean;
}
QString HtmlGenerator::registerRef(const QString& ref) QString HtmlGenerator::registerRef(const QString& ref)
{ {
QString clean = HtmlGenerator::cleanRef(ref); QString clean = Generator::cleanRef(ref);
for (;;) { for (;;) {
QString& prevRef = refMap[clean.toLower()]; QString& prevRef = refMap[clean.toLower()];

View File

@ -85,7 +85,6 @@ public:
QString protectEnc(const QString &string); QString protectEnc(const QString &string);
static QString protect(const QString &string, const QString &encoding = "ISO-8859-1"); static QString protect(const QString &string, const QString &encoding = "ISO-8859-1");
static QString cleanRef(const QString& ref);
static QString sinceTitle(int i) { return sinceTitles[i]; } static QString sinceTitle(int i) { return sinceTitles[i]; }
protected: protected: