Imported Upstream version 1.9.8
[platform/upstream/doxygen.git] / src / docparser.cpp
index 88c97f0..dcdd9ef 100644 (file)
 #include "printdocvisitor.h"
 #include "util.h"
 #include "indexlist.h"
+#include "trace.h"
 
-// debug off
-#define DBG(x) do {} while(0)
-
-// debug to stdout
-//#define DBG(x) printf x
-
-// debug to stderr
-//#define myprintf(...) fprintf(stderr,__VA_ARGS__)
-//#define DBG(x) myprintf x
+#if !ENABLE_DOCPARSER_TRACING
+#undef  AUTO_TRACE
+#undef  AUTO_TRACE_ADD
+#undef  AUTO_TRACE_EXIT
+#define AUTO_TRACE(...)      (void)0
+#define AUTO_TRACE_ADD(...)  (void)0
+#define AUTO_TRACE_EXIT(...) (void)0
+#endif
 
 
 //---------------------------------------------------------------------------
@@ -59,14 +59,6 @@ IDocParserPtr createDocParser()
 //---------------------------------------------------------------------------
 DocParser::~DocParser()
 {
-  try
-  {
-    searchData.transfer();
-  }
-  catch(...)
-  {
-    err("Unexpected exception caught in DocParser\n");
-  }
 }
 
 void DocParser::pushContext()
@@ -80,7 +72,7 @@ void DocParser::pushContext()
   auto &ctx = contextStack.top();
   ctx = context;
   ctx.lineNo = tokenizer.getLineNr();
-  context.token = tokenizer.newToken();
+  context.token = tokenizer.token();
 }
 
 void DocParser::popContext()
@@ -88,10 +80,9 @@ void DocParser::popContext()
   auto &ctx = contextStack.top();
   context = ctx;
   tokenizer.setLineNr(ctx.lineNo);
-  context.token = ctx.token;
-  tokenizer.replaceToken(context.token);
   contextStack.pop();
   tokenizer.popContext();
+  context.token = tokenizer.token();
 
   //QCString indent;
   //indent.fill(' ',contextStack.size()*2+2);
@@ -189,12 +180,10 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty
       epstopdfArgs.sprintf("\"%s/%s.eps\" --outfile=\"%s/%s.pdf\"",
                            qPrint(outputDir), qPrint(baseName),
                           qPrint(outputDir), qPrint(baseName));
-      Portable::sysTimerStart();
       if (Portable::system("epstopdf",epstopdfArgs)!=0)
       {
        err("Problems running epstopdf. Check your TeX installation!\n");
       }
-      Portable::sysTimerStop();
       return baseName;
     }
   }
