diff options
Diffstat (limited to 'gcc/d/dmd/doc.c')
-rw-r--r-- | gcc/d/dmd/doc.c | 2807 |
1 files changed, 0 insertions, 2807 deletions
diff --git a/gcc/d/dmd/doc.c b/gcc/d/dmd/doc.c deleted file mode 100644 index 5d2da1c..0000000 --- a/gcc/d/dmd/doc.c +++ /dev/null @@ -1,2807 +0,0 @@ - -/* Compiler implementation of the D programming language - * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved - * written by Walter Bright - * http://www.digitalmars.com - * Distributed under the Boost Software License, Version 1.0. - * http://www.boost.org/LICENSE_1_0.txt - * https://github.com/D-Programming-Language/dmd/blob/master/src/doc.c - */ - -// This implements the Ddoc capability. - -#include "root/dsystem.h" -#include "root/rmem.h" -#include "root/root.h" -#include "root/port.h" -#include "root/aav.h" - -#include "attrib.h" -#include "cond.h" -#include "mars.h" -#include "dsymbol.h" -#include "macro.h" -#include "template.h" -#include "lexer.h" -#include "aggregate.h" -#include "declaration.h" -#include "statement.h" -#include "enum.h" -#include "id.h" -#include "module.h" -#include "scope.h" -#include "hdrgen.h" -#include "doc.h" -#include "mtype.h" -#include "utf.h" - -void emitMemberComments(ScopeDsymbol *sds, OutBuffer *buf, Scope *sc); -void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc); -void emitComment(Dsymbol *s, OutBuffer *buf, Scope *sc); - -struct Escape -{ - const char *strings[256]; - - const char *escapeChar(unsigned c); -}; - -class Section -{ -public: - const utf8_t *name; - size_t namelen; - - const utf8_t *body; - size_t bodylen; - - int nooutput; - - virtual void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf); -}; - -class ParamSection : public Section -{ -public: - void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf); -}; - -class MacroSection : public Section -{ -public: - void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf); -}; - -typedef Array<Section *> Sections; - -struct DocComment -{ - Sections sections; // Section*[] - - Section *summary; - Section *copyright; - Section *macros; - Macro **pmacrotable; - Escape **pescapetable; - - Dsymbols a; - - DocComment() : - summary(NULL), copyright(NULL), macros(NULL), pmacrotable(NULL), pescapetable(NULL) - { } - - static DocComment *parse(Dsymbol *s, const utf8_t *comment); - static void parseMacros(Escape **pescapetable, Macro **pmacrotable, const utf8_t *m, size_t mlen); - static void parseEscapes(Escape **pescapetable, const utf8_t *textstart, size_t textlen); - - void parseSections(const utf8_t *comment); - void writeSections(Scope *sc, Dsymbols *a, OutBuffer *buf); -}; - - -int cmp(const char *stringz, const void *s, size_t slen); -int icmp(const char *stringz, const void *s, size_t slen); -bool isDitto(const utf8_t *comment); -const utf8_t *skipwhitespace(const utf8_t *p); -size_t skiptoident(OutBuffer *buf, size_t i); -size_t skippastident(OutBuffer *buf, size_t i); -size_t skippastURL(OutBuffer *buf, size_t i); -void highlightText(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset); -void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset); -void highlightCode(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset); -void highlightCode2(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset); -void highlightCode3(Scope *sc, OutBuffer *buf, const utf8_t *p, const utf8_t *pend); -TypeFunction *isTypeFunction(Dsymbol *s); -Parameter *isFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len); -TemplateParameter *isTemplateParameter(Dsymbols *a, const utf8_t *p, size_t len); - -bool isIdStart(const utf8_t *p); -bool isCVariadicArg(const utf8_t *p, size_t len); -bool isIdTail(const utf8_t *p); -bool isIndentWS(const utf8_t *p); -int utfStride(const utf8_t *p); - -// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one). -bool isCVariadicParameter(Dsymbols *a, const utf8_t *p, size_t len) -{ - for (size_t i = 0; i < a->length; i++) - { - TypeFunction *tf = isTypeFunction((*a)[i]); - if (tf && tf->parameterList.varargs == VARARGvariadic && cmp("...", p, len) == 0) - return true; - } - return false; -} - -/**************************************************** - */ -static Parameter *isFunctionParameter(Dsymbol *s, const utf8_t *p, size_t len) -{ - TypeFunction *tf = isTypeFunction(s); - if (tf && tf->parameterList.parameters) - { - for (size_t k = 0; k < tf->parameterList.parameters->length; k++) - { - Parameter *fparam = (*tf->parameterList.parameters)[k]; - if (fparam->ident && cmp(fparam->ident->toChars(), p, len) == 0) - { - return fparam; - } - } - } - return NULL; -} - -static Dsymbol *getEponymousMember(TemplateDeclaration *td) -{ - if (!td->onemember) - return NULL; - - if (AggregateDeclaration *ad = td->onemember->isAggregateDeclaration()) - return ad; - if (FuncDeclaration *fd = td->onemember->isFuncDeclaration()) - return fd; - if (td->onemember->isEnumMember()) - return NULL; // Keep backward compatibility. See compilable/ddoc9.d - if (VarDeclaration *vd = td->onemember->isVarDeclaration()) - return td->constraint ? NULL : vd; - - return NULL; -} - -/**************************************************** - */ -static Parameter *isEponymousFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len) -{ - for (size_t i = 0; i < a->length; i++) - { - TemplateDeclaration *td = (*a)[i]->isTemplateDeclaration(); - if (td && td->onemember) - { - /* Case 1: we refer to a template declaration inside the template - - /// ...ddoc... - template case1(T) { - void case1(R)() {} - } - */ - td = td->onemember->isTemplateDeclaration(); - } - if (!td) - { - /* Case 2: we're an alias to a template declaration - - /// ...ddoc... - alias case2 = case1!int; - */ - AliasDeclaration *ad = (*a)[i]->isAliasDeclaration(); - if (ad && ad->aliassym) - { - td = ad->aliassym->isTemplateDeclaration(); - } - } - while (td) - { - Dsymbol *sym = getEponymousMember(td); - if (sym) - { - Parameter *fparam = isFunctionParameter(sym, p, len); - if (fparam) - { - return fparam; - } - } - td = td->overnext; - } - } - return NULL; -} - -static TemplateDeclaration *getEponymousParent(Dsymbol *s) -{ - if (!s->parent) - return NULL; - TemplateDeclaration *td = s->parent->isTemplateDeclaration(); - return (td && getEponymousMember(td)) ? td : NULL; -} - -static const char ddoc_default[] = "\ -DDOC = <html><head>\n\ - <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\ - <title>$(TITLE)</title>\n\ - </head><body>\n\ - <h1>$(TITLE)</h1>\n\ - $(BODY)\n\ - <hr>$(SMALL Page generated by $(LINK2 http://dlang.org/ddoc.html, Ddoc). $(COPYRIGHT))\n\ - </body></html>\n\ -\n\ -B = <b>$0</b>\n\ -I = <i>$0</i>\n\ -U = <u>$0</u>\n\ -P = <p>$0</p>\n\ -DL = <dl>$0</dl>\n\ -DT = <dt>$0</dt>\n\ -DD = <dd>$0</dd>\n\ -TABLE = <table>$0</table>\n\ -TR = <tr>$0</tr>\n\ -TH = <th>$0</th>\n\ -TD = <td>$0</td>\n\ -OL = <ol>$0</ol>\n\ -UL = <ul>$0</ul>\n\ -LI = <li>$0</li>\n\ -BIG = <big>$0</big>\n\ -SMALL = <small>$0</small>\n\ -BR = <br>\n\ -LINK = <a href=\"$0\">$0</a>\n\ -LINK2 = <a href=\"$1\">$+</a>\n\ -LPAREN= (\n\ -RPAREN= )\n\ -BACKTICK= `\n\ -DOLLAR= $\n\ -DEPRECATED= $0\n\ -\n\ -RED = <font color=red>$0</font>\n\ -BLUE = <font color=blue>$0</font>\n\ -GREEN = <font color=green>$0</font>\n\ -YELLOW =<font color=yellow>$0</font>\n\ -BLACK = <font color=black>$0</font>\n\ -WHITE = <font color=white>$0</font>\n\ -\n\ -D_CODE = <pre class=\"d_code\">$0</pre>\n\ -DDOC_BACKQUOTED = $(D_INLINECODE $0)\n\ -D_INLINECODE = <pre style=\"display:inline;\" class=\"d_inline_code\">$0</pre>\n\ -D_COMMENT = $(GREEN $0)\n\ -D_STRING = $(RED $0)\n\ -D_KEYWORD = $(BLUE $0)\n\ -D_PSYMBOL = $(U $0)\n\ -D_PARAM = $(I $0)\n\ -\n\ -DDOC_COMMENT = <!-- $0 -->\n\ -DDOC_DECL = $(DT $(BIG $0))\n\ -DDOC_DECL_DD = $(DD $0)\n\ -DDOC_DITTO = $(BR)$0\n\ -DDOC_SECTIONS = $0\n\ -DDOC_SUMMARY = $0$(BR)$(BR)\n\ -DDOC_DESCRIPTION = $0$(BR)$(BR)\n\ -DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\ -DDOC_SECTION_H = $(B $0)$(BR)\n\ -DDOC_SECTION = $0$(BR)$(BR)\n\ -DDOC_MEMBERS = $(DL $0)\n\ -DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\ -DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\ -DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\ -DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\ -DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\ -DDOC_ENUM_BASETYPE = $0\n\ -DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\ -DDOC_PARAM_ROW = $(TR $0)\n\ -DDOC_PARAM_ID = $(TD $0)\n\ -DDOC_PARAM_DESC = $(TD $0)\n\ -DDOC_BLANKLINE = $(BR)$(BR)\n\ -\n\ -DDOC_ANCHOR = <a name=\"$1\"></a>\n\ -DDOC_PSYMBOL = $(U $0)\n\ -DDOC_PSUPER_SYMBOL = $(U $0)\n\ -DDOC_KEYWORD = $(B $0)\n\ -DDOC_PARAM = $(I $0)\n\ -\n\ -ESCAPES = /</</\n\ - />/>/\n\ - /&/&/\n\ -"; - -static const char ddoc_decl_s[] = "$(DDOC_DECL "; -static const char ddoc_decl_e[] = ")\n"; - -static const char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD "; -static const char ddoc_decl_dd_e[] = ")\n"; - - -/**************************************************** - */ - -void gendocfile(Module *m) -{ - static OutBuffer mbuf; - static int mbuf_done; - - OutBuffer buf; - - //printf("Module::gendocfile()\n"); - - if (!mbuf_done) // if not already read the ddoc files - { - mbuf_done = 1; - - // Use our internal default - mbuf.write(ddoc_default, strlen(ddoc_default)); - - // Override with DDOCFILE specified in the sc.ini file - char *p = getenv("DDOCFILE"); - if (p) - global.params.ddocfiles.shift(p); - - // Override with the ddoc macro files from the command line - for (size_t i = 0; i < global.params.ddocfiles.length; i++) - { - FileName f(global.params.ddocfiles[i]); - File file(&f); - readFile(m->loc, &file); - // BUG: convert file contents to UTF-8 before use - - //printf("file: '%.*s'\n", file.len, file.buffer); - mbuf.write(file.buffer, file.len); - } - } - DocComment::parseMacros(&m->escapetable, &m->macrotable, (utf8_t *)mbuf.slice().ptr, mbuf.length()); - - Scope *sc = Scope::createGlobal(m); // create root scope - - DocComment *dc = DocComment::parse(m, m->comment); - dc->pmacrotable = &m->macrotable; - dc->pescapetable = &m->escapetable; - sc->lastdc = dc; - - // Generate predefined macros - - // Set the title to be the name of the module - { - const char *p = m->toPrettyChars(); - Macro::define(&m->macrotable, (const utf8_t *)"TITLE", 5, (const utf8_t *)p, strlen(p)); - } - - // Set time macros - { - time_t t; - time(&t); - char *p = ctime(&t); - p = mem.xstrdup(p); - Macro::define(&m->macrotable, (const utf8_t *)"DATETIME", 8, (const utf8_t *)p, strlen(p)); - Macro::define(&m->macrotable, (const utf8_t *)"YEAR", 4, (const utf8_t *)p + 20, 4); - } - - const char *srcfilename = m->srcfile->toChars(); - Macro::define(&m->macrotable, (const utf8_t *)"SRCFILENAME", 11, (const utf8_t *)srcfilename, strlen(srcfilename)); - - const char *docfilename = m->docfile->toChars(); - Macro::define(&m->macrotable, (const utf8_t *)"DOCFILENAME", 11, (const utf8_t *)docfilename, strlen(docfilename)); - - if (dc->copyright) - { - dc->copyright->nooutput = 1; - Macro::define(&m->macrotable, (const utf8_t *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); - } - - buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", m->srcfile->toChars()); - if (m->isDocFile) - { - Loc loc = m->md ? m->md->loc : m->loc; - size_t commentlen = strlen((const char *)m->comment); - Dsymbols a; - // Bugzilla 9764: Don't push m in a, to prevent emphasize ddoc file name. - if (dc->macros) - { - commentlen = dc->macros->name - m->comment; - dc->macros->write(loc, dc, sc, &a, &buf); - } - buf.write(m->comment, commentlen); - highlightText(sc, &a, &buf, 0); - } - else - { - Dsymbols a; - a.push(m); - dc->writeSections(sc, &a, &buf); - emitMemberComments(m, &buf, sc); - } - - //printf("BODY= '%.*s'\n", buf.length(), buf.slice().ptr); - Macro::define(&m->macrotable, (const utf8_t *)"BODY", 4, (const utf8_t *)buf.slice().ptr, buf.length()); - - OutBuffer buf2; - buf2.writestring("$(DDOC)\n"); - size_t end = buf2.length(); - m->macrotable->expand(&buf2, 0, &end, NULL, 0); - - /* Remove all the escape sequences from buf2, - * and make CR-LF the newline. - */ - { - buf.setsize(0); - buf.reserve(buf2.length()); - utf8_t *p = (utf8_t *)buf2.slice().ptr; - for (size_t j = 0; j < buf2.length(); j++) - { - utf8_t c = p[j]; - if (c == 0xFF && j + 1 < buf2.length()) - { - j++; - continue; - } - if (c == '\n') - buf.writeByte('\r'); - else if (c == '\r') - { - buf.writestring("\r\n"); - if (j + 1 < buf2.length() && p[j + 1] == '\n') - { - j++; - } - continue; - } - buf.writeByte(c); - } - } - - // Transfer image to file - assert(m->docfile); - m->docfile->setbuffer(buf.slice().ptr, buf.length()); - m->docfile->ref = 1; - ensurePathToNameExists(Loc(), m->docfile->toChars()); - writeFile(m->loc, m->docfile); -} - -/**************************************************** - * Having unmatched parentheses can hose the output of Ddoc, - * as the macros depend on properly nested parentheses. - * This function replaces all ( with $(LPAREN) and ) with $(RPAREN) - * to preserve text literally. This also means macros in the - * text won't be expanded. - */ -void escapeDdocString(OutBuffer *buf, size_t start) -{ - for (size_t u = start; u < buf->length(); u++) - { - utf8_t c = buf->slice().ptr[u]; - switch(c) - { - case '$': - buf->remove(u, 1); - buf->insert(u, (const char *)"$(DOLLAR)", 9); - u += 8; - break; - - case '(': - buf->remove(u, 1); //remove the ( - buf->insert(u, (const char *)"$(LPAREN)", 9); //insert this instead - u += 8; //skip over newly inserted macro - break; - - case ')': - buf->remove(u, 1); //remove the ) - buf->insert(u, (const char *)"$(RPAREN)", 9); //insert this instead - u += 8; //skip over newly inserted macro - break; - } - } -} - -/**************************************************** - * Having unmatched parentheses can hose the output of Ddoc, - * as the macros depend on properly nested parentheses. - - * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN). - */ -void escapeStrayParenthesis(Loc loc, OutBuffer *buf, size_t start) -{ - unsigned par_open = 0; - - for (size_t u = start; u < buf->length(); u++) - { - utf8_t c = buf->slice().ptr[u]; - switch(c) - { - case '(': - par_open++; - break; - - case ')': - if (par_open == 0) - { - //stray ')' - warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output." - " Use $(RPAREN) instead for unpaired right parentheses."); - buf->remove(u, 1); //remove the ) - buf->insert(u, (const char *)"$(RPAREN)", 9); //insert this instead - u += 8; //skip over newly inserted macro - } - else - par_open--; - break; - } - } - - if (par_open) // if any unmatched lparens - { - par_open = 0; - for (size_t u = buf->length(); u > start;) - { - u--; - utf8_t c = buf->slice().ptr[u]; - switch(c) - { - case ')': - par_open++; - break; - - case '(': - if (par_open == 0) - { - //stray '(' - warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output." - " Use $(LPAREN) instead for unpaired left parentheses."); - buf->remove(u, 1); //remove the ( - buf->insert(u, (const char *)"$(LPAREN)", 9); //insert this instead - } - else - par_open--; - break; - } - } - } -} - -// Basically, this is to skip over things like private{} blocks in a struct or -// class definition that don't add any components to the qualified name. -static Scope *skipNonQualScopes(Scope *sc) -{ - while (sc && !sc->scopesym) - sc = sc->enclosing; - return sc; -} - -static bool emitAnchorName(OutBuffer *buf, Dsymbol *s, Scope *sc) -{ - if (!s || s->isPackage() || s->isModule()) - return 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 (getEponymousParent(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 = Identifier::idPool(anc.peekChars()); - } - size_t *count = (size_t*)dmd_aaGet(&sc->anchorCounts, (void *)ident); - TemplateDeclaration *td = getEponymousParent(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 "); - buf->writestring(ident->toChars()); - // only append count once there's a duplicate - if (*count != 1) - buf->printf(".%u", *count); - buf->writeByte(')'); -} - -/******************************* emitComment **********************************/ - -/** Get leading indentation from 'src' which represents lines of code. */ -static size_t getCodeIndent(const char *src) -{ - while (src && (*src == '\r' || *src == '\n')) - ++src; // skip until we find the first non-empty line - - size_t codeIndent = 0; - while (src && (*src == ' ' || *src == '\t')) - { - codeIndent++; - src++; - } - return codeIndent; -} - -/** Recursively expand template mixin member docs into the scope. */ -static void expandTemplateMixinComments(TemplateMixin *tm, OutBuffer *buf, Scope *sc) -{ - if (!tm->semanticRun) - dsymbolSemantic(tm, sc); - TemplateDeclaration *td = (tm && tm->tempdecl) ? - tm->tempdecl->isTemplateDeclaration() : NULL; - if (td && td->members) - { - for (size_t i = 0; i < td->members->length; i++) - { - Dsymbol *sm = (*td->members)[i]; - TemplateMixin *tmc = sm->isTemplateMixin(); - if (tmc && tmc->comment) - expandTemplateMixinComments(tmc, buf, sc); - else - emitComment(sm, buf, sc); - } - } -} - -void emitMemberComments(ScopeDsymbol *sds, OutBuffer *buf, Scope *sc) -{ - if (!sds->members) - return; - - //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars()); - - const char *m = "$(DDOC_MEMBERS "; - if (sds->isTemplateDeclaration()) - m = "$(DDOC_TEMPLATE_MEMBERS "; - else if (sds->isClassDeclaration()) - m = "$(DDOC_CLASS_MEMBERS "; - else if (sds->isStructDeclaration()) - m = "$(DDOC_STRUCT_MEMBERS "; - else if (sds->isEnumDeclaration()) - m = "$(DDOC_ENUM_MEMBERS "; - else if (sds->isModule()) - m = "$(DDOC_MODULE_MEMBERS "; - - size_t offset1 = buf->length(); // save starting offset - buf->writestring(m); - size_t offset2 = buf->length(); // to see if we write anything - - sc = sc->push(sds); - - for (size_t i = 0; i < sds->members->length; i++) - { - Dsymbol *s = (*sds->members)[i]; - //printf("\ts = '%s'\n", s->toChars()); - - // only expand if parent is a non-template (semantic won't work) - if (s->comment && s->isTemplateMixin() && s->parent && !s->parent->isTemplateDeclaration()) - expandTemplateMixinComments((TemplateMixin *)s, buf, sc); - - emitComment(s, buf, sc); - } - emitComment(NULL, buf, sc); - - sc->pop(); - - if (buf->length() == offset2) - { - /* Didn't write out any members, so back out last write - */ - buf->setsize(offset1); - } - else - buf->writestring(")\n"); -} - -void emitProtection(OutBuffer *buf, Prot prot) -{ - if (prot.kind != Prot::undefined && prot.kind != Prot::public_) - { - protectionToBuffer(buf, prot); - buf->writeByte(' '); - } -} - -void emitComment(Dsymbol *s, OutBuffer *buf, Scope *sc) -{ - class EmitComment : public Visitor - { - public: - OutBuffer *buf; - Scope *sc; - - EmitComment(OutBuffer *buf, Scope *sc) - : buf(buf), sc(sc) - { - } - - void visit(Dsymbol *) {} - void visit(InvariantDeclaration *) {} - void visit(UnitTestDeclaration *) {} - void visit(PostBlitDeclaration *) {} - void visit(DtorDeclaration *) {} - void visit(StaticCtorDeclaration *) {} - void visit(StaticDtorDeclaration *) {} - void visit(TypeInfoDeclaration *) {} - - void emit(Scope *sc, Dsymbol *s, const utf8_t *com) - { - if (s && sc->lastdc && isDitto(com)) - { - sc->lastdc->a.push(s); - return; - } - - // Put previous doc comment if exists - if (DocComment *dc = sc->lastdc) - { - // Put the declaration signatures as the document 'title' - buf->writestring(ddoc_decl_s); - for (size_t i = 0; i < dc->a.length; i++) - { - Dsymbol *sx = dc->a[i]; - - if (i == 0) - { - size_t o = buf->length(); - toDocBuffer(sx, buf, sc); - highlightCode(sc, sx, buf, o); - continue; - } - - buf->writestring("$(DDOC_DITTO "); - { - size_t o = buf->length(); - toDocBuffer(sx, buf, sc); - highlightCode(sc, sx, buf, o); - } - buf->writeByte(')'); - } - buf->writestring(ddoc_decl_e); - - // Put the ddoc comment as the document 'description' - buf->writestring(ddoc_decl_dd_s); - { - dc->writeSections(sc, &dc->a, buf); - if (ScopeDsymbol *sds = dc->a[0]->isScopeDsymbol()) - emitMemberComments(sds, buf, sc); - } - buf->writestring(ddoc_decl_dd_e); - //printf("buf.2 = [[%.*s]]\n", buf->length() - o0, buf->slice().ptr + o0); - } - - if (s) - { - DocComment *dc = DocComment::parse(s, com); - dc->pmacrotable = &sc->_module->macrotable; - sc->lastdc = dc; - } - } - - void visit(Declaration *d) - { - //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d->toChars(), d->comment); - //printf("type = %p\n", d->type); - const utf8_t *com = d->comment; - if (TemplateDeclaration *td = getEponymousParent(d)) - { - if (isDitto(td->comment)) - com = td->comment; - else - com = Lexer::combineComments(td->comment, com); - } - else - { - if (!d->ident) - return; - if (!d->type && !d->isCtorDeclaration() && !d->isAliasDeclaration()) - return; - if (d->protection.kind == Prot::private_ || sc->protection.kind == Prot::private_) - return; - } - if (!com) - return; - - emit(sc, d, com); - } - - void visit(AggregateDeclaration *ad) - { - //printf("AggregateDeclaration::emitComment() '%s'\n", ad->toChars()); - const utf8_t *com = ad->comment; - if (TemplateDeclaration *td = getEponymousParent(ad)) - { - if (isDitto(td->comment)) - com = td->comment; - else - com = Lexer::combineComments(td->comment, com); - } - else - { - if (ad->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_) - return; - if (!ad->comment) - return; - } - if (!com) - return; - - emit(sc, ad, com); - } - - void visit(TemplateDeclaration *td) - { - //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td->toChars(), td->kind()); - if (td->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_) - return; - if (!td->comment) - return; - - if (Dsymbol *ss = getEponymousMember(td)) - { - ss->accept(this); - return; - } - emit(sc, td, td->comment); - } - - void visit(EnumDeclaration *ed) - { - if (ed->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_) - return; - if (ed->isAnonymous() && ed->members) - { - for (size_t i = 0; i < ed->members->length; i++) - { - Dsymbol *s = (*ed->members)[i]; - emitComment(s, buf, sc); - } - return; - } - if (!ed->comment) - return; - if (ed->isAnonymous()) - return; - - emit(sc, ed, ed->comment); - } - - void visit(EnumMember *em) - { - //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em->toChars(), em->comment); - if (em->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_) - return; - if (!em->comment) - return; - - emit(sc, em, em->comment); - } - - void visit(AttribDeclaration *ad) - { - //printf("AttribDeclaration::emitComment(sc = %p)\n", sc); - - /* A general problem with this, illustrated by BUGZILLA 2516, - * is that attributes are not transmitted through to the underlying - * member declarations for template bodies, because semantic analysis - * is not done for template declaration bodies - * (only template instantiations). - * Hence, Ddoc omits attributes from template members. - */ - - Dsymbols *d = ad->include(NULL); - - if (d) - { - for (size_t i = 0; i < d->length; i++) - { - Dsymbol *s = (*d)[i]; - //printf("AttribDeclaration::emitComment %s\n", s->toChars()); - emitComment(s, buf, sc); - } - } - } - - void visit(ProtDeclaration *pd) - { - if (pd->decl) - { - Scope *scx = sc; - sc = sc->copy(); - sc->protection = pd->protection; - visit((AttribDeclaration *)pd); - scx->lastdc = sc->lastdc; - sc = sc->pop(); - } - } - - void visit(ConditionalDeclaration *cd) - { - //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc); - if (cd->condition->inc) - { - visit((AttribDeclaration *)cd); - return; - } - - /* If generating doc comment, be careful because if we're inside - * a template, then include(NULL) will fail. - */ - Dsymbols *d = cd->decl ? cd->decl : cd->elsedecl; - for (size_t i = 0; i < d->length; i++) - { - Dsymbol *s = (*d)[i]; - emitComment(s, buf, sc); - } - } - }; - - EmitComment v(buf, sc); - - if (!s) - v.emit(sc, NULL, NULL); - else - s->accept(&v); -} - -/******************************* toDocBuffer **********************************/ - -void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc) -{ - class ToDocBuffer : public Visitor - { - public: - OutBuffer *buf; - Scope *sc; - - ToDocBuffer(OutBuffer *buf, Scope *sc) - : buf(buf), sc(sc) - { - } - - void visit(Dsymbol *s) - { - //printf("Dsymbol::toDocbuffer() %s\n", s->toChars()); - HdrGenState hgs; - hgs.ddoc = true; - ::toCBuffer(s, buf, &hgs); - } - - void prefix(Dsymbol *s) - { - if (s->isDeprecated()) - buf->writestring("deprecated "); - - if (Declaration *d = s->isDeclaration()) - { - emitProtection(buf, d->protection); - - if (d->isStatic()) - buf->writestring("static "); - else if (d->isFinal()) - buf->writestring("final "); - else if (d->isAbstract()) - buf->writestring("abstract "); - - if (!d->isFuncDeclaration()) // functionToBufferFull handles this - { - if (d->isConst()) - buf->writestring("const "); - if (d->isImmutable()) - buf->writestring("immutable "); - if (d->isSynchronized()) - buf->writestring("synchronized "); - - if (d->storage_class & STCmanifest) - buf->writestring("enum "); - } - } - } - - void visit(Declaration *d) - { - if (!d->ident) - return; - - TemplateDeclaration *td = getEponymousParent(d); - //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d->toChars(), d->originalType ? d->originalType->toChars() : "--", td ? td->toChars() : "--"); - - HdrGenState hgs; - hgs.ddoc = true; - - if (d->isDeprecated()) - buf->writestring("$(DEPRECATED "); - - prefix(d); - - if (d->type) - { - Type *origType = d->originalType ? d->originalType : d->type; - if (origType->ty == Tfunction) - { - functionToBufferFull((TypeFunction *)origType, buf, d->ident, &hgs, td); - } - else - ::toCBuffer(origType, buf, d->ident, &hgs); - } - else - buf->writestring(d->ident->toChars()); - - if (d->isVarDeclaration() && td) - { - buf->writeByte('('); - if (td->origParameters && td->origParameters->length) - { - for (size_t i = 0; i < td->origParameters->length; i++) - { - if (i) - buf->writestring(", "); - toCBuffer((*td->origParameters)[i], buf, &hgs); - } - } - buf->writeByte(')'); - } - - // emit constraints if declaration is a templated declaration - if (td && td->constraint) - { - buf->writestring(" if ("); - ::toCBuffer(td->constraint, buf, &hgs); - buf->writeByte(')'); - } - - if (d->isDeprecated()) - buf->writestring(")"); - - buf->writestring(";\n"); - } - - void visit(AliasDeclaration *ad) - { - //printf("AliasDeclaration::toDocbuffer() %s\n", ad->toChars()); - if (!ad->ident) - return; - - if (ad->isDeprecated()) - buf->writestring("deprecated "); - - emitProtection(buf, ad->protection); - buf->printf("alias %s = ", ad->toChars()); - - if (Dsymbol *sa = ad->aliassym) // ident alias - { - prettyPrintDsymbol(sa, ad->parent); - } - else if (Type *type = ad->getType()) // type alias - { - if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum) - { - if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type - prettyPrintDsymbol(s, ad->parent); - else - buf->writestring(type->toChars()); - } - else - { - // simple type - buf->writestring(type->toChars()); - } - } - - buf->writestring(";\n"); - } - - void parentToBuffer(Dsymbol *s) - { - if (s && !s->isPackage() && !s->isModule()) - { - parentToBuffer(s->parent); - buf->writestring(s->toChars()); - buf->writestring("."); - } - } - - static bool inSameModule(Dsymbol *s, Dsymbol *p) - { - for ( ; s ; s = s->parent) - { - if (s->isModule()) - break; - } - - for ( ; p ; p = p->parent) - { - if (p->isModule()) - break; - } - - return s == p; - } - - void prettyPrintDsymbol(Dsymbol *s, Dsymbol *parent) - { - if (s->parent && (s->parent == parent)) // in current scope -> naked name - { - buf->writestring(s->toChars()); - } - else if (!inSameModule(s, parent)) // in another module -> full name - { - buf->writestring(s->toPrettyChars()); - } - else // nested in a type in this module -> full name w/o module name - { - // if alias is nested in a user-type use module-scope lookup - if (!parent->isModule() && !parent->isPackage()) - buf->writestring("."); - - parentToBuffer(s->parent); - buf->writestring(s->toChars()); - } - } - - void visit(AggregateDeclaration *ad) - { - if (!ad->ident) - return; - - buf->printf("%s %s", ad->kind(), ad->toChars()); - buf->writestring(";\n"); - } - - void visit(StructDeclaration *sd) - { - //printf("StructDeclaration::toDocbuffer() %s\n", sd->toChars()); - if (!sd->ident) - return; - - if (TemplateDeclaration *td = getEponymousParent(sd)) - { - toDocBuffer(td, buf, sc); - } - else - { - buf->printf("%s %s", sd->kind(), sd->toChars()); - } - buf->writestring(";\n"); - } - - void visit(ClassDeclaration *cd) - { - //printf("ClassDeclaration::toDocbuffer() %s\n", cd->toChars()); - if (!cd->ident) - return; - - if (TemplateDeclaration *td = getEponymousParent(cd)) - { - toDocBuffer(td, buf, sc); - } - else - { - if (!cd->isInterfaceDeclaration() && cd->isAbstract()) - buf->writestring("abstract "); - buf->printf("%s %s", cd->kind(), cd->toChars()); - } - int any = 0; - for (size_t i = 0; i < cd->baseclasses->length; i++) - { - BaseClass *bc = (*cd->baseclasses)[i]; - - if (bc->sym && bc->sym->ident == Id::Object) - continue; - - if (any) - buf->writestring(", "); - else - { - buf->writestring(": "); - any = 1; - } - emitProtection(buf, Prot(Prot::public_)); - if (bc->sym) - { - buf->printf("$(DDOC_PSUPER_SYMBOL %s)", bc->sym->toPrettyChars()); - } - else - { - HdrGenState hgs; - ::toCBuffer(bc->type, buf, NULL, &hgs); - } - } - buf->writestring(";\n"); - } - - void visit(EnumDeclaration *ed) - { - if (!ed->ident) - return; - - buf->printf("%s %s", ed->kind(), ed->toChars()); - if (ed->memtype) - { - buf->writestring(": $(DDOC_ENUM_BASETYPE "); - HdrGenState hgs; - ::toCBuffer(ed->memtype, buf, NULL, &hgs); - buf->writestring(")"); - } - buf->writestring(";\n"); - } - - void visit(EnumMember *em) - { - if (!em->ident) - return; - - buf->writestring(em->toChars()); - } - }; - - ToDocBuffer v(buf, sc); - s->accept(&v); -} - -/********************************* DocComment *********************************/ - -DocComment *DocComment::parse(Dsymbol *s, const utf8_t *comment) -{ - //printf("parse(%s): '%s'\n", s->toChars(), comment); - DocComment *dc = new DocComment(); - dc->a.push(s); - if (!comment) - return dc; - - dc->parseSections(comment); - - for (size_t i = 0; i < dc->sections.length; i++) - { - Section *sec = dc->sections[i]; - - if (icmp("copyright", sec->name, sec->namelen) == 0) - { - dc->copyright = sec; - } - if (icmp("macros", sec->name, sec->namelen) == 0) - { - dc->macros = sec; - } - } - - return dc; -} - -/***************************************** - * Parse next paragraph out of *pcomment. - * Update *pcomment to point past paragraph. - * Returns NULL if no more paragraphs. - * If paragraph ends in 'identifier:', - * then (*pcomment)[0 .. idlen] is the identifier. - */ - -void DocComment::parseSections(const utf8_t *comment) -{ - const utf8_t *p; - const utf8_t *pstart; - const utf8_t *pend; - const utf8_t *idstart = NULL; // dead-store to prevent spurious warning - size_t idlen; - - const utf8_t *name = NULL; - size_t namelen = 0; - - //printf("parseSections('%s')\n", comment); - p = comment; - while (*p) - { - const utf8_t *pstart0 = p; - p = skipwhitespace(p); - pstart = p; - pend = p; - - /* Find end of section, which is ended by one of: - * 'identifier:' (but not inside a code section) - * '\0' - */ - idlen = 0; - int inCode = 0; - while (1) - { - // Check for start/end of a code section - if (*p == '-') - { - if (!inCode) - { - // restore leading indentation - while (pstart0 < pstart && isIndentWS(pstart-1)) --pstart; - } - - int numdash = 0; - while (*p == '-') - { - ++numdash; - p++; - } - // BUG: handle UTF PS and LS too - if ((!*p || *p == '\r' || *p == '\n') && numdash >= 3) - inCode ^= 1; - pend = p; - } - - if (!inCode && isIdStart(p)) - { - const utf8_t *q = p + utfStride(p); - while (isIdTail(q)) - q += utfStride(q); - // Detected tag ends it - if (*q == ':' && isupper(*p) - && (isspace(q[1]) || q[1] == 0)) - { - idlen = q - p; - idstart = p; - for (pend = p; pend > pstart; pend--) - { - if (pend[-1] == '\n') - break; - } - p = q + 1; - break; - } - } - while (1) - { - if (!*p) - goto L1; - if (*p == '\n') - { - p++; - if (*p == '\n' && !summary && !namelen && !inCode) - { - pend = p; - p++; - goto L1; - } - break; - } - p++; - pend = p; - } - p = skipwhitespace(p); - } - L1: - - if (namelen || pstart < pend) - { - Section *s; - if (icmp("Params", name, namelen) == 0) - s = new ParamSection(); - else if (icmp("Macros", name, namelen) == 0) - s = new MacroSection(); - else - s = new Section(); - s->name = name; - s->namelen = namelen; - s->body = pstart; - s->bodylen = pend - pstart; - s->nooutput = 0; - - //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body); - - sections.push(s); - - if (!summary && !namelen) - summary = s; - } - - if (idlen) - { - name = idstart; - namelen = idlen; - } - else - { - name = NULL; - namelen = 0; - if (!*p) - break; - } - } -} - -void DocComment::writeSections(Scope *sc, Dsymbols *a, OutBuffer *buf) -{ - assert(a->length); - - //printf("DocComment::writeSections()\n"); - Loc loc = (*a)[0]->loc; - if (Module *m = (*a)[0]->isModule()) - { - if (m->md) - loc = m->md->loc; - } - - size_t offset1 = buf->length(); - buf->writestring("$(DDOC_SECTIONS "); - size_t offset2 = buf->length(); - - for (size_t i = 0; i < sections.length; i++) - { - Section *sec = sections[i]; - if (sec->nooutput) - continue; - - //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body); - if (!sec->namelen && i == 0) - { - buf->writestring("$(DDOC_SUMMARY "); - size_t o = buf->length(); - buf->write(sec->body, sec->bodylen); - escapeStrayParenthesis(loc, buf, o); - highlightText(sc, a, buf, o); - buf->writestring(")\n"); - } - else - sec->write(loc, this, sc, a, buf); - } - - for (size_t i = 0; i < a->length; i++) - { - Dsymbol *s = (*a)[i]; - if (Dsymbol *td = getEponymousParent(s)) - s = td; - - for (UnitTestDeclaration *utd = s->ddocUnittest; utd; utd = utd->ddocUnittest) - { - if (utd->protection.kind == Prot::private_ || !utd->comment || !utd->fbody) - continue; - - // Strip whitespaces to avoid showing empty summary - const utf8_t *c = utd->comment; - while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r') ++c; - - buf->writestring("$(DDOC_EXAMPLES "); - - size_t o = buf->length(); - buf->writestring((const char *)c); - - if (utd->codedoc) - { - size_t n = getCodeIndent(utd->codedoc); - while (n--) buf->writeByte(' '); - buf->writestring("----\n"); - buf->writestring(utd->codedoc); - buf->writestring("----\n"); - highlightText(sc, a, buf, o); - } - - buf->writestring(")"); - } - } - - if (buf->length() == offset2) - { - /* Didn't write out any sections, so back out last write - */ - buf->setsize(offset1); - buf->writestring("$(DDOC_BLANKLINE)\n"); - } - else - buf->writestring(")\n"); -} - -/*************************************************** - */ - -void Section::write(Loc loc, DocComment *, Scope *sc, Dsymbols *a, OutBuffer *buf) -{ - assert(a->length); - - if (namelen) - { - static const char *table[] = - { - "AUTHORS", "BUGS", "COPYRIGHT", "DATE", - "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE", - "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS", - "VERSION", NULL - }; - - for (size_t i = 0; table[i]; i++) - { - if (icmp(table[i], name, namelen) == 0) - { - buf->printf("$(DDOC_%s ", table[i]); - goto L1; - } - } - - buf->writestring("$(DDOC_SECTION "); - - // Replace _ characters with spaces - buf->writestring("$(DDOC_SECTION_H "); - size_t o = buf->length(); - for (size_t u = 0; u < namelen; u++) - { - utf8_t c = name[u]; - buf->writeByte((c == '_') ? ' ' : c); - } - escapeStrayParenthesis(loc, buf, o); - buf->writestring(":)\n"); - } - else - { - buf->writestring("$(DDOC_DESCRIPTION "); - } - L1: - size_t o = buf->length(); - buf->write(body, bodylen); - escapeStrayParenthesis(loc, buf, o); - highlightText(sc, a, buf, o); - buf->writestring(")\n"); -} - -/*************************************************** - */ - -void ParamSection::write(Loc loc, DocComment *, Scope *sc, Dsymbols *a, OutBuffer *buf) -{ - assert(a->length); - Dsymbol *s = (*a)[0]; // test - - const utf8_t *p = body; - size_t len = bodylen; - const utf8_t *pend = p + len; - - const utf8_t *tempstart = NULL; - size_t templen = 0; - - const utf8_t *namestart = NULL; - size_t namelen = 0; // !=0 if line continuation - - const utf8_t *textstart = NULL; - size_t textlen = 0; - - size_t paramcount = 0; - - buf->writestring("$(DDOC_PARAMS "); - while (p < pend) - { - // Skip to start of macro - while (1) - { - switch (*p) - { - case ' ': - case '\t': - p++; - continue; - - case '\n': - p++; - goto Lcont; - - default: - if (isIdStart(p) || isCVariadicArg(p, pend - p)) - break; - if (namelen) - goto Ltext; // continuation of prev macro - goto Lskipline; - } - break; - } - tempstart = p; - - while (isIdTail(p)) - p += utfStride(p); - if (isCVariadicArg(p, pend - p)) - p += 3; - - templen = p - tempstart; - - while (*p == ' ' || *p == '\t') - p++; - - if (*p != '=') - { - if (namelen) - goto Ltext; // continuation of prev macro - goto Lskipline; - } - p++; - - if (namelen) - { - // Output existing param - - L1: - //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); - ++paramcount; - HdrGenState hgs; - buf->writestring("$(DDOC_PARAM_ROW "); - { - buf->writestring("$(DDOC_PARAM_ID "); - { - size_t o = buf->length(); - Parameter *fparam = isFunctionParameter(a, namestart, namelen); - if (!fparam) - { - // Comments on a template might refer to function parameters within. - // Search the parameters of nested eponymous functions (with the same name.) - fparam = isEponymousFunctionParameter(a, namestart, namelen); - } - bool isCVariadic = isCVariadicParameter(a, namestart, namelen); - if (isCVariadic) - { - buf->writestring("..."); - } - else if (fparam && fparam->type && fparam->ident) - { - ::toCBuffer(fparam->type, buf, fparam->ident, &hgs); - } - else - { - if (isTemplateParameter(a, namestart, namelen)) - { - // 10236: Don't count template parameters for params check - --paramcount; - } - else if (!fparam) - { - warning(s->loc, "Ddoc: function declaration has no parameter '%.*s'", (int)namelen, namestart); - } - buf->write(namestart, namelen); - } - escapeStrayParenthesis(loc, buf, o); - highlightCode(sc, a, buf, o); - } - buf->writestring(")\n"); - - buf->writestring("$(DDOC_PARAM_DESC "); - { - size_t o = buf->length(); - buf->write(textstart, textlen); - escapeStrayParenthesis(loc, buf, o); - highlightText(sc, a, buf, o); - } - buf->writestring(")"); - } - buf->writestring(")\n"); - namelen = 0; - if (p >= pend) - break; - } - - namestart = tempstart; - namelen = templen; - - while (*p == ' ' || *p == '\t') - p++; - textstart = p; - - Ltext: - while (*p != '\n') - p++; - textlen = p - textstart; - p++; - - Lcont: - continue; - - Lskipline: - // Ignore this line - while (*p++ != '\n') - ; - } - if (namelen) - goto L1; // write out last one - buf->writestring(")\n"); - - TypeFunction *tf = a->length == 1 ? isTypeFunction(s) : NULL; - if (tf) - { - size_t pcount = (tf->parameterList.parameters ? tf->parameterList.parameters->length : 0) + - (int)(tf->parameterList.varargs == VARARGvariadic); - if (pcount != paramcount) - { - warning(s->loc, "Ddoc: parameter count mismatch"); - } - } -} - -/*************************************************** - */ - -void MacroSection::write(Loc, DocComment *dc, Scope *, Dsymbols *, OutBuffer *) -{ - //printf("MacroSection::write()\n"); - DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen); -} - -/************************************************ - * Parse macros out of Macros: section. - * Macros are of the form: - * name1 = value1 - * - * name2 = value2 - */ - -void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, const utf8_t *m, size_t mlen) -{ - const utf8_t *p = m; - size_t len = mlen; - const utf8_t *pend = p + len; - - const utf8_t *tempstart = NULL; - size_t templen = 0; - - const utf8_t *namestart = NULL; - size_t namelen = 0; // !=0 if line continuation - - const utf8_t *textstart = NULL; - size_t textlen = 0; - - while (p < pend) - { - // Skip to start of macro - while (1) - { - if (p >= pend) - goto Ldone; - switch (*p) - { - case ' ': - case '\t': - p++; - continue; - - case '\r': - case '\n': - p++; - goto Lcont; - - default: - if (isIdStart(p)) - break; - if (namelen) - goto Ltext; // continuation of prev macro - goto Lskipline; - } - break; - } - tempstart = p; - - while (1) - { - if (p >= pend) - goto Ldone; - if (!isIdTail(p)) - break; - p += utfStride(p); - } - templen = p - tempstart; - - while (1) - { - if (p >= pend) - goto Ldone; - if (!(*p == ' ' || *p == '\t')) - break; - p++; - } - - if (*p != '=') - { - if (namelen) - goto Ltext; // continuation of prev macro - goto Lskipline; - } - p++; - if (p >= pend) - goto Ldone; - - if (namelen) - { - // Output existing macro - L1: - //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); - if (icmp("ESCAPES", namestart, namelen) == 0) - parseEscapes(pescapetable, textstart, textlen); - else - Macro::define(pmacrotable, namestart, namelen, textstart, textlen); - namelen = 0; - if (p >= pend) - break; - } - - namestart = tempstart; - namelen = templen; - - while (p < pend && (*p == ' ' || *p == '\t')) - p++; - textstart = p; - - Ltext: - while (p < pend && *p != '\r' && *p != '\n') - p++; - textlen = p - textstart; - - p++; - //printf("p = %p, pend = %p\n", p, pend); - - Lcont: - continue; - - Lskipline: - // Ignore this line - while (p < pend && *p != '\r' && *p != '\n') - p++; - } -Ldone: - if (namelen) - goto L1; // write out last one -} - -/************************************** - * Parse escapes of the form: - * /c/string/ - * where c is a single character. - * Multiple escapes can be separated - * by whitespace and/or commas. - */ - -void DocComment::parseEscapes(Escape **pescapetable, const utf8_t *textstart, size_t textlen) -{ - Escape *escapetable = *pescapetable; - - if (!escapetable) - { - escapetable = new Escape; - memset(escapetable, 0, sizeof(Escape)); - *pescapetable = escapetable; - } - //printf("parseEscapes('%.*s') pescapetable = %p\n", textlen, textstart, pescapetable); - const utf8_t *p = textstart; - const utf8_t *pend = p + textlen; - - while (1) - { - while (1) - { - if (p + 4 >= pend) - return; - if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ',')) - break; - p++; - } - if (p[0] != '/' || p[2] != '/') - return; - utf8_t c = p[1]; - p += 3; - const utf8_t *start = p; - while (1) - { - if (p >= pend) - return; - if (*p == '/') - break; - p++; - } - size_t len = p - start; - char *s = (char *)memcpy(mem.xmalloc(len + 1), start, len); - s[len] = 0; - escapetable->strings[c] = s; - //printf("\t%c = '%s'\n", c, s); - p++; - } -} - - -/****************************************** - * Compare 0-terminated string with length terminated string. - * Return < 0, ==0, > 0 - */ - -int cmp(const char *stringz, const void *s, size_t slen) -{ - size_t len1 = strlen(stringz); - - if (len1 != slen) - return (int)(len1 - slen); - return memcmp(stringz, s, slen); -} - -int icmp(const char *stringz, const void *s, size_t slen) -{ - size_t len1 = strlen(stringz); - - if (len1 != slen) - return (int)(len1 - slen); - return Port::memicmp(stringz, (const char *)s, slen); -} - -/***************************************** - * Return true if comment consists entirely of "ditto". - */ - -bool isDitto(const utf8_t *comment) -{ - if (comment) - { - const utf8_t *p = skipwhitespace(comment); - - if (Port::memicmp((const char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0) - return true; - } - return false; -} - -/********************************************** - * Skip white space. - */ - -const utf8_t *skipwhitespace(const utf8_t *p) -{ - for (; 1; p++) - { - switch (*p) - { - case ' ': - case '\t': - case '\n': - continue; - } - break; - } - return p; -} - - -/************************************************ - * Scan forward to one of: - * start of identifier - * beginning of next line - * end of buf - */ - -size_t skiptoident(OutBuffer *buf, size_t i) -{ - while (i < buf->length()) - { - dchar_t c; - - size_t oi = i; - if (utf_decodeChar((utf8_t *)buf->slice().ptr, buf->length(), &i, &c)) - { - /* Ignore UTF errors, but still consume input - */ - break; - } - if (c >= 0x80) - { - if (!isUniAlpha(c)) - continue; - } - else if (!(isalpha(c) || c == '_' || c == '\n')) - continue; - i = oi; - break; - } - return i; -} - -/************************************************ - * Scan forward past end of identifier. - */ - -size_t skippastident(OutBuffer *buf, size_t i) -{ - while (i < buf->length()) - { - dchar_t c; - - size_t oi = i; - if (utf_decodeChar((utf8_t *)buf->slice().ptr, buf->length(), &i, &c)) - { - /* Ignore UTF errors, but still consume input - */ - break; - } - if (c >= 0x80) - { - if (isUniAlpha(c)) - continue; - } - else if (isalnum(c) || c == '_') - continue; - i = oi; - break; - } - return i; -} - - -/************************************************ - * Scan forward past URL starting at i. - * We don't want to highlight parts of a URL. - * Returns: - * i if not a URL - * index just past it if it is a URL - */ - -size_t skippastURL(OutBuffer *buf, size_t i) -{ - size_t length = buf->length() - i; - utf8_t *p = (utf8_t *)&buf->slice().ptr[i]; - size_t j; - unsigned sawdot = 0; - - if (length > 7 && Port::memicmp((char *)p, "http://", 7) == 0) - { - j = 7; - } - else if (length > 8 && Port::memicmp((char *)p, "https://", 8) == 0) - { - j = 8; - } - else - goto Lno; - - for (; j < length; j++) - { - utf8_t c = p[j]; - if (isalnum(c)) - continue; - if (c == '-' || c == '_' || c == '?' || - c == '=' || c == '%' || c == '&' || - c == '/' || c == '+' || c == '#' || - c == '~') - continue; - if (c == '.') - { - sawdot = 1; - continue; - } - break; - } - if (sawdot) - return i + j; - -Lno: - return i; -} - - -/**************************************************** - */ - -bool isIdentifier(Dsymbols *a, const utf8_t *p, size_t len) -{ - for (size_t i = 0; i < a->length; i++) - { - const char *s = (*a)[i]->ident->toChars(); - if (cmp(s, p, len) == 0) - return true; - } - return false; -} - -/**************************************************** - */ - -bool isKeyword(utf8_t *p, size_t len) -{ - static const char *table[] = { "true", "false", "null", NULL }; - - for (int i = 0; table[i]; i++) - { - if (cmp(table[i], p, len) == 0) - return true; - } - return false; -} - -/**************************************************** - */ - -TypeFunction *isTypeFunction(Dsymbol *s) -{ - FuncDeclaration *f = s->isFuncDeclaration(); - - /* f->type may be NULL for template members. - */ - if (f && f->type) - { - Type *t = f->originalType ? f->originalType : f->type; - if (t->ty == Tfunction) - return (TypeFunction *)t; - } - return NULL; -} - -/**************************************************** - */ - -Parameter *isFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len) -{ - for (size_t i = 0; i < a->length; i++) - { - Parameter *fparam = isFunctionParameter((*a)[i], p, len); - if (fparam) - { - return fparam; - } - } - return NULL; -} - -/**************************************************** - */ - -TemplateParameter *isTemplateParameter(Dsymbols *a, const utf8_t *p, size_t len) -{ - for (size_t i = 0; i < a->length; i++) - { - TemplateDeclaration *td = (*a)[i]->isTemplateDeclaration(); - // Check for the parent, if the current symbol is not a template declaration. - if (!td) - td = getEponymousParent((*a)[i]); - if (td && td->origParameters) - { - for (size_t k = 0; k < td->origParameters->length; k++) - { - TemplateParameter *tp = (*td->origParameters)[k]; - if (tp->ident && cmp(tp->ident->toChars(), p, len) == 0) - { - return tp; - } - } - } - } - return NULL; -} - -/**************************************************** - * Return true if str is a reserved symbol name - * that starts with a double underscore. - */ - -bool isReservedName(utf8_t *str, size_t len) -{ - static const char *table[] = { - "__ctor", "__dtor", "__postblit", "__invariant", "__unitTest", - "__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result", - "__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp", - "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__", - "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__", - "__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", NULL }; - - for (int i = 0; table[i]; i++) - { - if (cmp(table[i], str, len) == 0) - return true; - } - return false; -} - -/************************************************** - * Highlight text section. - */ - -void highlightText(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset) -{ - Dsymbol *s = a->length ? (*a)[0] : NULL; // test - - //printf("highlightText()\n"); - - int leadingBlank = 1; - int inCode = 0; - int inBacktick = 0; - //int inComment = 0; // in <!-- ... --> comment - size_t iCodeStart = 0; // start of code section - size_t codeIndent = 0; - - size_t iLineStart = offset; - - for (size_t i = offset; i < buf->length(); i++) - { - utf8_t c = buf->slice().ptr[i]; - - Lcont: - switch (c) - { - case ' ': - case '\t': - break; - - case '\n': - if (inBacktick) - { - // `inline code` is only valid if contained on a single line - // otherwise, the backticks should be output literally. - // - // This lets things like `output from the linker' display - // unmolested while keeping the feature consistent with GitHub. - - inBacktick = false; - inCode = false; // the backtick also assumes we're in code - - // Nothing else is necessary since the DDOC_BACKQUOTED macro is - // inserted lazily at the close quote, meaning the rest of the - // text is already OK. - } - - if (!sc->_module->isDocFile && - !inCode && i == iLineStart && i + 1 < buf->length()) // if "\n\n" - { - static const char blankline[] = "$(DDOC_BLANKLINE)\n"; - - i = buf->insert(i, blankline, strlen(blankline)); - } - leadingBlank = 1; - iLineStart = i + 1; - break; - - case '<': - { - leadingBlank = 0; - if (inCode) - break; - utf8_t *p = (utf8_t *)&buf->slice().ptr[i]; - const char *se = sc->_module->escapetable->escapeChar('<'); - if (se && strcmp(se, "<") == 0) - { - // Generating HTML - // Skip over comments - if (p[1] == '!' && p[2] == '-' && p[3] == '-') - { - size_t j = i + 4; - p += 4; - while (1) - { - if (j == buf->length()) - goto L1; - if (p[0] == '-' && p[1] == '-' && p[2] == '>') - { - i = j + 2; // place on closing '>' - break; - } - j++; - p++; - } - break; - } - - // Skip over HTML tag - if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2]))) - { - size_t j = i + 2; - p += 2; - while (1) - { - if (j == buf->length()) - break; - if (p[0] == '>') - { - i = j; // place on closing '>' - break; - } - j++; - p++; - } - break; - } - } - L1: - // Replace '<' with '<' character entity - if (se) - { - size_t len = strlen(se); - buf->remove(i, 1); - i = buf->insert(i, se, len); - i--; // point to ';' - } - break; - } - case '>': - { - leadingBlank = 0; - if (inCode) - break; - // Replace '>' with '>' character entity - const char *se = sc->_module->escapetable->escapeChar('>'); - if (se) - { - size_t len = strlen(se); - buf->remove(i, 1); - i = buf->insert(i, se, len); - i--; // point to ';' - } - break; - } - case '&': - { - leadingBlank = 0; - if (inCode) - break; - utf8_t *p = (utf8_t *)&buf->slice().ptr[i]; - if (p[1] == '#' || isalpha(p[1])) - break; // already a character entity - // Replace '&' with '&' character entity - const char *se = sc->_module->escapetable->escapeChar('&'); - if (se) - { - size_t len = strlen(se); - buf->remove(i, 1); - i = buf->insert(i, se, len); - i--; // point to ';' - } - break; - } - case '`': - { - if (inBacktick) - { - inBacktick = 0; - inCode = 0; - - OutBuffer codebuf; - - codebuf.write(buf->slice().ptr + iCodeStart + 1, i - (iCodeStart + 1)); - - // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL - highlightCode(sc, a, &codebuf, 0); - - buf->remove(iCodeStart, i - iCodeStart + 1); // also trimming off the current ` - - static const char pre[] = "$(DDOC_BACKQUOTED "; - i = buf->insert(iCodeStart, pre, strlen(pre)); - i = buf->insert(i, (char *)codebuf.slice().ptr, codebuf.length()); - i = buf->insert(i, ")", 1); - - i--; // point to the ending ) so when the for loop does i++, it will see the next character - - break; - } - - if (inCode) - break; - - inCode = 1; - inBacktick = 1; - codeIndent = 0; // inline code is not indented - - // All we do here is set the code flags and record - // the location. The macro will be inserted lazily - // so we can easily cancel the inBacktick if we come - // across a newline character. - iCodeStart = i; - - break; - } - case '-': - /* A line beginning with --- delimits a code section. - * inCode tells us if it is start or end of a code section. - */ - if (leadingBlank) - { - size_t istart = i; - size_t eollen = 0; - - leadingBlank = 0; - while (1) - { - ++i; - if (i >= buf->length()) - break; - c = buf->slice().ptr[i]; - if (c == '\n') - { - eollen = 1; - break; - } - if (c == '\r') - { - eollen = 1; - if (i + 1 >= buf->length()) - break; - if (buf->slice().ptr[i + 1] == '\n') - { - eollen = 2; - break; - } - } - // BUG: handle UTF PS and LS too - if (c != '-') - goto Lcont; - } - if (i - istart < 3) - goto Lcont; - - // We have the start/end of a code section - - // Remove the entire --- line, including blanks and \n - buf->remove(iLineStart, i - iLineStart + eollen); - i = iLineStart; - - if (inCode && (i <= iCodeStart)) - { - // Empty code section, just remove it completely. - inCode = 0; - break; - } - - if (inCode) - { - inCode = 0; - // The code section is from iCodeStart to i - OutBuffer codebuf; - - codebuf.write(buf->slice().ptr + iCodeStart, i - iCodeStart); - codebuf.writeByte(0); - - // Remove leading indentations from all lines - bool lineStart = true; - utf8_t *endp = (utf8_t *)codebuf.slice().ptr + codebuf.length(); - for (utf8_t *p = (utf8_t *)codebuf.slice().ptr; p < endp; ) - { - if (lineStart) - { - size_t j = codeIndent; - utf8_t *q = p; - while (j-- > 0 && q < endp && isIndentWS(q)) - ++q; - codebuf.remove(p - (utf8_t *)codebuf.slice().ptr, q - p); - assert((utf8_t *)codebuf.slice().ptr <= p); - assert(p < (utf8_t *)codebuf.slice().ptr + codebuf.length()); - lineStart = false; - endp = (utf8_t *)codebuf.slice().ptr + codebuf.length(); // update - continue; - } - if (*p == '\n') - lineStart = true; - ++p; - } - - highlightCode2(sc, a, &codebuf, 0); - buf->remove(iCodeStart, i - iCodeStart); - i = buf->insert(iCodeStart, codebuf.slice().ptr, codebuf.length()); - i = buf->insert(i, (const char *)")\n", 2); - i -= 2; // in next loop, c should be '\n' - } - else - { - static const char d_code[] = "$(D_CODE "; - - inCode = 1; - codeIndent = istart - iLineStart; // save indent count - i = buf->insert(i, d_code, strlen(d_code)); - iCodeStart = i; - i--; // place i on > - leadingBlank = true; - } - } - break; - - default: - leadingBlank = 0; - if (sc->_module->isDocFile || inCode) - break; - - utf8_t *start = (utf8_t *)buf->slice().ptr + i; - if (isIdStart(start)) - { - size_t j = skippastident(buf, i); - if (i < j) - { - size_t k = skippastURL(buf, i); - if (i < k) - { - i = k - 1; - break; - } - } - else - break; - size_t len = j - i; - - // leading '_' means no highlight unless it's a reserved symbol name - if (c == '_' && - (i == 0 || !isdigit(*(start - 1))) && - (i == buf->length() - 1 || !isReservedName(start, len))) - { - buf->remove(i, 1); - i = j - 1; - break; - } - if (isIdentifier(a, start, len)) - { - i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; - break; - } - if (isKeyword(start, len)) - { - i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1; - break; - } - if (isFunctionParameter(a, start, len)) - { - //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); - i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; - break; - } - - i = j - 1; - } - break; - } - } - if (inCode) - error(s ? s->loc : Loc(), "unmatched --- in DDoc comment"); -} - -/************************************************** - * Highlight code for DDOC section. - */ - -void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) -{ - //printf("highlightCode(s = %s '%s')\n", s->kind(), s->toChars()); - OutBuffer ancbuf; - emitAnchor(&ancbuf, s, sc); - buf->insert(offset, (char *)ancbuf.slice().ptr, ancbuf.length()); - offset += ancbuf.length(); - - Dsymbols a; - a.push(s); - highlightCode(sc, &a, buf, offset); -} - -/**************************************************** - */ - -void highlightCode(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset) -{ - //printf("highlightCode(a = '%s')\n", a->toChars()); - - for (size_t i = offset; i < buf->length(); i++) - { - utf8_t c = buf->slice().ptr[i]; - const char *se = sc->_module->escapetable->escapeChar(c); - if (se) - { - size_t len = strlen(se); - buf->remove(i, 1); - i = buf->insert(i, se, len); - i--; // point to ';' - continue; - } - - utf8_t *start = (utf8_t *)buf->slice().ptr + i; - if (isIdStart(start)) - { - size_t j = skippastident(buf, i); - if (i < j) - { - size_t len = j - i; - if (isIdentifier(a, start, len)) - { - i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; - continue; - } - if (isFunctionParameter(a, start, len)) - { - //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); - i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; - continue; - } - i = j - 1; - } - } - } -} - -/**************************************** - */ - -void highlightCode3(Scope *sc, OutBuffer *buf, const utf8_t *p, const utf8_t *pend) -{ - for (; p < pend; p++) - { - const char *s = sc->_module->escapetable->escapeChar(*p); - if (s) - buf->writestring(s); - else - buf->writeByte(*p); - } -} - -/************************************************** - * Highlight code for CODE section. - */ - -void highlightCode2(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset) -{ - unsigned errorsave = global.errors; - Lexer lex(NULL, (utf8_t *)buf->slice().ptr, 0, buf->length() - 1, 0, 1); - OutBuffer res; - const utf8_t *lastp = (utf8_t *)buf->slice().ptr; - - //printf("highlightCode2('%.*s')\n", buf->length() - 1, buf->slice().ptr); - res.reserve(buf->length()); - while (1) - { - Token tok; - lex.scan(&tok); - highlightCode3(sc, &res, lastp, tok.ptr); - - const char *highlight = NULL; - switch (tok.value) - { - case TOKidentifier: - { - if (!sc) - break; - size_t len = lex.p - tok.ptr; - if (isIdentifier(a, tok.ptr, len)) - { - highlight = "$(D_PSYMBOL "; - break; - } - if (isFunctionParameter(a, tok.ptr, len)) - { - //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); - highlight = "$(D_PARAM "; - break; - } - break; - } - case TOKcomment: - highlight = "$(D_COMMENT "; - break; - - case TOKstring: - highlight = "$(D_STRING "; - break; - - default: - if (tok.isKeyword()) - highlight = "$(D_KEYWORD "; - break; - } - if (highlight) - { - res.writestring(highlight); - size_t o = res.length(); - highlightCode3(sc, &res, tok.ptr, lex.p); - if (tok.value == TOKcomment || tok.value == TOKstring) - escapeDdocString(&res, o); // Bugzilla 7656, 7715, and 10519 - res.writeByte(')'); - } - else - highlightCode3(sc, &res, tok.ptr, lex.p); - if (tok.value == TOKeof) - break; - lastp = lex.p; - } - buf->setsize(offset); - buf->write(&res); - global.errors = errorsave; -} - -/*************************************** - * Find character string to replace c with. - */ - -const char *Escape::escapeChar(unsigned c) -{ - assert(c < 256); - //printf("escapeChar('%c') => %p, %p\n", c, strings, strings[c]); - return strings[c]; -} - -/**************************************** - * Determine if p points to the start of a "..." parameter identifier. - */ - -bool isCVariadicArg(const utf8_t *p, size_t len) -{ - return len >= 3 && cmp("...", p, 3) == 0; -} - -/**************************************** - * Determine if p points to the start of an identifier. - */ - -bool isIdStart(const utf8_t *p) -{ - unsigned c = *p; - if (isalpha(c) || c == '_') - return true; - if (c >= 0x80) - { - size_t i = 0; - if (utf_decodeChar(p, 4, &i, &c)) - return false; // ignore errors - if (isUniAlpha(c)) - return true; - } - return false; -} - -/**************************************** - * Determine if p points to the rest of an identifier. - */ - -bool isIdTail(const utf8_t *p) -{ - unsigned c = *p; - if (isalnum(c) || c == '_') - return true; - if (c >= 0x80) - { - size_t i = 0; - if (utf_decodeChar(p, 4, &i, &c)) - return false; // ignore errors - if (isUniAlpha(c)) - return true; - } - return false; -} - -/**************************************** - * Determine if p points to the indentation space. - */ - -bool isIndentWS(const utf8_t *p) -{ - return (*p == ' ') || (*p == '\t'); -} - -/***************************************** - * Return number of bytes in UTF character. - */ - -int utfStride(const utf8_t *p) -{ - unsigned c = *p; - if (c < 0x80) - return 1; - size_t i = 0; - utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input - return (int)i; -} |