Skip to content

Commit

Permalink
Merge pull request #3862 from ntrel/unique-anchors
Browse files Browse the repository at this point in the history
Fix Issue 9728 - Ddoc anchors non-unique across overloads
  • Loading branch information
WalterBright committed Oct 16, 2014
2 parents f150674 + b66c0a4 commit 15b6899
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 65 deletions.
71 changes: 43 additions & 28 deletions src/doc.c
Expand Up @@ -20,6 +20,7 @@
#include "rmem.h"
#include "root.h"
#include "port.h"
#include "aav.h"

#include "attrib.h"
#include "cond.h"
Expand Down Expand Up @@ -129,6 +130,14 @@ bool isCVariadicParameter(Dsymbol *s, const utf8_t *p, size_t len)
return tf && tf->varargs == 1 && cmp("...", p, len) == 0;
}

static TemplateDeclaration *getEponymousParentTemplate(Dsymbol *s)
{
if (!s->parent)
return NULL;
TemplateDeclaration *td = s->parent->isTemplateDeclaration();
return (td && td->onemember == s) ? td : NULL;
}

static const char ddoc_default[] = "\
DDOC = <html><head>\n\
<META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
Expand Down Expand Up @@ -522,39 +531,59 @@ static bool emitAnchorName(OutBuffer *buf, Dsymbol *s, Scope *sc)
if (!s || s->isPackage() || s->isModule())
return false;

TemplateDeclaration *td;
bool dot = false;

// Add parent names first
bool dot = false;
if (s->parent)
dot = emitAnchorName(buf, s->parent, sc);
else if (sc)
dot = emitAnchorName(buf, sc->scopesym, skipNonQualScopes(sc->enclosing));

// Eponymous template members can share the parent anchor name
if (s->parent && (td = s->parent->isTemplateDeclaration()) != NULL &&
td->onemember == s)
if (getEponymousParentTemplate(s))
return dot;
if (dot)
buf->writeByte('.');

// Use "this" not "__ctor"
TemplateDeclaration *td;
if (s->isCtorDeclaration() || ((td = s->isTemplateDeclaration()) != NULL &&
td->onemember && td->onemember->isCtorDeclaration()))
{
buf->writestring("this");
}
else
{
/* We just want the identifier, not overloads like TemplateDeclaration::toChars.
* We don't want the template parameter list and constraints. */
buf->writestring(s->Dsymbol::toChars());
}

return true;
}

static void emitAnchor(OutBuffer *buf, Dsymbol *s, Scope *sc)
{
Identifier *ident;
{
OutBuffer anc;
emitAnchorName(&anc, s, skipNonQualScopes(sc));
ident = Lexer::idPool(anc.peekString());
}
size_t *count = (size_t*)dmd_aaGet(&sc->anchorCounts, ident);
TemplateDeclaration *td = getEponymousParentTemplate(s);
// don't write an anchor for matching consecutive ditto symbols
if (*count > 0 && sc->prevAnchor == ident &&
sc->lastdc && (isDitto(s->comment) || (td && isDitto(td->comment))))
return;

(*count)++;
// cache anchor name
sc->prevAnchor = ident;

buf->writestring("$(DDOC_ANCHOR ");
emitAnchorName(buf, s, skipNonQualScopes(sc));
buf->writestring(ident->string);
// only append count once there's a duplicate
if (*count != 1)
buf->printf(".%u", *count);
buf->writeByte(')');
}