@@ -373,7 +362,7 @@ void DocParser::checkUnOrMultipleDocumentedParams()
     }
     else
     {
-      if (!context.paramsFound.size() && Config_getBool(WARN_IF_DOC_ERROR))
+      if (context.paramsFound.empty() && Config_getBool(WARN_IF_DOC_ERROR))
       {
         warn_doc_error(context.memberDef->docFile(),
                        context.memberDef->docLine(),
@@ -483,24 +472,21 @@ bool DocParser::findDocsForMemberOrCompound(const QCString &commandName,
   QCString name=removeRedundantWhiteSpace(cmdArg.left(funcStart));
   QCString args=cmdArg.right(l-funcStart);
   // try if the link is to a member
-  const MemberDef    *md=0;
-  const ClassDef     *cd=0;
-  const NamespaceDef *nd=0;
-  bool found = getDefs(
-      context.context.find('.')==-1?context.context:QCString(), // find('.') is a hack to detect files
+  GetDefInput input(
+      context.context.find('.')==-1 ? context.context : QCString(), // find('.') is a hack to detect files
       name,
-      args.isEmpty() ? QCString() : args,
-      md,cd,fd,nd,gd,FALSE,0,TRUE);
+      args.isEmpty() ? QCString() : args);
+  input.checkCV=true;
+  GetDefResult result = getDefs(input);
   //printf("found=%d context=%s name=%s\n",found,qPrint(context.context),qPrint(name));
-  if (found && md)
+  if (result.found && result.md)
   {
-    *pDoc=md->documentation();
-    *pBrief=md->briefDescription();
-    *pDef=md;
+    *pDoc=result.md->documentation();
+    *pBrief=result.md->briefDescription();
+    *pDef=result.md;
     return TRUE;
   }
 
-
   int scopeOffset=static_cast<int>(context.context.length());
   do // for each scope
   {
@@ -512,7 +498,7 @@ bool DocParser::findDocsForMemberOrCompound(const QCString &commandName,
     //printf("Trying fullName='%s'\n",qPrint(fullName));
 
     // try class, namespace, group, page, file reference
-    cd = Doxygen::classLinkedMap->find(fullName);
+    const ClassDef *cd = Doxygen::classLinkedMap->find(fullName);
     if (cd) // class
     {
       *pDoc=cd->documentation();
@@ -520,7 +506,7 @@ bool DocParser::findDocsForMemberOrCompound(const QCString &commandName,
       *pDef=cd;
       return TRUE;
     }
-    nd = Doxygen::namespaceLinkedMap->find(fullName);
+    const NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(fullName);
     if (nd) // namespace
     {
       *pDoc=nd->documentation();
@@ -562,6 +548,10 @@ void DocParser::errorHandleDefaultToken(DocNodeVariant *parent,int tok,
       warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol %s found as part of a %s",
            qPrint(context.token->name), qPrint(txt));
       break;
+    case TK_HTMLTAG:
+      warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <%s%s> found as part of a %s",
+           context.token->endTag ? "/" : "",qPrint(context.token->name), qPrint(txt));
+      break;
     default:
       children.append<DocWord>(this,parent,context.token->name);
       warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token %s found as part of a %s",
@@ -574,7 +564,7 @@ void DocParser::errorHandleDefaultToken(DocNodeVariant *parent,int tok,
 
 int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,const QCString &cmdName)
 {
-  DBG(("handleStyleArgument(%s)\n",qPrint(cmdName)));
+  AUTO_TRACE("cmdName={}",cmdName);
   QCString saveCmdName = cmdName;
   int tok=tokenizer.lex();
   if (tok!=TK_WHITESPACE)
@@ -606,7 +596,7 @@ int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,
           { // ignore </li> as the end of a style command
             continue;
           }
-          DBG(("handleStyleArgument(%s) end tok=%s\n",qPrint(saveCmdName), DocTokenizer::tokToString(tok)));
+          AUTO_TRACE_EXIT("end tok={}",DocTokenizer::tokToString(tok));
           return tok;
           break;
         default:
@@ -616,7 +606,7 @@ int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,
       break;
     }
   }
-  DBG(("handleStyleArgument(%s) end tok=%s\n",qPrint(saveCmdName), DocTokenizer::tokToString(tok)));
+  AUTO_TRACE_EXIT("end tok={}",DocTokenizer::tokToString(tok));
   return (tok==TK_NEWPARA || tok==TK_LISTITEM || tok==TK_ENDLIST
          ) ? tok : RetVal_OK;
 }
@@ -627,7 +617,7 @@ int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,
 void DocParser::handleStyleEnter(DocNodeVariant *parent,DocNodeList &children,
           DocStyleChange::Style s,const QCString &tagName,const HtmlAttribList *attribs)
 {
-  DBG(("HandleStyleEnter\n"));
+  AUTO_TRACE("tagName={}",tagName);
   children.append<DocStyleChange>(this,parent,context.nodeStack.size(),s,tagName,TRUE,attribs);
   context.styleStack.push(&children.back());
 }
@@ -638,7 +628,7 @@ void DocParser::handleStyleEnter(DocNodeVariant *parent,DocNodeList &children,
 void DocParser::handleStyleLeave(DocNodeVariant *parent,DocNodeList &children,
          DocStyleChange::Style s,const QCString &tagName)
 {
-  DBG(("HandleStyleLeave\n"));
+  AUTO_TRACE("tagName={}",tagName);
   QCString tagNameLower = QCString(tagName).lower();
 
   auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
@@ -688,6 +678,7 @@ void DocParser::handleStyleLeave(DocNodeVariant *parent,DocNodeList &children,
  */
 void DocParser::handlePendingStyleCommands(DocNodeVariant *parent,DocNodeList &children)
 {
+  AUTO_TRACE();
   if (!context.styleStack.empty())
   {
     const DocStyleChange *sc = &std::get<DocStyleChange>(*context.styleStack.top());
@@ -704,6 +695,7 @@ void DocParser::handlePendingStyleCommands(DocNodeVariant *parent,DocNodeList &c
 
 void DocParser::handleInitialStyleCommands(DocNodeVariant *parent,DocNodeList &children)
 {
+  AUTO_TRACE();
   while (!context.initialStyleStack.empty())
   {
     const DocStyleChange &sc = std::get<DocStyleChange>(*context.initialStyleStack.top());
@@ -715,7 +707,8 @@ void DocParser::handleInitialStyleCommands(DocNodeVariant *parent,DocNodeList &c
 int DocParser::handleAHref(DocNodeVariant *parent,DocNodeList &children,
                            const HtmlAttribList &tagHtmlAttribs)
 {
-  uint index=0;
+  AUTO_TRACE();
+  uint32_t index=0;
   int retval = RetVal_OK;
   for (const auto &opt : tagHtmlAttribs)
   {
@@ -739,12 +732,11 @@ int DocParser::handleAHref(DocNodeVariant *parent,DocNodeList &children,
       attrList.erase(attrList.begin()+index);
       QCString relPath;
       if (opt.value.at(0) != '#') relPath = context.relPath;
-      auto vDocHRef = children.append<DocHRef>(this, parent, attrList,
-                                               opt.value, relPath,
-                                               convertNameToFile(context.fileName, FALSE, TRUE));
-      DocHRef *href = children.get_last<DocHRef>();
+      children.append<DocHRef>(this, parent, attrList,
+                               opt.value, relPath,
+                               convertNameToFile(context.fileName, FALSE, TRUE));
       context.insideHtmlLink=TRUE;
-      retval = href->parse(vDocHRef);
+      retval = children.get_last<DocHRef>()->parse();
       context.insideHtmlLink=FALSE;
       break;
     }
@@ -758,6 +750,7 @@ int DocParser::handleAHref(DocNodeVariant *parent,DocNodeList &children,
 
 void DocParser::handleUnclosedStyleCommands()
 {
+  AUTO_TRACE();
   if (!context.initialStyleStack.empty())
   {
     QCString tagName = std::get<DocStyleChange>(*context.initialStyleStack.top()).tagName();
@@ -772,6 +765,7 @@ void DocParser::handleUnclosedStyleCommands()
 void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag)
 {
   QCString name = linkToText(SrcLangExt_Unknown,context.token->name,TRUE);
+  AUTO_TRACE("word={}",name);
   bool autolinkSupport = Config_getBool(AUTOLINK_SUPPORT);
   if (!autolinkSupport && !ignoreAutoLinkFlag) // no autolinking -> add as normal word
   {
@@ -783,7 +777,7 @@ void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bo
 
   const Definition *compound=0;
   const MemberDef  *member=0;
-  uint len = context.token->name.length();
+  uint32_t len = context.token->name.length();
   ClassDef *cd=0;
   bool ambig;
   FileDef *fd = findFileDef(Doxygen::inputNameLinkedMap,context.fileName,ambig);
@@ -880,6 +874,7 @@ void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bo
 void DocParser::handleParameterType(DocNodeVariant *parent,DocNodeList &children,const QCString &paramTypes)
 {
   QCString name = context.token->name; // save token name
+  AUTO_TRACE("name={}",name);
   QCString name1;
   int p=0,i,ii;
   while ((i=paramTypes.find('|',p))!=-1)
@@ -902,9 +897,9 @@ void DocParser::handleParameterType(DocNodeVariant *parent,DocNodeList &children
 
 void DocParser::handleInternalRef(DocNodeVariant *parent,DocNodeList &children)
 {
-  //printf("CMD_INTERNALREF\n");
   int tok=tokenizer.lex();
   QCString tokenName = context.token->name;
+  AUTO_TRACE("name={}",tokenName);
   if (tok!=TK_WHITESPACE)
   {
     warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\%s command",
@@ -919,12 +914,13 @@ void DocParser::handleInternalRef(DocNodeVariant *parent,DocNodeList &children)
         DocTokenizer::tokToString(tok),qPrint(tokenName));
     return;
   }
-  auto vDocInternalRef = children.append<DocInternalRef>(this,parent,context.token->name);
-  children.get_last<DocInternalRef>()->parse(vDocInternalRef);
+  children.append<DocInternalRef>(this,parent,context.token->name);
+  children.get_last<DocInternalRef>()->parse();
 }
 
 void DocParser::handleAnchor(DocNodeVariant *parent,DocNodeList &children)
 {
+  AUTO_TRACE();
   int tok=tokenizer.lex();
   if (tok!=TK_WHITESPACE)
   {
@@ -962,6 +958,7 @@ void DocParser::handleAnchor(DocNodeVariant *parent,DocNodeList &children)
  */
 void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, DocNodeList &children, QCString &width,QCString &height)
 {
+  AUTO_TRACE();
   auto ns = AutoNodeStack(this,parent);
 
   // parse title
@@ -1032,10 +1029,12 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent,
   tokenizer.setStatePara();
 
   handlePendingStyleCommands(parent,children);
+  AUTO_TRACE_EXIT("width={} height={}",width,height);
 }
 
 void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children)
 {
+  AUTO_TRACE();
   bool inlineImage = false;
   QCString anchorStr;
 
@@ -1047,7 +1046,7 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children)
       if (context.token->name == "{")
       {
         tokenizer.setStateOptions();
-        tok=tokenizer.lex();
+        tokenizer.lex();
         tokenizer.setStatePara();
         StringVector optList=split(context.token->name.str(),",");
         for (const auto &opt : optList)
@@ -1136,9 +1135,9 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children)
     children.append<DocAnchor>(this,parent,anchorStr,true);
   }
   HtmlAttribList attrList;
-  auto vDocImage = children.append<DocImage>(this,parent,attrList,
+  children.append<DocImage>(this,parent,attrList,
                  findAndCopyImage(context.token->name,t),t,"",inlineImage);
-  children.get_last<DocImage>()->parse(vDocImage);
+  children.get_last<DocImage>()->parse();
 }
 
 
@@ -1155,16 +1154,15 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children)
  */
 bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList &children,bool handleWord)
 {
-  DBG(("token %s at %d",DocTokenizer::tokToString(tok),tokenizer.getLineNr()));
+  AUTO_TRACE("token={} handleWord={}",DocTokenizer::tokToString(tok),handleWord);
   if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL ||
       tok==TK_COMMAND_AT || tok==TK_COMMAND_BS || tok==TK_HTMLTAG
      )
   {
-    DBG((" name=%s",qPrint(context.token->name)));
   }
-  DBG(("\n"));
 reparsetoken:
   QCString tokenName = context.token->name;
+  AUTO_TRACE_ADD("tokenName={}",tokenName);
   switch (tok)
   {
     case TK_COMMAND_AT:
@@ -1232,7 +1230,7 @@ reparsetoken:
             if (tok==TK_NEWPARA) goto handlepara;
             else if (tok==TK_WORD || tok==TK_HTMLTAG)
             {
-             DBG(("CMD_EMPHASIS: reparsing command %s\n",qPrint(context.token->name)));
+             AUTO_TRACE_ADD("CMD_EMPHASIS: reparsing");
               goto reparsetoken;
             }
           }
@@ -1246,7 +1244,7 @@ reparsetoken:
             if (tok==TK_NEWPARA) goto handlepara;
             else if (tok==TK_WORD || tok==TK_HTMLTAG)
             {
-             DBG(("CMD_BOLD: reparsing command %s\n",qPrint(context.token->name)));
+             AUTO_TRACE_ADD("CMD_BOLD: reparsing");
               goto reparsetoken;
             }
           }
@@ -1260,7 +1258,7 @@ reparsetoken:
             if (tok==TK_NEWPARA) goto handlepara;
             else if (tok==TK_WORD || tok==TK_HTMLTAG)
             {
-             DBG(("CMD_CODE: reparsing command %s\n",qPrint(context.token->name)));
+             AUTO_TRACE_ADD("CMD_CODE: reparsing");
               goto reparsetoken;
             }
           }
@@ -1325,6 +1323,7 @@ reparsetoken:
           }
           break;
         case CMD_ANCHOR:
+        case CMD_IANCHOR:
           {
             handleAnchor(parent,children);
           }
@@ -1358,10 +1357,10 @@ reparsetoken:
         switch (Mappers::htmlTagMapper->map(tokenName))
         {
           case HTML_DIV:
-            warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <div> tag in heading\n");
+            warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <div> tag in heading");
             break;
           case HTML_PRE:
-            warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <pre> tag in heading\n");
+            warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <pre> tag in heading");
             break;
           case HTML_BOLD:
             if (!context.token->endTag)
@@ -1561,11 +1560,12 @@ handlepara:
 
 void DocParser::handleImg(DocNodeVariant *parent, DocNodeList &children,const HtmlAttribList &tagHtmlAttribs)
 {
+  AUTO_TRACE();
   bool found=FALSE;
-  uint index=0;
+  uint32_t index=0;
   for (const auto &opt : tagHtmlAttribs)
   {
-    //printf("option name=%s value=%s\n",qPrint(opt.name),qPrint(opt.value));
+    AUTO_TRACE_ADD("option name={} value='{}'",opt.name,opt.value);
     if (opt.name=="src" && !opt.value.isEmpty())
     {
       // copy attributes
@@ -1583,7 +1583,7 @@ void DocParser::handleImg(DocNodeVariant *parent, DocNodeList &children,const Ht
   }
   if (!found)
   {
-    warn_doc_error(context.fileName,tokenizer.getLineNr(),"IMG tag does not have a SRC attribute!\n");
+    warn_doc_error(context.fileName,tokenizer.getLineNr(),"IMG tag does not have a SRC attribute!");
   }
 }
 
@@ -1592,11 +1592,12 @@ void DocParser::handleImg(DocNodeVariant *parent, DocNodeList &children,const Ht
 int DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &children,
                                     const QCString &doc)
 {
+  AUTO_TRACE();
   int retval = RetVal_OK;
 
   if (doc.isEmpty()) return retval;
 
-  tokenizer.init(doc.data(),context.fileName,context.markdownSupport);
+  tokenizer.init(doc.data(),context.fileName,context.markdownSupport,context.insideHtmlLink);
 
   // first parse any number of paragraphs
   bool isFirst=TRUE;
@@ -1607,10 +1608,10 @@ int DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &ch
   }
   do
   {
-    auto vDocPara = children.append<DocPara>(this,parent);
+    children.append<DocPara>(this,parent);
     DocPara *par  = children.get_last<DocPara>();
     if (isFirst) { par->markFirst(); isFirst=FALSE; }
-    retval=par->parse(vDocPara);
+    retval=par->parse();
     if (!par->isEmpty())
     {
       if (lastPar) lastPar->markLast(FALSE);
@@ -1623,9 +1624,7 @@ int DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &ch
   } while (retval==TK_NEWPARA);
   if (lastPar) lastPar->markLast();
 
-  //printf("internalValidateParsingDoc: %p: isFirst=%d isLast=%d\n",
-  //   lastPar,lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
-
+  AUTO_TRACE_EXIT("isFirst={} isLast={}",lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
   return retval;
 }
 
@@ -1633,6 +1632,7 @@ int DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &ch
 
 void DocParser::readTextFileByName(const QCString &file,QCString &text)
 {
+  AUTO_TRACE("file={} text={}",file,text);
   if (Portable::isAbsolutePath(file))
   {
     FileInfo fi(file.str());
@@ -1677,9 +1677,9 @@ void DocParser::readTextFileByName(const QCString &file,QCString &text)
 
 //---------------------------------------------------------------------------
 
-static QCString extractCopyDocId(const char *data, uint &j, uint len)
+static QCString extractCopyDocId(const char *data, uint32_t &j, size_t len)
 {
-  uint s=j;
+  uint32_t s=j;
   int round=0;
   bool insideDQuote=FALSE;
   bool insideSQuote=FALSE;
@@ -1725,7 +1725,7 @@ static QCString extractCopyDocId(const char *data, uint &j, uint len)
   {
     j+=9;
   }
-  uint e=j;
+  uint32_t e=j;
   if (j>0 && data[j-1]=='.') { e--; } // do not include punctuation added by Definition::_setBriefDescription()
   QCString id(data+s,e-s);
   //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]);
@@ -1740,9 +1740,9 @@ static QCString extractCopyDocId(const char *data, uint &j, uint len)
    do if ((i+sizeof(str)<len) && qstrncmp(data+i+1,str,sizeof(str)-1)==0) \
    { j=i+sizeof(str); action; } while(0)
 
-static uint isCopyBriefOrDetailsCmd(const char *data, uint i,uint len,bool &brief)
+static uint32_t isCopyBriefOrDetailsCmd(const char *data, uint32_t i,size_t len,bool &brief)
 {
-  uint j=0;
+  uint32_t j=0;
   if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
   {
     CHECK_FOR_COMMAND("copybrief",brief=TRUE);    // @copybrief or \copybrief
@@ -1751,9 +1751,9 @@ static uint isCopyBriefOrDetailsCmd(const char *data, uint i,uint len,bool &brie
   return j;
 }
 
-static uint isVerbatimSection(const char *data,uint i,uint len,QCString &endMarker)
+static uint32_t isVerbatimSection(const char *data,uint32_t i,size_t len,QCString &endMarker)
 {
-  uint j=0;
+  uint32_t j=0;
   if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
   {
     CHECK_FOR_COMMAND("dot",endMarker="enddot");
@@ -1775,7 +1775,7 @@ static uint isVerbatimSection(const char *data,uint i,uint len,QCString &endMark
   return j;
 }
 
-static uint skipToEndMarker(const char *data,uint i,uint len,const QCString &endMarker)
+static uint32_t skipToEndMarker(const char *data,uint32_t i,size_t len,const QCString &endMarker)
 {
   while (i<len)
   {
@@ -1790,15 +1790,15 @@ static uint skipToEndMarker(const char *data,uint i,uint len,const QCString &end
     i++;
   }
   // oops no endmarker found...
-  return i<len ? i+1 : len;
+  return i<len ? i+1 : static_cast<uint32_t>(len);
 }
 
 
-QCString DocParser::processCopyDoc(const char *data,uint &len)
+QCString DocParser::processCopyDoc(const char *data,size_t &len)
 {
-  //printf("processCopyDoc start '%s'\n",data);
+  AUTO_TRACE("data={} len={}",Trace::trunc(data),len);
   GrowBuf buf;
-  uint i=0;
+  uint32_t i=0;
   int lineNr = tokenizer.getLineNr();
   while (i<len)
   {
@@ -1806,7 +1806,7 @@ QCString DocParser::processCopyDoc(const char *data,uint &len)
     if (c=='@' || c=='\\') // look for a command
     {
       bool isBrief=TRUE;
-      uint j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
+      uint32_t j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
       if (j>0)
       {
         // skip whitespace
@@ -1824,28 +1824,37 @@ QCString DocParser::processCopyDoc(const char *data,uint &len)
           {
             QCString orgFileName = context.fileName;
             context.copyStack.push_back(def);
+            auto addDocs = [&](const QCString &file_,int line_,const QCString &doc_)
+            {
+              buf.addStr(" \\ilinebr\\ifile \""+file_+"\" ");
+              buf.addStr("\\iline "+QCString().setNum(line_)+" ");
+              size_t len_ = doc_.length();
+              buf.addStr(processCopyDoc(doc_.data(),len_));
+            };
             if (isBrief)
             {
-              buf.addStr("\\ifile \""+QCString(def->briefFile())+"\" ");
-              buf.addStr("\\iline "+QCString().setNum(def->briefLine())+" ");
-              uint l=static_cast<uint>(brief.length());
-              buf.addStr(processCopyDoc(brief.data(),l));
+              addDocs(def->briefFile(),def->briefLine(),brief);
             }
             else
             {
-              buf.addStr("\\ifile \""+QCString(def->docFile())+"\" ");
-              buf.addStr("\\iline "+QCString().setNum(def->docLine())+" ");
-              uint l=static_cast<uint>(doc.length());
-              buf.addStr(processCopyDoc(doc.data(),l));
+              addDocs(def->docFile(),def->docLine(),doc);
+              if (def->definitionType()==Definition::TypeMember)
+              {
+                const MemberDef *md = toMemberDef(def);
+                const ArgumentList &docArgList = md->templateMaster() ?
+                    md->templateMaster()->argumentList() :
+                    md->argumentList();
+                buf.addStr(inlineArgListToDoc(docArgList));
+              }
             }
             context.copyStack.pop_back();
-            buf.addStr("\\ifile \""+context.fileName+"\" ");
+            buf.addStr(" \\ilinebr\\ifile \""+context.fileName+"\" ");
             buf.addStr("\\iline "+QCString().setNum(lineNr)+" ");
           }
           else
           {
             warn_doc_error(context.fileName,tokenizer.getLineNr(),
-                "Found recursive @copy%s or @copydoc relation for argument '%s'.\n",
+                "Found recursive @copy%s or @copydoc relation for argument '%s'.",
                  isBrief?"brief":"details",qPrint(id));
           }
         }
@@ -1861,10 +1870,10 @@ QCString DocParser::processCopyDoc(const char *data,uint &len)
       else
       {
         QCString endMarker;
-        uint k = isVerbatimSection(data,i,len,endMarker);
+        uint32_t k = isVerbatimSection(data,i,len,endMarker);
         if (k>0)
         {
-          uint orgPos = i;
+          uint32_t orgPos = i;
           i=skipToEndMarker(data,k,len,endMarker);
           buf.addStr(data+orgPos,i-orgPos);
           // TODO: adjust lineNr
@@ -1883,8 +1892,9 @@ QCString DocParser::processCopyDoc(const char *data,uint &len)
       lineNr += (c=='\n') ? 1 : 0;
     }
   }
-  len = static_cast<uint>(buf.getPos());
+  len = static_cast<uint32_t>(buf.getPos());
   buf.addChar(0);
+  AUTO_TRACE_EXIT("result={}",Trace::trunc(buf.get()));
   return buf.get();
 }
 
@@ -1907,11 +1917,9 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
   //                                     input);
   //printf("========== validating %s at line %d\n",qPrint(fileName),startLine);
   //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",qPrint(input));
-  //parser->context.token = new TokenInfo;
 
-  // store parser state so we can re-enter this function if needed
-  //bool fortranOpt = Config_getBool(OPTIMIZE_FOR_FORTRAN);
-  parser->pushContext();
+  // set initial token
+  parser->context.token = parser->tokenizer.resetToken();
 
   if (ctx && ctx!=Doxygen::globalScope &&
       (ctx->definitionType()==Definition::TypeClass ||
@@ -1919,7 +1927,7 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
       )
      )
   {
-    parser->context.context = ctx->name();
+    parser->context.context = ctx->qualifiedName();
   }
   else if (ctx && ctx->definitionType()==Definition::TypePage)
   {
@@ -1942,12 +1950,12 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
     if (md)
     {
       parser->context.searchUrl=md->getOutputFileBase();
-      parser->searchData.setCurrentDoc(md,md->anchor(),false);
+      Doxygen::searchIndex->setCurrentDoc(md,md->anchor(),false);
     }
     else if (ctx)
     {
       parser->context.searchUrl=ctx->getOutputFileBase();
-      parser->searchData.setCurrentDoc(ctx,ctx->anchor(),false);
+      Doxygen::searchIndex->setCurrentDoc(ctx,ctx->anchor(),false);
     }
   }
   else
@@ -1980,18 +1988,19 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
 
   //printf("Starting comment block at %s:%d\n",qPrint(parser->context.fileName),startLine);
   parser->tokenizer.setLineNr(startLine);
-  uint ioLen = static_cast<uint>(input.length());
+  size_t ioLen = input.length();
   QCString inpStr = parser->processCopyDoc(input.data(),ioLen);
   if (inpStr.isEmpty() || inpStr.at(inpStr.length()-1)!='\n')
   {
     inpStr+='\n';
   }
   //printf("processCopyDoc(in='%s' out='%s')\n",input,qPrint(inpStr));
-  parser->tokenizer.init(inpStr.data(),parser->context.fileName,markdownSupport);
+  parser->tokenizer.init(inpStr.data(),parser->context.fileName,
+                         parser->context.markdownSupport,parser->context.insideHtmlLink);
 
   // build abstract syntax tree
   auto ast = std::make_unique<DocNodeAST>(DocRoot(parser,md!=0,singleLine));
-  std::get<DocRoot>(ast->root).parse(&ast->root);
+  std::get<DocRoot>(ast->root).parse();
 
   if (Debug::isFlagSet(Debug::PrintTree))
   {
@@ -1999,16 +2008,14 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
     std::visit(PrintDocVisitor{},ast->root);
   }
 
-  parser->checkUnOrMultipleDocumentedParams();
+  if (md && md->isFunction())
+  {
+    parser->checkUnOrMultipleDocumentedParams();
+  }
   if (parser->context.memberDef) parser->context.memberDef->detectUndocumentedParams(parser->context.hasParamCommand,parser->context.hasReturnCommand);
 
-  // TODO: These should be called at the end of the program.
-  //parser->tokenizer.cleanup();
-  //Mappers::cmdMapper->freeInstance();
-  //Mappers::htmlTagMapper->freeInstance();
-
-  // restore original parser state
-  parser->popContext();
+  // reset token
+  parser->tokenizer.resetToken();
 
   //printf(">>>>>> end validatingParseDoc(%s,%s)\n",ctx?qPrint(ctx->name()):"<none>",
   //                                     md?qPrint(md->name()):"<none>");
@@ -2022,8 +2029,8 @@ IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
   assert(parser!=0);
   if (parser==0) return 0;
 
-  // store parser state so we can re-enter this function if needed
-  parser->pushContext();
+  // set initial token
+  parser->context.token = parser->tokenizer.resetToken();
 
   //printf("------------ input ---------\n%s\n"
   //       "------------ end input -----\n",input);
@@ -2048,6 +2055,7 @@ IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
   parser->context.retvalsFound.clear();
   parser->context.paramsFound.clear();
   parser->context.searchUrl="";
+  parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
 
 
   auto ast = std::make_unique<DocNodeAST>(DocText(parser));
@@ -2055,10 +2063,11 @@ IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
   if (!input.isEmpty())
   {
     parser->tokenizer.setLineNr(1);
-    parser->tokenizer.init(input.data(),parser->context.fileName,Config_getBool(MARKDOWN_SUPPORT));
+    parser->tokenizer.init(input.data(),parser->context.fileName,
+                           parser->context.markdownSupport,parser->context.insideHtmlLink);
 
     // build abstract syntax tree
-    std::get<DocText>(ast->root).parse(&ast->root);
+    std::get<DocText>(ast->root).parse();
 
     if (Debug::isFlagSet(Debug::PrintTree))
     {
@@ -2067,8 +2076,6 @@ IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
     }
   }
 
-  // restore original parser state
-  parser->popContext();
   return ast;
 }