Expand Down Expand Up @@ -627,13 +656,7 @@ void emitDitto(Dsymbol *s, Scope *sc)
/* If 'this' is a function template, then highlightCode() was
* already run by FuncDeclaration::toDocbuffer().
*/
TemplateDeclaration *td;
if (s->parent &&
(td = s->parent->isTemplateDeclaration()) != NULL &&
td->onemember == s)
{
}
else
if (!getEponymousParentTemplate(s))
highlightCode(sc, s, &b, o);
b.writeByte(')');
buf->spread(sc->lastoffset, b.offset);
Expand Down Expand Up @@ -1184,18 +1207,14 @@ void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc)
//printf("FuncDeclaration::toDocbuffer() %s\n", fd->toChars());
if (fd->ident)
{
TemplateDeclaration *td;
TemplateDeclaration *td = getEponymousParentTemplate(fd);

if (fd->parent &&
(td = fd->parent->isTemplateDeclaration()) != NULL &&
td->onemember == fd)
if (td)
{
/* It's a function template
*/
size_t o = buf->offset;

declarationToDocBuffer(fd, td);

highlightCode(sc, fd, buf, o);
}
else
Expand Down Expand Up @@ -1225,11 +1244,9 @@ void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc)
#if 0
emitProtection(buf, sd->protection);
#endif
TemplateDeclaration *td;
TemplateDeclaration *td = getEponymousParentTemplate(sd);

if (sd->parent &&
(td = sd->parent->isTemplateDeclaration()) != NULL &&
td->onemember == sd)
if (td)
{
size_t o = buf->offset;
toDocBuffer(td, buf, sc);
Expand All @@ -1251,11 +1268,9 @@ void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc)
#if 0
emitProtection(buf, cd->protection);
#endif
TemplateDeclaration *td;
TemplateDeclaration *td = getEponymousParentTemplate(cd);

if (cd->parent &&
(td = cd->parent->isTemplateDeclaration()) != NULL &&
td->onemember == cd)
if (td)
{
size_t o = buf->offset;
toDocBuffer(td, buf, sc);
Expand Down
2 changes: 2 additions & 0 deletions src/scope.c
Expand Up @@ -87,6 +87,8 @@ Scope::Scope()
this->lastdc = NULL;
this->lastoffset = 0;
this->docbuf = NULL;
this->anchorCounts = NULL;
this->prevAnchor = NULL;
this->userAttribDecl = NULL;
}

Expand Down
3 changes: 3 additions & 0 deletions src/scope.h
Expand Up @@ -30,6 +30,7 @@ class AggregateDeclaration;
class FuncDeclaration;
class UserAttributeDeclaration;
struct DocComment;
struct AA;
class TemplateInstance;

#include "dsymbol.h"
Expand Down Expand Up @@ -119,6 +120,8 @@ struct Scope
size_t lastoffset; // offset in docbuf of where to insert next dec (for ditto)
size_t lastoffset2; // offset in docbuf of where to insert next dec (for unittest)
OutBuffer *docbuf; // buffer for documentation output
AA *anchorCounts; // lookup duplicate anchor name count
Identifier *prevAnchor; // qualified symbol name of last doc anchor

static Scope *freelist;
static Scope *alloc();
Expand Down
5 changes: 5 additions & 0 deletions test/compilable/ddoc10.d
Expand Up @@ -23,6 +23,8 @@ int func2(T,U)(T x, U y) {}
/// ditto
int func2(T)(T x) {}

/// Separate overload item.
int func2()() {}


///
Expand Down Expand Up @@ -169,4 +171,7 @@ struct T
/****
*/
this(A...)(A args) { }

///
this(int){}
}
19 changes: 14 additions & 5 deletions test/compilable/extra-files/ddoc10.html
Expand Up @@ -5,7 +5,7 @@
<h1>ddoc10</h1>
<br><br>
<dl><dt><big><a name="Foo"></a>struct <u>Foo</u>(T);
<br><a name="Foo"></a>struct <u>Foo</u>(T, U);
<br>struct <u>Foo</u>(T, U);
</big></dt>
<dd>The foo<br><br>

Expand All @@ -18,28 +18,33 @@ <h1>ddoc10</h1>

</dd>
<dt><big><a name="func2"></a>int <u>func2</u>(T, U)(T <i>x</i>, U <i>y</i>);
<br><a name="func2"></a>int <u>func2</u>(T)(T <i>x</i>);
<br>int <u>func2</u>(T)(T <i>x</i>);
</big></dt>
<dd>This comment is also repeated twice, and the second function signature is
not very well documented.<br><br>

</dd>
<dt><big><a name="func2.2"></a>int <u>func2</u>()();
</big></dt>
<dd>Separate overload item.<br><br>

</dd>
<dt><big><a name="func3"></a>int <u>func3</u>(T, U)(T <i>x</i>, U <i>y</i>);
<br><a name="func3"></a>int <u>func3</u>(T, U = int, V : long)(T <i>x</i>);
<br>int <u>func3</u>(T, U = int, V : long)(T <i>x</i>);
</big></dt>
<dd>This used to work adequately and documented both <u>func3</u> templates
simultaneously. Now, it documents the first template twice and
no longer documents the function argument and return types.<br><br>

</dd>
<dt><big><a name="map"></a>void <u>map</u>(char <i>rs</i>);
<br><a name="map"></a>void <u>map</u>(int <i>rs</i>);
<br>void <u>map</u>(int <i>rs</i>);
</big></dt>
<dd>blah<br><br>

</dd>
<dt><big><a name="map2"></a>void <u>map2</u>()(char <i>rs</i>);
<br><a name="map2"></a>void <u>map2</u>()(int <i>rs</i>);
<br>void <u>map2</u>()(int <i>rs</i>);
</big></dt>
<dd>blah<br><br>

Expand Down Expand Up @@ -125,6 +130,10 @@ <h1>ddoc10</h1>
</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="T.this.2"></a>this(int);
</big></dt>
<dd><br><br>
</dd>
</dl>
</dd>
</dl>
Expand Down
34 changes: 17 additions & 17 deletions test/compilable/extra-files/ddoc10334.html
Expand Up @@ -7,55 +7,55 @@ <h1>ddoc10334</h1>
<dl><dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!())</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!100)</big></dt>
<dt><big><a name="Foo10334.2"></a>template <u>Foo10334</u>(T) if (Bar10334!100)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!3.14)</big></dt>
<dt><big><a name="Foo10334.3"></a>template <u>Foo10334</u>(T) if (Bar10334!3.14)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!"str")</big></dt>
<dt><big><a name="Foo10334.4"></a>template <u>Foo10334</u>(T) if (Bar10334!"str")</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!1.4i)</big></dt>
<dt><big><a name="Foo10334.5"></a>template <u>Foo10334</u>(T) if (Bar10334!1.4i)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!null)</big></dt>
<dt><big><a name="Foo10334.6"></a>template <u>Foo10334</u>(T) if (Bar10334!null)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!true)</big></dt>
<dt><big><a name="Foo10334.7"></a>template <u>Foo10334</u>(T) if (Bar10334!true)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!false)</big></dt>
<dt><big><a name="Foo10334.8"></a>template <u>Foo10334</u>(T) if (Bar10334!false)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!'A')</big></dt>
<dt><big><a name="Foo10334.9"></a>template <u>Foo10334</u>(T) if (Bar10334!'A')</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!int)</big></dt>
<dt><big><a name="Foo10334.10"></a>template <u>Foo10334</u>(T) if (Bar10334!int)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!string)</big></dt>
<dt><big><a name="Foo10334.11"></a>template <u>Foo10334</u>(T) if (Bar10334!string)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!([1, 2, 3]))</big></dt>
<dt><big><a name="Foo10334.12"></a>template <u>Foo10334</u>(T) if (Bar10334!([1, 2, 3]))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!(Baz10334!()))</big></dt>
<dt><big><a name="Foo10334.13"></a>template <u>Foo10334</u>(T) if (Bar10334!(Baz10334!()))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!(Baz10334!T))</big></dt>
<dt><big><a name="Foo10334.14"></a>template <u>Foo10334</u>(T) if (Bar10334!(Baz10334!T))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!(Baz10334!100))</big></dt>
<dt><big><a name="Foo10334.15"></a>template <u>Foo10334</u>(T) if (Bar10334!(Baz10334!100))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!(.foo))</big></dt>
<dt><big><a name="Foo10334.16"></a>template <u>Foo10334</u>(T) if (Bar10334!(.foo))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!(const(int)))</big></dt>
<dt><big><a name="Foo10334.17"></a>template <u>Foo10334</u>(T) if (Bar10334!(const(int)))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Foo10334"></a>template <u>Foo10334</u>(T) if (Bar10334!(shared(T)))</big></dt>
<dt><big><a name="Foo10334.18"></a>template <u>Foo10334</u>(T) if (Bar10334!(shared(T)))</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="Test10334"></a>template <u>Test10334</u>(T...)</big></dt>
Expand Down
2 changes: 1 addition & 1 deletion test/compilable/extra-files/ddoc11511.html
Expand Up @@ -16,7 +16,7 @@ <h1>ddoc11511</h1>
</table><br>

</dd>
<dt><big><a name="foo"></a>void <u>foo</u>(int <i>abcd</i>, int <i>bcdef</i>, int[] <i>arr</i>...);
<dt><big><a name="foo.2"></a>void <u>foo</u>(int <i>abcd</i>, int <i>bcdef</i>, int[] <i>arr</i>...);
</big></dt>
<dd><b>Params:</b><br>
<table><tr><td>int <i>abcd</i></td>
Expand Down
20 changes: 10 additions & 10 deletions test/compilable/extra-files/ddoc9305.html
Expand Up @@ -9,51 +9,51 @@ <h1>ddoc9305</h1>
<dd><u>foo</u>()<br><br>

</dd>
<dt><big><a name="X"></a>template <u>X</u>(alias pred = (x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = (x)
<dt><big><a name="X"></a>template <u>X</u>(alias pred = (x) =&gt; x)<br>template <u>X</u>(alias pred = (x)
{
int y;
return y;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = (int x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = (int x)
)<br>template <u>X</u>(alias pred = (int x) =&gt; x)<br>template <u>X</u>(alias pred = (int x)
{
int y;
return y;
}
)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="X"></a>template <u>X</u>(alias pred = function (x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = function (x)
<dt><big><a name="X.2"></a>template <u>X</u>(alias pred = function (x) =&gt; x)<br>template <u>X</u>(alias pred = function (x)
{
return x + 1;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = function (int x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = function (int x)
)<br>template <u>X</u>(alias pred = function (int x) =&gt; x)<br>template <u>X</u>(alias pred = function (int x)
{
return x + 1;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = function int(x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = function int(x)
)<br>template <u>X</u>(alias pred = function int(x) =&gt; x)<br>template <u>X</u>(alias pred = function int(x)
{
return x + 1;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = function int(int x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = function int(int x)
)<br>template <u>X</u>(alias pred = function int(int x) =&gt; x)<br>template <u>X</u>(alias pred = function int(int x)
{
return x + 1;
}
)</big></dt>
<dd><br><br>
</dd>
<dt><big><a name="X"></a>template <u>X</u>(alias pred = delegate (x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = delegate (x)
<dt><big><a name="X.3"></a>template <u>X</u>(alias pred = delegate (x) =&gt; x)<br>template <u>X</u>(alias pred = delegate (x)
{
return x + 1;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = delegate (int x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = delegate (int x)
)<br>template <u>X</u>(alias pred = delegate (int x) =&gt; x)<br>template <u>X</u>(alias pred = delegate (int x)
{
return x + 1;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = delegate int(x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = delegate int(x)
)<br>template <u>X</u>(alias pred = delegate int(x) =&gt; x)<br>template <u>X</u>(alias pred = delegate int(x)
{
return x + 1;
}
)<br><a name="X"></a>template <u>X</u>(alias pred = delegate int(int x) =&gt; x)<br><a name="X"></a>template <u>X</u>(alias pred = delegate int(int x)
)<br>template <u>X</u>(alias pred = delegate int(int x) =&gt; x)<br>template <u>X</u>(alias pred = delegate int(int x)
{
return x + 1;
}
Expand Down

0 comments on commit 15b6899

Please sign in to comment.