Imported Upstream version 1.9.8
[platform/upstream/doxygen.git] / src / doxygen.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2015 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cerrno>
19 #include <sys/stat.h>
20
21 #include <algorithm>
22 #include <unordered_map>
23 #include <memory>
24 #include <cinttypes>
25 #include <chrono>
26 #include <clocale>
27 #include <locale>
28
29 #include "version.h"
30 #include "doxygen.h"
31 #include "scanner.h"
32 #include "entry.h"
33 #include "index.h"
34 #include "indexlist.h"
35 #include "message.h"
36 #include "config.h"
37 #include "util.h"
38 #include "pre.h"
39 #include "tagreader.h"
40 #include "dot.h"
41 #include "msc.h"
42 #include "docparser.h"
43 #include "dirdef.h"
44 #include "outputlist.h"
45 #include "declinfo.h"
46 #include "htmlgen.h"
47 #include "latexgen.h"
48 #include "mangen.h"
49 #include "language.h"
50 #include "debug.h"
51 #include "htmlhelp.h"
52 #include "qhp.h"
53 #include "sitemap.h"
54 #include "ftvhelp.h"
55 #include "defargs.h"
56 #include "rtfgen.h"
57 #include "sqlite3gen.h"
58 #include "xmlgen.h"
59 #include "docbookgen.h"
60 #include "defgen.h"
61 #include "perlmodgen.h"
62 #include "reflist.h"
63 #include "pagedef.h"
64 #include "bufstr.h"
65 #include "commentcnv.h"
66 #include "cmdmapper.h"
67 #include "searchindex.h"
68 #include "searchindex_js.h"
69 #include "parserintf.h"
70 #include "htags.h"
71 #include "pycode.h"
72 #include "pyscanner.h"
73 #include "fortrancode.h"
74 #include "fortranscanner.h"
75 #include "xmlcode.h"
76 #include "sqlcode.h"
77 #include "lexcode.h"
78 #include "lexscanner.h"
79 #include "code.h"
80 #include "portable.h"
81 #include "vhdljjparser.h"
82 #include "vhdldocgen.h"
83 #include "vhdlcode.h"
84 #include "eclipsehelp.h"
85 #include "cite.h"
86 #include "markdown.h"
87 #include "arguments.h"
88 #include "memberlist.h"
89 #include "layout.h"
90 #include "groupdef.h"
91 #include "classlist.h"
92 #include "namespacedef.h"
93 #include "filename.h"
94 #include "membername.h"
95 #include "membergroup.h"
96 #include "docsets.h"
97 #include "formula.h"
98 #include "settings.h"
99 #include "fileparser.h"
100 #include "emoji.h"
101 #include "plantuml.h"
102 #include "stlsupport.h"
103 #include "threadpool.h"
104 #include "clangparser.h"
105 #include "symbolresolver.h"
106 #include "regex.h"
107 #include "aliases.h"
108 #include "fileinfo.h"
109 #include "dir.h"
110 #include "conceptdef.h"
111 #include "trace.h"
112 #include "moduledef.h"
113
114 #include <sqlite3.h>
115
116 #if USE_LIBCLANG
117 #include <clang/Basic/Version.h>
118 #endif
119
120 // provided by the generated file resources.cpp
121 extern void initResources();
122
123 #if !defined(_WIN32) || defined(__CYGWIN__)
124 #include <signal.h>
125 #define HAS_SIGNALS
126 #endif
127
128 // globally accessible variables
129 ClassLinkedMap       *Doxygen::classLinkedMap = 0;
130 ClassLinkedMap       *Doxygen::hiddenClassLinkedMap = 0;
131 ConceptLinkedMap     *Doxygen::conceptLinkedMap = 0;
132 NamespaceLinkedMap   *Doxygen::namespaceLinkedMap = 0;
133 MemberNameLinkedMap  *Doxygen::memberNameLinkedMap = 0;
134 MemberNameLinkedMap  *Doxygen::functionNameLinkedMap = 0;
135 FileNameLinkedMap    *Doxygen::inputNameLinkedMap = 0;
136 GroupLinkedMap       *Doxygen::groupLinkedMap = 0;
137 PageLinkedMap        *Doxygen::pageLinkedMap = 0;
138 PageLinkedMap        *Doxygen::exampleLinkedMap = 0;
139 StringSet             Doxygen::inputPaths;
140 FileNameLinkedMap    *Doxygen::includeNameLinkedMap = 0;     // include names
141 FileNameLinkedMap    *Doxygen::exampleNameLinkedMap = 0;     // examples
142 FileNameLinkedMap    *Doxygen::imageNameLinkedMap = 0;       // images
143 FileNameLinkedMap    *Doxygen::dotFileNameLinkedMap = 0;     // dot files
144 FileNameLinkedMap    *Doxygen::mscFileNameLinkedMap = 0;     // msc files
145 FileNameLinkedMap    *Doxygen::diaFileNameLinkedMap = 0;     // dia files
146 StringUnorderedMap    Doxygen::namespaceAliasMap;            // all namespace aliases
147 StringMap             Doxygen::tagDestinationMap;            // all tag locations
148 StringUnorderedSet    Doxygen::expandAsDefinedSet;           // all macros that should be expanded
149 MemberGroupInfoMap    Doxygen::memberGroupInfoMap;           // dictionary of the member groups heading
150 std::unique_ptr<PageDef> Doxygen::mainPage;
151 std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
152 NamespaceDefMutable  *Doxygen::globalScope;
153 bool                  Doxygen::parseSourcesNeeded = FALSE;
154 std::unique_ptr<SearchIndexIntf> Doxygen::searchIndex;
155 SymbolMap<Definition>*Doxygen::symbolMap;
156 ClangUsrMap          *Doxygen::clangUsrMap = 0;
157 Cache<std::string,LookupInfo> *Doxygen::typeLookupCache;
158 Cache<std::string,LookupInfo> *Doxygen::symbolLookupCache;
159 DirLinkedMap         *Doxygen::dirLinkedMap;
160 DirRelationLinkedMap  Doxygen::dirRelations;
161 ParserManager        *Doxygen::parserManager = 0;
162 QCString              Doxygen::htmlFileExtension;
163 bool                  Doxygen::suppressDocWarnings = FALSE;
164 QCString              Doxygen::filterDBFileName;
165 IndexList            *Doxygen::indexList;
166 QCString              Doxygen::spaces;
167 bool                  Doxygen::generatingXmlOutput = FALSE;
168 DefinesPerFileList    Doxygen::macroDefinitions;
169 bool                  Doxygen::clangAssistedParsing = FALSE;
170 QCString              Doxygen::verifiedDotPath;
171 volatile bool         Doxygen::terminating = false;
172 InputFileEncodingList Doxygen::inputFileEncodingList;
173 std::mutex            Doxygen::countFlowKeywordsMutex;
174 std::mutex            Doxygen::addExampleMutex;
175 StaticInitMap         Doxygen::staticInitMap;
176
177 // locally accessible globals
178 static std::multimap< std::string, const Entry* > g_classEntries;
179 static StringVector     g_inputFiles;
180 static StringSet        g_compoundKeywords;        // keywords recognised as compounds
181 static OutputList      *g_outputList = 0;          // list of output generating objects
182 static StringSet        g_usingDeclarations; // used classes
183 static bool             g_successfulRun = FALSE;
184 static bool             g_dumpSymbolMap = FALSE;
185
186 void clearAll()
187 {
188   g_inputFiles.clear();
189   //g_excludeNameDict.clear();
190   //delete g_outputList; g_outputList=0;
191
192   Doxygen::classLinkedMap->clear();
193   Doxygen::hiddenClassLinkedMap->clear();
194   Doxygen::conceptLinkedMap->clear();
195   Doxygen::namespaceLinkedMap->clear();
196   Doxygen::pageLinkedMap->clear();
197   Doxygen::exampleLinkedMap->clear();
198   Doxygen::inputNameLinkedMap->clear();
199   Doxygen::includeNameLinkedMap->clear();
200   Doxygen::exampleNameLinkedMap->clear();
201   Doxygen::imageNameLinkedMap->clear();
202   Doxygen::dotFileNameLinkedMap->clear();
203   Doxygen::mscFileNameLinkedMap->clear();
204   Doxygen::diaFileNameLinkedMap->clear();
205   Doxygen::tagDestinationMap.clear();
206   SectionManager::instance().clear();
207   CitationManager::instance().clear();
208   Doxygen::mainPage.reset();
209   FormulaManager::instance().clear();
210 }
211
212 class Statistics
213 {
214   public:
215     Statistics() {}
216     void begin(const char *name)
217     {
218       msg("%s", name);
219       stats.emplace_back(name,0);
220       startTime = std::chrono::steady_clock::now();
221     }
222     void end()
223     {
224       std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
225       stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
226                                 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
227       warn_flush();
228     }
229     void print()
230     {
231       bool restore=FALSE;
232       if (Debug::isFlagSet(Debug::Time))
233       {
234         Debug::clearFlag(Debug::Time);
235         restore=TRUE;
236       }
237       msg("----------------------\n");
238       for (const auto &s : stats)
239       {
240         msg("Spent %.6f seconds in %s",s.elapsed,s.name);
241       }
242       if (restore) Debug::setFlag(Debug::Time);
243     }
244   private:
245     struct stat
246     {
247       const char *name;
248       double elapsed;
249       //stat() : name(NULL),elapsed(0) {}
250       stat(const char *n, double el) : name(n),elapsed(el) {}
251     };
252     std::vector<stat> stats;
253     std::chrono::steady_clock::time_point startTime;
254 } g_s;
255
256
257 static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
258                    const ArgumentList *al,bool over_load,uint64_t spec);
259 static void findMember(const Entry *root,
260                        const QCString &relates,
261                        const QCString &type,
262                        const QCString &args,
263                        QCString funcDecl,
264                        bool overloaded,
265                        bool isFunc
266                       );
267
268 enum FindBaseClassRelation_Mode
269 {
270   TemplateInstances,
271   DocumentedOnly,
272   Undocumented
273 };
274
275
276 static bool findClassRelation(
277                            const Entry *root,
278                            Definition *context,
279                            ClassDefMutable *cd,
280                            const BaseInfo *bi,
281                            const TemplateNameMap &templateNames,
282                            /*bool insertUndocumented*/
283                            FindBaseClassRelation_Mode mode,
284                            bool isArtificial
285                           );
286
287 //----------------------------------------------------------------------------
288
289 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
290                                               FileDef *fileScope,const TagInfo *tagInfo);
291
292 static void addPageToContext(PageDef *pd,Entry *root)
293 {
294   if (root->parent()) // add the page to it's scope
295   {
296     QCString scope = root->parent()->name;
297     if (root->parent()->section==Entry::PACKAGEDOC_SEC)
298     {
299       scope=substitute(scope,".","::");
300     }
301     scope = stripAnonymousNamespaceScope(scope);
302     scope+="::"+pd->name();
303     Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
304     if (d)
305     {
306       pd->setPageScope(d);
307     }
308   }
309 }
310
311 static void addRelatedPage(Entry *root)
312 {
313   GroupDef *gd=0;
314   for (const Grouping &g : root->groups)
315   {
316     if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
317   }
318   //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
319   QCString doc=root->doc+root->inbodyDocs;
320
321   PageDef *pd = addRelatedPage(root->name,root->args,doc,
322       root->docFile,
323       root->docLine,
324       root->startLine,
325       root->sli,
326       gd,root->tagInfo(),
327       FALSE,
328       root->lang
329      );
330   if (pd)
331   {
332     pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
333     pd->addSectionsToDefinition(root->anchors);
334     pd->setLocalToc(root->localToc);
335     addPageToContext(pd,root);
336   }
337 }
338
339 static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
340 {
341   if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
342         ((!includeExternal && root->tagInfo()==0) ||
343          ( includeExternal && root->tagInfo()!=0))
344      )
345   {
346     AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
347     if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
348         (root->groupDocType!=Entry::GROUPDOC_NORMAL &&  additional))
349     {
350       GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
351       AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
352
353       if (gd)
354       {
355         if ( !gd->hasGroupTitle() )
356         {
357           gd->setGroupTitle( root->type );
358         }
359         else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
360         {
361           warn( root->fileName,root->startLine,
362               "group %s: ignoring title \"%s\" that does not match old title \"%s\"",
363               qPrint(root->name), qPrint(root->type), qPrint(gd->groupTitle()) );
364         }
365         gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
366         gd->setDocumentation( root->doc, root->docFile, root->docLine );
367         gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
368         gd->addSectionsToDefinition(root->anchors);
369         gd->setRefItems(root->sli);
370         gd->setLanguage(root->lang);
371         if (root->groupDocType==Entry::GROUPDOC_NORMAL)
372         {
373           gd->enableGroupGraph(root->groupGraph);
374         }
375       }
376       else
377       {
378         if (root->tagInfo())
379         {
380           gd = Doxygen::groupLinkedMap->add(root->name,
381                std::unique_ptr<GroupDef>(
382                   createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
383           gd->setReference(root->tagInfo()->tagName);
384         }
385         else
386         {
387           gd = Doxygen::groupLinkedMap->add(root->name,
388                std::unique_ptr<GroupDef>(
389                   createGroupDef(root->fileName,root->startLine,root->name,root->type)));
390         }
391         gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
392         // allow empty docs for group
393         gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
394         gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
395         gd->addSectionsToDefinition(root->anchors);
396         gd->setRefItems(root->sli);
397         gd->setLanguage(root->lang);
398         if (root->groupDocType==Entry::GROUPDOC_NORMAL)
399         {
400           gd->enableGroupGraph(root->groupGraph);
401         }
402       }
403     }
404   }
405   for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
406 }
407
408 static void buildGroupList(const Entry *root)
409 {
410   // --- first process only local groups
411   // first process the @defgroups blocks
412   buildGroupListFiltered(root,FALSE,FALSE);
413   // then process the @addtogroup, @weakgroup blocks
414   buildGroupListFiltered(root,TRUE,FALSE);
415
416   // --- then also process external groups
417   // first process the @defgroups blocks
418   buildGroupListFiltered(root,FALSE,TRUE);
419   // then process the @addtogroup, @weakgroup blocks
420   buildGroupListFiltered(root,TRUE,TRUE);
421 }
422
423 static void findGroupScope(const Entry *root)
424 {
425   if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
426       root->parent() && !root->parent()->name.isEmpty())
427   {
428     GroupDef *gd;
429     if ((gd=Doxygen::groupLinkedMap->find(root->name)))
430     {
431       QCString scope = root->parent()->name;
432       if (root->parent()->section==Entry::PACKAGEDOC_SEC)
433       {
434         scope=substitute(scope,".","::");
435       }
436       scope = stripAnonymousNamespaceScope(scope);
437       scope+="::"+gd->name();
438       Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
439       if (d)
440       {
441         gd->setGroupScope(d);
442       }
443     }
444   }
445   for (const auto &e : root->children()) findGroupScope(e.get());
446 }
447
448 static void organizeSubGroupsFiltered(const Entry *root,bool additional)
449 {
450   if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty())
451   {
452     AUTO_TRACE("additional={}",additional);
453     if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
454         (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
455     {
456       GroupDef *gd;
457       if ((gd=Doxygen::groupLinkedMap->find(root->name)))
458       {
459         AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
460         addGroupToGroups(root,gd);
461       }
462     }
463   }
464   for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
465 }
466
467 static void organizeSubGroups(const Entry *root)
468 {
469   //printf("Defining groups\n");
470   // first process the @defgroups blocks
471   organizeSubGroupsFiltered(root,FALSE);
472   //printf("Additional groups\n");
473   // then process the @addtogroup, @weakgroup blocks
474   organizeSubGroupsFiltered(root,TRUE);
475 }
476
477 //----------------------------------------------------------------------
478
479 static void buildFileList(const Entry *root)
480 {
481   if (((root->section==Entry::FILEDOC_SEC) ||
482         ((root->section & Entry::FILE_MASK) && Config_getBool(EXTRACT_ALL))) &&
483       !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
484      )
485   {
486     bool ambig;
487     FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,root->name,ambig);
488     if (!fd || ambig)
489     {
490       bool save_ambig = ambig;
491       // use the directory of the file to see if the described file is in the same
492       // directory as the describing file.
493       QCString fn = root->fileName;
494       int newIndex=fn.findRev('/');
495       if (newIndex<0)
496       {
497         fn = root->name;
498       }
499       else
500       {
501         fn = fn.left(newIndex)+"/"+root->name;
502       }
503       fd=findFileDef(Doxygen::inputNameLinkedMap,fn,ambig);
504       if (!fd) ambig = save_ambig;
505     }
506     //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
507     if (fd && !ambig)
508     {
509       //printf("Adding documentation!\n");
510       // using FALSE in setDocumentation is small hack to make sure a file
511       // is documented even if a \file command is used without further
512       // documentation
513       fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
514       fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
515       fd->addSectionsToDefinition(root->anchors);
516       fd->setRefItems(root->sli);
517       fd->enableIncludeGraph(root->includeGraph);
518       fd->enableIncludedByGraph(root->includedByGraph);
519       for (const Grouping &g : root->groups)
520       {
521         GroupDef *gd=0;
522         if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
523         {
524           if (!gd->containsFile(fd))
525           {
526             gd->addFile(fd);
527             fd->makePartOfGroup(gd);
528             //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
529           }
530         }
531       }
532     }
533     else
534     {
535       QCString text(4096);
536       text.sprintf("the name '%s' supplied as "
537           "the argument in the \\file statement ",
538           qPrint(root->name));
539       if (ambig) // name is ambiguous
540       {
541         text+="matches the following input files:\n";
542         text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->name);
543         text+="Please use a more specific name by "
544           "including a (larger) part of the path!";
545       }
546       else // name is not an input file
547       {
548         text+="is not an input file";
549       }
550       warn(root->fileName,root->startLine,"%s", qPrint(text));
551     }
552   }
553   for (const auto &e : root->children()) buildFileList(e.get());
554 }
555
556 template<class DefMutable>
557 static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
558 {
559   if (
560       (!root->doc.stripWhiteSpace().isEmpty() ||
561        !root->brief.stripWhiteSpace().isEmpty() ||
562        Config_getBool(EXTRACT_ALL)
563       ) && root->protection!=Protection::Private
564      )
565   {
566     //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
567
568     bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
569     QCString includeFile = root->includeFile;
570     if (!includeFile.isEmpty() && includeFile.at(0)=='"')
571     {
572       local = TRUE;
573       includeFile=includeFile.mid(1,includeFile.length()-2);
574     }
575     else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
576     {
577       local = FALSE;
578       includeFile=includeFile.mid(1,includeFile.length()-2);
579     }
580
581     bool ambig;
582     FileDef *fd=0;
583     // see if we need to include a verbatim copy of the header file
584     //printf("root->includeFile=%s\n",qPrint(root->includeFile));
585     if (!includeFile.isEmpty() &&
586         (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==0
587        )
588     { // explicit request
589       QCString text;
590       text.sprintf("the name '%s' supplied as "
591                   "the argument of the \\class, \\struct, \\union, or \\include command ",
592                   qPrint(includeFile)
593                  );
594       if (ambig) // name is ambiguous
595       {
596         text+="matches the following input files:\n";
597         text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->includeFile);
598         text+="Please use a more specific name by "
599             "including a (larger) part of the path!";
600       }
601       else // name is not an input file
602       {
603         text+="is not an input file";
604       }
605       warn(root->fileName,root->startLine, "%s", qPrint(text));
606     }
607     else if (includeFile.isEmpty() && ifd &&
608         // see if the file extension makes sense
609         guessSection(ifd->name())==Entry::HEADER_SEC)
610     { // implicit assumption
611       fd=ifd;
612     }
613
614     // if a file is found, we mark it as a source file.
615     if (fd)
616     {
617       QCString iName = !root->includeName.isEmpty() ?
618                        root->includeName : includeFile;
619       if (!iName.isEmpty()) // user specified include file
620       {
621         if (iName.at(0)=='<') local=FALSE; // explicit override
622         else if (iName.at(0)=='"') local=TRUE;
623         if (iName.at(0)=='"' || iName.at(0)=='<')
624         {
625           iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
626         }
627         if (iName.isEmpty())
628         {
629           iName=fd->name();
630         }
631       }
632       else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
633       {
634         iName=stripFromIncludePath(fd->absFilePath());
635       }
636       else // use name of the file containing the class definition
637       {
638         iName=fd->name();
639       }
640       if (fd->generateSourceFile()) // generate code for header
641       {
642         cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
643       }
644       else // put #include in the class documentation without link
645       {
646         cd->setIncludeFile(0,iName,local,TRUE);
647       }
648     }
649   }
650 }
651
652
653 QCString stripTemplateSpecifiers(const QCString &s)
654 {
655   int l = s.length();
656   int count=0;
657   int round=0;
658   QCString result;
659   for (int i=0;i<l;i++)
660   {
661     char c=s.at(i);
662     if      (c=='(') round++;
663     else if (c==')' && round>0) round--;
664     else if (c=='<' && round==0) count++;
665     if (count==0)
666     {
667       result+=c;
668     }
669     if (c=='>' && round==0 && count>0) count--;
670   }
671   //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
672   return result;
673 }
674
675 /*! returns the Definition object belonging to the first \a level levels of
676  *  full qualified name \a name. Creates an artificial scope if the scope is
677  *  not found and set the parent/child scope relation if the scope is found.
678  */
679 [[maybe_unused]]
680 static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
681 {
682   QCString name = stripTemplateSpecifiers(name_);
683   int level = name.contains("::");
684   //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
685   int i=0;
686   int p=0,l;
687   Definition *prevScope=Doxygen::globalScope;
688   QCString fullScope;
689   while (i<level)
690   {
691     int idx=getScopeFragment(name,p,&l);
692     if (idx==-1) return prevScope;
693     QCString nsName = name.mid(idx,l);
694     if (nsName.isEmpty()) return prevScope;
695     if (!fullScope.isEmpty()) fullScope+="::";
696     fullScope+=nsName;
697     NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
698     DefinitionMutable *innerScope = toDefinitionMutable(nd);
699     ClassDef *cd=0;
700     if (nd==0) cd = getClass(fullScope);
701     if (nd==0 && cd) // scope is a class
702     {
703       innerScope = toDefinitionMutable(cd);
704     }
705     else if (nd==0 && cd==0 && fullScope.find('<')==-1) // scope is not known and could be a namespace!
706     {
707       // introduce bogus namespace
708       //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
709       NamespaceDefMutable *newNd=
710         toNamespaceDefMutable(
711           Doxygen::namespaceLinkedMap->add(fullScope,
712             createNamespaceDef(
713               "[generated]",1,1,fullScope,
714               tagInfo?tagInfo->tagName:QCString(),
715               tagInfo?tagInfo->fileName:QCString())));
716       if (newNd)
717       {
718         newNd->setLanguage(lang);
719         newNd->setArtificial(TRUE);
720         // add namespace to the list
721         innerScope = newNd;
722       }
723     }
724     else // scope is a namespace
725     {
726     }
727     if (innerScope)
728     {
729       // make the parent/child scope relation
730       DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
731       if (prevScopeMutable)
732       {
733         prevScopeMutable->addInnerCompound(toDefinition(innerScope));
734       }
735       innerScope->setOuterScope(prevScope);
736     }
737     else // current scope is a class, so return only the namespace part...
738     {
739       return prevScope;
740     }
741     // proceed to the next scope fragment
742     p=idx+l+2;
743     prevScope=toDefinition(innerScope);
744     i++;
745   }
746   return prevScope;
747 }
748
749 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
750                                               FileDef *fileScope,const TagInfo *tagInfo)
751 {
752   //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
753   Definition *resultScope=toDefinition(startScope);
754   if (resultScope==0) resultScope=Doxygen::globalScope;
755   QCString scope=stripTemplateSpecifiersFromScope(n,FALSE);
756   int l1=0,i1;
757   i1=getScopeFragment(scope,0,&l1);
758   if (i1==-1)
759   {
760     //printf(">no fragments!\n");
761     return resultScope;
762   }
763   int p=i1+l1,l2=0,i2;
764   while ((i2=getScopeFragment(scope,p,&l2))!=-1)
765   {
766     QCString nestedNameSpecifier = scope.mid(i1,l1);
767     Definition *orgScope = resultScope;
768     //printf("  nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
769     resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
770     //printf("  resultScope=%p\n",resultScope);
771     if (resultScope==0)
772     {
773       if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
774         // also search for used namespaces
775       {
776         for (const auto &nd : fileScope->getUsedNamespaces())
777         {
778           NamespaceDef *mnd = toNamespaceDefMutable(nd);
779           if (mnd)
780           {
781             resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
782             if (resultScope!=0) break;
783           }
784         }
785         if (resultScope)
786         {
787           // for a nested class A::I in used namespace N, we get
788           // N::A::I while looking for A, so we should compare
789           // resultScope->name() against scope.left(i2+l2)
790           //printf("  -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
791           if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
792           {
793             break;
794           }
795           goto nextFragment;
796         }
797       }
798
799       // also search for used classes. Complication: we haven't been able
800       // to put them in the right scope yet, because we are still resolving
801       // the scope relations!
802       // Therefore loop through all used classes and see if there is a right
803       // scope match between the used class and nestedNameSpecifier.
804       for (const auto &usedName : g_usingDeclarations)
805       {
806         //printf("Checking using class %s\n",ui.currentKey());
807         if (rightScopeMatch(usedName.c_str(),nestedNameSpecifier))
808         {
809           // ui.currentKey() is the fully qualified name of nestedNameSpecifier
810           // so use this instead.
811           QCString fqn = QCString(usedName) + scope.right(scope.length()-p);
812           resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),0);
813           //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
814           if (resultScope)
815           {
816             //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
817             return resultScope;
818           }
819         }
820       }
821
822       //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
823       return 0;
824     }
825  nextFragment:
826     i1=i2;
827     l1=l2;
828     p=i2+l2;
829   }
830   //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
831   return resultScope;
832 }
833
834 std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
835                   const QCString &name,
836                   const ArgumentLists &tArgLists)
837 {
838   // for each scope fragment, check if it is a template and advance through
839   // the list if so.
840   int i,p=0;
841   auto alIt = tArgLists.begin();
842   while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
843   {
844     NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
845     if (nd==0)
846     {
847       ClassDef *cd = getClass(name.left(i));
848       if (cd)
849       {
850         if (!cd->templateArguments().empty())
851         {
852           ++alIt;
853         }
854       }
855     }
856     p=i+2;
857   }
858   return alIt!=tArgLists.end() ?
859          std::make_unique<ArgumentList>(*alIt) :
860          std::unique_ptr<ArgumentList>();
861 }
862
863 static
864 ClassDef::CompoundType convertToCompoundType(int section,uint64_t specifier)
865 {
866   ClassDef::CompoundType sec=ClassDef::Class;
867   if (specifier&Entry::Struct)
868     sec=ClassDef::Struct;
869   else if (specifier&Entry::Union)
870     sec=ClassDef::Union;
871   else if (specifier&Entry::Category)
872     sec=ClassDef::Category;
873   else if (specifier&Entry::Interface)
874     sec=ClassDef::Interface;
875   else if (specifier&Entry::Protocol)
876     sec=ClassDef::Protocol;
877   else if (specifier&Entry::Exception)
878     sec=ClassDef::Exception;
879   else if (specifier&Entry::Service)
880     sec=ClassDef::Service;
881   else if (specifier&Entry::Singleton)
882     sec=ClassDef::Singleton;
883
884   switch(section)
885   {
886     //case Entry::UNION_SEC:
887     case Entry::UNIONDOC_SEC:
888       sec=ClassDef::Union;
889       break;
890       //case Entry::STRUCT_SEC:
891     case Entry::STRUCTDOC_SEC:
892       sec=ClassDef::Struct;
893       break;
894       //case Entry::INTERFACE_SEC:
895     case Entry::INTERFACEDOC_SEC:
896       sec=ClassDef::Interface;
897       break;
898       //case Entry::PROTOCOL_SEC:
899     case Entry::PROTOCOLDOC_SEC:
900       sec=ClassDef::Protocol;
901       break;
902       //case Entry::CATEGORY_SEC:
903     case Entry::CATEGORYDOC_SEC:
904       sec=ClassDef::Category;
905       break;
906       //case Entry::EXCEPTION_SEC:
907     case Entry::EXCEPTIONDOC_SEC:
908       sec=ClassDef::Exception;
909       break;
910     case Entry::SERVICEDOC_SEC:
911       sec=ClassDef::Service;
912       break;
913     case Entry::SINGLETONDOC_SEC:
914       sec=ClassDef::Singleton;
915       break;
916   }
917   return sec;
918 }
919
920
921 static void addClassToContext(const Entry *root)
922 {
923   AUTO_TRACE("name={}",root->name);
924   FileDef *fd = root->fileDef();
925
926   QCString scName;
927   if (root->parent()->section&Entry::SCOPE_MASK)
928   {
929      scName=root->parent()->name;
930   }
931   // name without parent's scope
932   QCString fullName = root->name;
933
934   // strip off any template parameters (but not those for specializations)
935   fullName=stripTemplateSpecifiersFromScope(fullName);
936
937   // name with scope (if not present already)
938   QCString qualifiedName = fullName;
939   if (!scName.isEmpty() && !leftScopeMatch(fullName,scName))
940   {
941     qualifiedName.prepend(scName+"::");
942   }
943
944   // see if we already found the class before
945   ClassDefMutable *cd = getClassMutable(qualifiedName);
946
947   AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
948
949   if (cd)
950   {
951     fullName=cd->name();
952     AUTO_TRACE_ADD("Existing class '{}'",cd->name());
953     //if (cd->templateArguments()==0)
954     //{
955     //  //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
956     //  cd->setTemplateArguments(tArgList);
957     //}
958
959     cd->setDocumentation(root->doc,root->docFile,root->docLine);
960     cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
961
962     if ((root->spec&Entry::ForwardDecl)==0 && cd->isForwardDeclared())
963     {
964       cd->setDefFile(root->fileName,root->startLine,root->startColumn);
965       if (root->bodyLine!=-1)
966       {
967         cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
968         cd->setBodyDef(fd);
969       }
970     }
971
972     if (cd->templateArguments().empty() || (cd->isForwardDeclared() && (root->spec&Entry::ForwardDecl)==0))
973     {
974       // this happens if a template class declared with @class is found
975       // before the actual definition or if a forward declaration has different template
976       // parameter names.
977       std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
978       if (tArgList)
979       {
980         cd->setTemplateArguments(*tArgList);
981       }
982     }
983     if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
984     {
985       cd->setRequiresClause(root->req);
986     }
987
988     cd->setCompoundType(convertToCompoundType(root->section,root->spec));
989
990     cd->setMetaData(root->metaData);
991   }
992   else // new class
993   {
994     ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec);
995
996     QCString className;
997     QCString namespaceName;
998     extractNamespaceName(fullName,className,namespaceName);
999
1000     AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1001         fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1002
1003     QCString tagName;
1004     QCString refFileName;
1005     const TagInfo *tagInfo = root->tagInfo();
1006     int i;
1007     if (tagInfo)
1008     {
1009       tagName     = tagInfo->tagName;
1010       refFileName = tagInfo->fileName;
1011       if (fullName.find("::")!=-1)
1012         // symbols imported via tag files may come without the parent scope,
1013         // so we artificially create it here
1014       {
1015         buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1016       }
1017     }
1018     std::unique_ptr<ArgumentList> tArgList;
1019     if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) && (i=fullName.findRev('<'))!=-1)
1020     {
1021       // a Java/C# generic class looks like a C++ specialization, so we need to split the
1022       // name and template arguments here
1023       tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1024       fullName=fullName.left(i);
1025     }
1026     else
1027     {
1028       tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1029     }
1030     // add class to the list
1031     cd = toClassDefMutable(
1032         Doxygen::classLinkedMap->add(fullName,
1033           createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1034              fullName,sec,tagName,refFileName,TRUE,root->spec&Entry::Enum) ));
1035     if (cd)
1036     {
1037       AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1038               fullName,cd->compoundTypeString(),root->tArgLists.size(),
1039               fmt::ptr(tagInfo),root->hidden,root->artificial);
1040       cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1041       cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1042       cd->setLanguage(root->lang);
1043       cd->setId(root->id);
1044       cd->setHidden(root->hidden);
1045       cd->setArtificial(root->artificial);
1046       cd->setClassSpecifier(root->spec);
1047       cd->addQualifiers(root->qualifiers);
1048       cd->setTypeConstraints(root->typeConstr);
1049       cd->enableCollaborationGraph(root->collaborationGraph);
1050
1051       if (tArgList)
1052       {
1053         cd->setTemplateArguments(*tArgList);
1054       }
1055       cd->setRequiresClause(root->req);
1056       cd->setProtection(root->protection);
1057       cd->setIsStatic(root->isStatic);
1058
1059       // file definition containing the class cd
1060       cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1061       cd->setBodyDef(fd);
1062
1063       cd->setMetaData(root->metaData);
1064
1065       cd->insertUsedFile(fd);
1066     }
1067     else
1068     {
1069       AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1070     }
1071   }
1072
1073   if (cd)
1074   {
1075     cd->addSectionsToDefinition(root->anchors);
1076     if (!root->subGrouping) cd->setSubGrouping(FALSE);
1077     if ((root->spec&Entry::ForwardDecl)==0)
1078     {
1079       if (cd->hasDocumentation())
1080       {
1081         addIncludeFile(cd,fd,root);
1082       }
1083       if (fd && (root->section & Entry::COMPOUND_MASK))
1084       {
1085         AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1086         cd->setFileDef(fd);
1087         fd->insertClass(cd);
1088       }
1089     }
1090     addClassToGroups(root,cd);
1091     ModuleManager::instance().addClassToModule(root,cd);
1092     cd->setRefItems(root->sli);
1093   }
1094 }
1095
1096 //----------------------------------------------------------------------
1097 // build a list of all classes mentioned in the documentation
1098 // and all classes that have a documentation block before their definition.
1099 static void buildClassList(const Entry *root)
1100 {
1101   if (
1102         ((root->section & Entry::COMPOUND_MASK) ||
1103          root->section==Entry::OBJCIMPL_SEC) && !root->name.isEmpty()
1104      )
1105   {
1106     AUTO_TRACE();
1107     addClassToContext(root);
1108   }
1109   for (const auto &e : root->children()) buildClassList(e.get());
1110 }
1111
1112 static void buildClassDocList(const Entry *root)
1113 {
1114   if (
1115        (root->section & Entry::COMPOUNDDOC_MASK) && !root->name.isEmpty()
1116      )
1117   {
1118     AUTO_TRACE();
1119     addClassToContext(root);
1120   }
1121   for (const auto &e : root->children()) buildClassDocList(e.get());
1122 }
1123
1124 //----------------------------------------------------------------------
1125 // build a list of all classes mentioned in the documentation
1126 // and all classes that have a documentation block before their definition.
1127
1128 static void addConceptToContext(const Entry *root)
1129 {
1130   AUTO_TRACE();
1131   FileDef *fd = root->fileDef();
1132
1133   QCString scName;
1134   if (root->parent()->section&Entry::SCOPE_MASK)
1135   {
1136      scName=root->parent()->name;
1137   }
1138
1139   // name with scope (if not present already)
1140   QCString qualifiedName = root->name;
1141   if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1142   {
1143     qualifiedName.prepend(scName+"::");
1144   }
1145
1146   // see if we already found the concept before
1147   ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1148
1149   AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1150
1151   if (cd)
1152   {
1153     qualifiedName=cd->name();
1154     AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1155
1156     cd->setDocumentation(root->doc,root->docFile,root->docLine);
1157     cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1158
1159     addIncludeFile(cd,fd,root);
1160   }
1161   else // new concept
1162   {
1163     QCString className;
1164     QCString namespaceName;
1165     extractNamespaceName(qualifiedName,className,namespaceName);
1166
1167     AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1168         qualifiedName,namespaceName,className,root->brief,root->doc);
1169
1170     QCString tagName;
1171     QCString refFileName;
1172     const TagInfo *tagInfo = root->tagInfo();
1173     if (tagInfo)
1174     {
1175       tagName     = tagInfo->tagName;
1176       refFileName = tagInfo->fileName;
1177       if (qualifiedName.find("::")!=-1)
1178         // symbols imported via tag files may come without the parent scope,
1179         // so we artificially create it here
1180       {
1181         buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1182       }
1183     }
1184     std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1185     // add concept to the list
1186     cd = toConceptDefMutable(
1187         Doxygen::conceptLinkedMap->add(qualifiedName,
1188             createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1189                qualifiedName,tagName,refFileName)));
1190     if (cd)
1191     {
1192       AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1193           qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1194       cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1195       cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1196       cd->setLanguage(root->lang);
1197       cd->setId(root->id);
1198       cd->setHidden(root->hidden);
1199       cd->setGroupId(root->mGrpId);
1200       if (tArgList)
1201       {
1202         cd->setTemplateArguments(*tArgList);
1203       }
1204       cd->setInitializer(root->initializer.str().c_str());
1205       // file definition containing the class cd
1206       cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1207       cd->setBodyDef(fd);
1208       addIncludeFile(cd,fd,root);
1209
1210       // also add namespace to the correct structural context
1211       Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,0,tagInfo);
1212       if (d && d->definitionType()==Definition::TypeNamespace)
1213       {
1214         DefinitionMutable *dm = toDefinitionMutable(d);
1215         if (dm)
1216         {
1217           dm->addInnerCompound(cd);
1218         }
1219         cd->setOuterScope(d);
1220       }
1221     }
1222     else
1223     {
1224       AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1225     }
1226   }
1227
1228   if (cd)
1229   {
1230     cd->addSectionsToDefinition(root->anchors);
1231     if (fd)
1232     {
1233       AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1234       cd->setFileDef(fd);
1235       fd->insertConcept(cd);
1236     }
1237     addConceptToGroups(root,cd);
1238     ModuleManager::instance().addConceptToModule(root,cd);
1239     cd->setRefItems(root->sli);
1240   }
1241 }
1242
1243 static void findModuleDocumentation(const Entry *root)
1244 {
1245   if (root->section==Entry::MODULEDOC_SEC)
1246   {
1247     AUTO_TRACE();
1248     ModuleManager::instance().addDocs(root);
1249   }
1250   for (const auto &e : root->children()) findModuleDocumentation(e.get());
1251 }
1252
1253 static void buildConceptList(const Entry *root)
1254 {
1255   if (root->section & Entry::CONCEPT_SEC)
1256   {
1257     AUTO_TRACE();
1258     addConceptToContext(root);
1259   }
1260   for (const auto &e : root->children()) buildConceptList(e.get());
1261 }
1262
1263 static void buildConceptDocList(const Entry *root)
1264 {
1265   if (root->section & Entry::CONCEPTDOC_SEC)
1266   {
1267     AUTO_TRACE();
1268     addConceptToContext(root);
1269   }
1270   for (const auto &e : root->children()) buildConceptDocList(e.get());
1271 }
1272
1273 // This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1274 // (same also works for variable and functions because of logic in MemberGroup::insertMember)
1275 static void distributeConceptGroups()
1276 {
1277   AUTO_TRACE();
1278   for (const auto &cd : *Doxygen::conceptLinkedMap)
1279   {
1280     if (cd->groupId()!=DOX_NOGROUP)
1281     {
1282       for (const auto &ocd : *Doxygen::conceptLinkedMap)
1283       {
1284         if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1285             !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1286         {
1287           ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1288           if (ocdm)
1289           {
1290             for (const auto &gd : cd->partOfGroups())
1291             {
1292               if (gd)
1293               {
1294                 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1295                 ocdm->makePartOfGroup(gd);
1296                 gd->addConcept(ocd.get());
1297               }
1298             }
1299           }
1300         }
1301       }
1302     }
1303   }
1304 }
1305
1306 //----------------------------------------------------------------------
1307
1308 static void resolveClassNestingRelations()
1309 {
1310   ClassDefSet visitedClasses;
1311
1312   bool done=FALSE;
1313   //int iteration=0;
1314   while (!done)
1315   {
1316     done=TRUE;
1317     //++iteration;
1318     struct ClassAlias
1319     {
1320       ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1321         aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1322       QCString aliasFullName;
1323       std::unique_ptr<ClassDef> aliasCd;
1324       DefinitionMutable *aliasContext;
1325     };
1326     std::vector<ClassAlias> aliases;
1327     for (const auto &icd : *Doxygen::classLinkedMap)
1328     {
1329       ClassDefMutable *cd = toClassDefMutable(icd.get());
1330       if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1331       {
1332         QCString name = stripAnonymousNamespaceScope(icd->name());
1333         //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1334         // also add class to the correct structural context
1335         Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,
1336                                                  name,icd->getFileDef(),0);
1337         if (d)
1338         {
1339           //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1340           DefinitionMutable *dm = toDefinitionMutable(d);
1341           if (dm)
1342           {
1343             dm->addInnerCompound(cd);
1344           }
1345           cd->setOuterScope(d);
1346
1347           // for inline namespace add an alias of the class to the outer scope
1348           while (d->definitionType()==Definition::TypeNamespace)
1349           {
1350             NamespaceDef *nd = toNamespaceDef(d);
1351             //printf("nd->isInline()=%d\n",nd->isInline());
1352             if (nd && nd->isInline())
1353             {
1354               d = d->getOuterScope();
1355               if (d)
1356               {
1357                 dm = toDefinitionMutable(d);
1358                 if (dm)
1359                 {
1360                   auto aliasCd = createClassDefAlias(d,cd);
1361                   QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1362                   aliases.push_back(ClassAlias(aliasFullName,std::move(aliasCd),dm));
1363                   //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1364                 }
1365               }
1366             }
1367             else
1368             {
1369               break;
1370             }
1371           }
1372
1373           visitedClasses.insert(icd.get());
1374           done=FALSE;
1375         }
1376         //else
1377         //{
1378         //  printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1379         //}
1380       }
1381     }
1382     // add aliases
1383     for (auto &alias : aliases)
1384     {
1385        ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1386        if (aliasCd)
1387        {
1388          alias.aliasContext->addInnerCompound(aliasCd);
1389        }
1390     }
1391   }
1392
1393   //give warnings for unresolved compounds
1394   for (const auto &icd : *Doxygen::classLinkedMap)
1395   {
1396     ClassDefMutable *cd = toClassDefMutable(icd.get());
1397     if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1398     {
1399       QCString name = stripAnonymousNamespaceScope(cd->name());
1400       //printf("processing unresolved=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1401       /// create the scope artificially
1402       // anyway, so we can at least relate scopes properly.
1403       Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),0);
1404       if (d && d!=cd && !cd->getDefFileName().isEmpty())
1405                  // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1406                  // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1407                  // also avoid warning for stuff imported via a tagfile.
1408       {
1409         DefinitionMutable *dm = toDefinitionMutable(d);
1410         if (dm)
1411         {
1412           dm->addInnerCompound(cd);
1413         }
1414         cd->setOuterScope(d);
1415         warn(cd->getDefFileName(),cd->getDefLine(),
1416             "Internal inconsistency: scope for class %s not "
1417             "found!",qPrint(name)
1418             );
1419       }
1420     }
1421   }
1422 }
1423
1424 void distributeClassGroupRelations()
1425 {
1426   //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1427   //if (!inlineGroupedClasses) return;
1428   //printf("** distributeClassGroupRelations()\n");
1429
1430   ClassDefSet visitedClasses;
1431   for (const auto &cd : *Doxygen::classLinkedMap)
1432   {
1433     //printf("Checking %s\n",qPrint(cd->name()));
1434     // distribute the group to nested classes as well
1435     if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1436     {
1437       //printf("  Candidate for merging\n");
1438       GroupDef *gd = cd->partOfGroups().front();
1439       for (auto &ncd : cd->getClasses())
1440       {
1441         ClassDefMutable *ncdm = toClassDefMutable(ncd);
1442         if (ncdm && ncdm->partOfGroups().empty())
1443         {
1444           //printf("  Adding %s to group '%s'\n",qPrint(ncd->name()),
1445           //    gd->groupTitle());
1446           ncdm->makePartOfGroup(gd);
1447           gd->addClass(ncdm);
1448         }
1449       }
1450       visitedClasses.insert(cd.get()); // only visit every class once
1451     }
1452   }
1453 }
1454
1455 //----------------------------
1456
1457 static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1458 {
1459   QCString fullName = removeAnonymousScopes(templ->name());
1460   if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1461   fullName+="."+fieldName;
1462
1463   //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1464   ClassDefMutable *cd = toClassDefMutable(
1465       Doxygen::classLinkedMap->add(fullName,
1466          createClassDef(templ->getDefFileName(),
1467                             templ->getDefLine(),
1468                             templ->getDefColumn(),
1469                             fullName,
1470                             templ->compoundType())));
1471   if (cd)
1472   {
1473     cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1474     cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1475     cd->setLanguage(templ->getLanguage());
1476     cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1477     cd->setBodyDef(templ->getBodyDef());
1478
1479     cd->setOuterScope(rootCd->getOuterScope());
1480     if (rootCd->getOuterScope()!=Doxygen::globalScope)
1481     {
1482       DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1483       if (outerScope)
1484       {
1485         outerScope->addInnerCompound(cd);
1486       }
1487     }
1488
1489     FileDef *fd = templ->getFileDef();
1490     if (fd)
1491     {
1492       cd->setFileDef(fd);
1493       fd->insertClass(cd);
1494     }
1495     for (const auto &gd : rootCd->partOfGroups())
1496     {
1497       cd->makePartOfGroup(gd);
1498       const_cast<GroupDef*>(gd)->addClass(cd);
1499     }
1500
1501     MemberList *ml = templ->getMemberList(MemberListType_pubAttribs);
1502     if (ml)
1503     {
1504       for (const auto &md : *ml)
1505       {
1506         //printf("    Member %s type=%s\n",qPrint(md->name()),md->typeString());
1507         auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1508             md->typeString(),md->name(),md->argsString(),md->excpString(),
1509             md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1510             md->memberType(),
1511             ArgumentList(),ArgumentList(),"");
1512         MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1513         imd->setMemberClass(cd);
1514         imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1515         imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1516         imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1517         imd->setMemberSpecifiers(md->getMemberSpecifiers());
1518         imd->setMemberGroupId(md->getMemberGroupId());
1519         imd->setInitializer(md->initializer());
1520         imd->setRequiresClause(md->requiresClause());
1521         imd->setMaxInitLines(md->initializerLines());
1522         imd->setBitfields(md->bitfieldString());
1523         imd->setLanguage(md->getLanguage());
1524         cd->insertMember(imd);
1525         MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1526         mn->push_back(std::move(newMd));
1527       }
1528     }
1529   }
1530   return cd;
1531 }
1532
1533 /** Look through the members of class \a cd and its public members.
1534  *  If there is a member m of a tag less struct/union,
1535  *  then we create a duplicate of the struct/union with the name of the
1536  *  member to identify it.
1537  *  So if cd has name S, then the tag less struct/union will get name S.m
1538  *  Since tag less structs can be nested we need to call this function
1539  *  recursively. Later on we need to patch the member types so we keep
1540  *  track of the hierarchy of classes we create.
1541  */
1542 static void processTagLessClasses(const ClassDef *rootCd,
1543                                   const ClassDef *cd,
1544                                   ClassDefMutable *tagParentCd,
1545                                   const QCString &prefix,int count)
1546 {
1547   //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1548   //printf("checking members for %s\n",qPrint(cd->name()));
1549   if (tagParentCd && !cd->getClasses().empty())
1550   {
1551     MemberList *ml = cd->getMemberList(MemberListType_pubAttribs);
1552     if (ml)
1553     {
1554       for (const auto &md : *ml)
1555       {
1556         QCString type = md->typeString();
1557         if (type.find("::@")!=-1) // member of tag less struct/union
1558         {
1559           for (const auto &icd : cd->getClasses())
1560           {
1561             //printf("  member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1562             //printf("  comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1563             if (type.find(icd->name())!=-1) // matching tag less struct/union
1564             {
1565               QCString name = md->name();
1566               if (md->isAnonymous()) name = "__unnamed" + name.right(name.length()-1)+"__";
1567               if (!prefix.isEmpty()) name.prepend(prefix+".");
1568               //printf("    found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1569               ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1570               if (ncd)
1571               {
1572                 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1573                 //printf("    addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1574                 ncd->setTagLessReference(icd);
1575
1576                 // replace tag-less type for generated/original member
1577                 // by newly created class name.
1578                 // note the difference between changing cd and tagParentCd.
1579                 // for the initial call this is the same pointer, but for
1580                 // recursive calls cd is the original tag-less struct (of which
1581                 // there is only one instance) and tagParentCd is the newly
1582                 // generated tagged struct of which there can be multiple instances!
1583                 MemberList *pml = tagParentCd->getMemberList(MemberListType_pubAttribs);
1584                 if (pml)
1585                 {
1586                   for (const auto &pmd : *pml)
1587                   {
1588                     MemberDefMutable *pmdm = toMemberDefMutable(pmd);
1589                     if (pmdm && pmd->name()==md->name())
1590                     {
1591                       pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1592                       //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1593                     }
1594                   }
1595                 }
1596               }
1597             }
1598           }
1599         }
1600       }
1601     }
1602   }
1603 }
1604
1605 static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1606 {
1607   for (const auto &icd : cd->getClasses())
1608   {
1609     if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1610     {
1611       findTagLessClasses(candidates,icd);
1612     }
1613   }
1614
1615   ClassDefMutable *cdm = toClassDefMutable(cd);
1616   if (cdm)
1617   {
1618     candidates.push_back(cdm);
1619   }
1620 }
1621
1622 static void findTagLessClasses()
1623 {
1624   std::vector<ClassDefMutable *> candidates;
1625   for (auto &cd : *Doxygen::classLinkedMap)
1626   {
1627     Definition *scope = cd->getOuterScope();
1628     if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1629     {
1630       findTagLessClasses(candidates,cd.get());
1631     }
1632   }
1633
1634   // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1635   // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1636   for (auto &cd : candidates)
1637   {
1638     processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1639   }
1640 }
1641
1642
1643 //----------------------------------------------------------------------
1644 // build a list of all namespaces mentioned in the documentation
1645 // and all namespaces that have a documentation block before their definition.
1646 static void buildNamespaceList(const Entry *root)
1647 {
1648   if (
1649        (root->section==Entry::NAMESPACE_SEC ||
1650         root->section==Entry::NAMESPACEDOC_SEC ||
1651         root->section==Entry::PACKAGEDOC_SEC
1652        ) &&
1653        !root->name.isEmpty()
1654      )
1655   {
1656     AUTO_TRACE("name={}",root->name);
1657
1658     QCString fName = root->name;
1659     if (root->section==Entry::PACKAGEDOC_SEC)
1660     {
1661       fName=substitute(fName,".","::");
1662     }
1663
1664     QCString fullName = stripAnonymousNamespaceScope(fName);
1665     if (!fullName.isEmpty())
1666     {
1667       AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1668       NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1669       if (ndi) // existing namespace
1670       {
1671         NamespaceDefMutable *nd = toNamespaceDefMutable(ndi);
1672         if (nd) // non-inline namespace
1673         {
1674           AUTO_TRACE_ADD("Existing namespace");
1675           nd->setDocumentation(root->doc,root->docFile,root->docLine);
1676           nd->setName(fullName); // change name to match docs
1677           nd->addSectionsToDefinition(root->anchors);
1678           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1679           if (nd->getLanguage()==SrcLangExt_Unknown)
1680           {
1681             nd->setLanguage(root->lang);
1682           }
1683           if (root->tagInfo()==0 && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1684             // if we previously found namespace nd in a tag file and now we find a
1685             // documented namespace with the same name in the project, then remove
1686             // the tag file reference
1687           {
1688             nd->setReference("");
1689             nd->setFileName(fullName);
1690           }
1691           nd->setMetaData(root->metaData);
1692
1693           // file definition containing the namespace nd
1694           FileDef *fd=root->fileDef();
1695           if (nd->isArtificial())
1696           {
1697             nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1698             nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1699           }
1700           // insert the namespace in the file definition
1701           if (fd) fd->insertNamespace(nd);
1702           addNamespaceToGroups(root,nd);
1703           nd->setRefItems(root->sli);
1704         }
1705       }
1706       else // fresh namespace
1707       {
1708         QCString tagName;
1709         QCString tagFileName;
1710         const TagInfo *tagInfo = root->tagInfo();
1711         if (tagInfo)
1712         {
1713           tagName     = tagInfo->tagName;
1714           tagFileName = tagInfo->fileName;
1715         }
1716         AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1717         // add namespace to the list
1718         NamespaceDefMutable *nd = toNamespaceDefMutable(
1719             Doxygen::namespaceLinkedMap->add(fullName,
1720               createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1721                 root->startColumn,fullName,tagName,tagFileName,
1722                 root->type,root->spec&Entry::Published)));
1723         if (nd)
1724         {
1725           nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1726           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1727           nd->addSectionsToDefinition(root->anchors);
1728           nd->setHidden(root->hidden);
1729           nd->setArtificial(root->artificial);
1730           nd->setLanguage(root->lang);
1731           nd->setId(root->id);
1732           nd->setMetaData(root->metaData);
1733           nd->setInline((root->spec&Entry::Inline)!=0);
1734           nd->setExported(root->exported);
1735
1736           addNamespaceToGroups(root,nd);
1737           nd->setRefItems(root->sli);
1738
1739           // file definition containing the namespace nd
1740           FileDef *fd=root->fileDef();
1741           // insert the namespace in the file definition
1742           if (fd) fd->insertNamespace(nd);
1743
1744           // the empty string test is needed for extract all case
1745           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1746           nd->insertUsedFile(fd);
1747           nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1748           nd->setBodyDef(fd);
1749
1750           // also add namespace to the correct structural context
1751           Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,0,tagInfo);
1752           AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1753           if (d==0) // we didn't find anything, create the scope artificially
1754             // anyway, so we can at least relate scopes properly.
1755           {
1756             d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1757             DefinitionMutable *dm = toDefinitionMutable(d);
1758             if (dm)
1759             {
1760               dm->addInnerCompound(nd);
1761             }
1762             nd->setOuterScope(d);
1763             // TODO: Due to the order in which the tag file is written
1764             // a nested class can be found before its parent!
1765           }
1766           else
1767           {
1768             DefinitionMutable *dm = toDefinitionMutable(d);
1769             if (dm)
1770             {
1771               dm->addInnerCompound(nd);
1772             }
1773             nd->setOuterScope(d);
1774             // in case of d is an inline namespace, alias insert nd in the part scope of d.
1775             while (d->definitionType()==Definition::TypeNamespace)
1776             {
1777               NamespaceDef *pnd = toNamespaceDef(d);
1778               if (pnd && pnd->isInline())
1779               {
1780                 d = d->getOuterScope();
1781                 if (d)
1782                 {
1783                   dm = toDefinitionMutable(d);
1784                   if (dm)
1785                   {
1786                     auto aliasNd = createNamespaceDefAlias(d,nd);
1787                     dm->addInnerCompound(aliasNd.get());
1788                     QCString aliasName = aliasNd->name();
1789                     AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1790                     Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1791                   }
1792                 }
1793                 else
1794                 {
1795                   break;
1796                 }
1797               }
1798               else
1799               {
1800                 break;
1801               }
1802             }
1803           }
1804         }
1805       }
1806     }
1807   }
1808   for (const auto &e : root->children()) buildNamespaceList(e.get());
1809 }
1810
1811 //----------------------------------------------------------------------
1812
1813 static NamespaceDef *findUsedNamespace(const LinkedRefMap<NamespaceDef> &unl,
1814                               const QCString &name)
1815 {
1816   NamespaceDef *usingNd =0;
1817   for (auto &und : unl)
1818   {
1819     QCString uScope=und->name()+"::";
1820     usingNd = getResolvedNamespace(uScope+name);
1821     if (usingNd!=0) break;
1822   }
1823   return usingNd;
1824 }
1825
1826 static void findUsingDirectives(const Entry *root)
1827 {
1828   if (root->section==Entry::USINGDIR_SEC)
1829   {
1830     AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1831     QCString name=substitute(root->name,".","::");
1832     if (name.endsWith("::"))
1833     {
1834       name=name.left(name.length()-2);
1835     }
1836     if (!name.isEmpty())
1837     {
1838       NamespaceDef *usingNd = 0;
1839       NamespaceDefMutable *nd = 0;
1840       FileDef      *fd = root->fileDef();
1841       QCString nsName;
1842
1843       // see if the using statement was found inside a namespace or inside
1844       // the global file scope.
1845       if (root->parent() && root->parent()->section==Entry::NAMESPACE_SEC &&
1846           (fd==0 || fd->getLanguage()!=SrcLangExt_Java) // not a .java file
1847          )
1848       {
1849         nsName=stripAnonymousNamespaceScope(root->parent()->name);
1850         if (!nsName.isEmpty())
1851         {
1852           nd = getResolvedNamespaceMutable(nsName);
1853         }
1854       }
1855
1856       // find the scope in which the 'using' namespace is defined by prepending
1857       // the possible scopes in which the using statement was found, starting
1858       // with the most inner scope and going to the most outer scope (i.e.
1859       // file scope).
1860       int scopeOffset = nsName.length();
1861       do
1862       {
1863         QCString scope=scopeOffset>0 ?
1864                       nsName.left(scopeOffset)+"::" : QCString();
1865         usingNd = getResolvedNamespace(scope+name);
1866         //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1867         if (scopeOffset==0)
1868         {
1869           scopeOffset=-1;
1870         }
1871         else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1872         {
1873           scopeOffset=0;
1874         }
1875       } while (scopeOffset>=0 && usingNd==0);
1876
1877       if (usingNd==0 && nd) // not found, try used namespaces in this scope
1878                             // or in one of the parent namespace scopes
1879       {
1880         const NamespaceDefMutable *pnd = nd;
1881         while (pnd && usingNd==0)
1882         {
1883           // also try with one of the used namespaces found earlier
1884           usingNd = toNamespaceDefMutable(findUsedNamespace(pnd->getUsedNamespaces(),name));
1885
1886           // goto the parent
1887           Definition *s = pnd->getOuterScope();
1888           if (s && s->definitionType()==Definition::TypeNamespace)
1889           {
1890             pnd = toNamespaceDefMutable(toNamespaceDef(s));
1891           }
1892           else
1893           {
1894             pnd = 0;
1895           }
1896         }
1897       }
1898       if (usingNd==0 && fd) // still nothing, also try used namespace in the
1899                             // global scope
1900       {
1901         usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1902       }
1903
1904       //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1905
1906       // add the namespace the correct scope
1907       if (usingNd)
1908       {
1909         //printf("using fd=%p nd=%p\n",fd,nd);
1910         if (nd)
1911         {
1912           //printf("Inside namespace %s\n",qPrint(nd->name()));
1913           nd->addUsingDirective(usingNd);
1914         }
1915         else if (fd)
1916         {
1917           //printf("Inside file %s\n",qPrint(fd->name()));
1918           fd->addUsingDirective(usingNd);
1919         }
1920       }
1921       else // unknown namespace, but add it anyway.
1922       {
1923         AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1924         // add namespace to the list
1925         nd = toNamespaceDefMutable(
1926             Doxygen::namespaceLinkedMap->add(name,
1927                createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1928         if (nd)
1929         {
1930           nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1931           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1932           nd->addSectionsToDefinition(root->anchors);
1933           nd->setHidden(root->hidden);
1934           nd->setArtificial(TRUE);
1935           nd->setLanguage(root->lang);
1936           nd->setId(root->id);
1937           nd->setMetaData(root->metaData);
1938           nd->setInline((root->spec&Entry::Inline)!=0);
1939           nd->setExported(root->exported);
1940
1941           for (const Grouping &g : root->groups)
1942           {
1943             GroupDef *gd=0;
1944             if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1945               gd->addNamespace(nd);
1946           }
1947
1948           // insert the namespace in the file definition
1949           if (fd)
1950           {
1951             fd->insertNamespace(nd);
1952             fd->addUsingDirective(nd);
1953           }
1954
1955           // the empty string test is needed for extract all case
1956           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1957           nd->insertUsedFile(fd);
1958           nd->setRefItems(root->sli);
1959         }
1960       }
1961     }
1962   }
1963   for (const auto &e : root->children()) findUsingDirectives(e.get());
1964 }
1965
1966 //----------------------------------------------------------------------
1967
1968 static void buildListOfUsingDecls(const Entry *root)
1969 {
1970   if (root->section==Entry::USINGDECL_SEC &&
1971       !(root->parent()->section&Entry::COMPOUND_MASK) // not a class/struct member
1972      )
1973   {
1974     QCString name = substitute(root->name,".","::");
1975     g_usingDeclarations.insert(name.str());
1976   }
1977   for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
1978 }
1979
1980
1981 static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
1982 {
1983   if (root->section==Entry::USINGDECL_SEC &&
1984       !(root->parent()->section&Entry::COMPOUND_MASK) && // not a class/struct member
1985       (!filterPythonPackages || (root->lang==SrcLangExt_Python && root->fileName.endsWith("__init__.py")))
1986      )
1987   {
1988     AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {:#10x}",
1989        root->name,root->startLine,root->fileName,root->parent()->section);
1990     if (!root->name.isEmpty())
1991     {
1992       ClassDefMutable *usingCd = 0;
1993       NamespaceDefMutable *nd = 0;
1994       FileDef      *fd = root->fileDef();
1995       QCString scName;
1996
1997       // see if the using statement was found inside a namespace or inside
1998       // the global file scope.
1999       if (root->parent()->section == Entry::NAMESPACE_SEC)
2000       {
2001         scName=root->parent()->name;
2002         if (!scName.isEmpty())
2003         {
2004           nd = getResolvedNamespaceMutable(scName);
2005         }
2006       }
2007
2008       // Assume the using statement was used to import a class.
2009       // Find the scope in which the 'using' namespace is defined by prepending
2010       // the possible scopes in which the using statement was found, starting
2011       // with the most inner scope and going to the most outer scope (i.e.
2012       // file scope).
2013
2014       QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2015       usingCd = getClassMutable(name); // try direct lookup first, this is needed to get
2016                                 // builtin STL classes to properly resolve, e.g.
2017                                 // vector -> std::vector
2018       if (usingCd==0)
2019       {
2020         SymbolResolver resolver(fd);
2021         usingCd = resolver.resolveClassMutable(nd,name); // try via resolving (see also bug757509)
2022       }
2023       if (usingCd==0)
2024       {
2025         usingCd = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(name)); // check if it is already hidden
2026       }
2027
2028       if (usingCd==0) // definition not in the input => add an artificial class
2029       {
2030         AUTO_TRACE_ADD("New using class '{}' (sec={:10x})! #tArgLists={}",
2031              name,root->section,root->tArgLists.size());
2032         usingCd = toClassDefMutable(
2033              Doxygen::hiddenClassLinkedMap->add(name,
2034                createClassDef( "<using>",1,1, name, ClassDef::Class)));
2035         if (usingCd)
2036         {
2037           usingCd->setArtificial(TRUE);
2038           usingCd->setLanguage(root->lang);
2039         }
2040       }
2041       else
2042       {
2043         AUTO_TRACE_ADD("Found used class '{}' in scope='{}'",
2044             usingCd->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2045       }
2046
2047       if (nd)
2048       {
2049         nd->addUsingDeclaration(usingCd);
2050       }
2051       else if (fd)
2052       {
2053         fd->addUsingDeclaration(usingCd);
2054       }
2055     }
2056   }
2057   for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2058 }
2059
2060 //----------------------------------------------------------------------
2061
2062 static void findUsingDeclImports(const Entry *root)
2063 {
2064   if (root->section==Entry::USINGDECL_SEC &&
2065       (root->parent()->section&Entry::COMPOUND_MASK) // in a class/struct member
2066      )
2067   {
2068     AUTO_TRACE("Found using declaration '{}' inside section {:#10x}", root->name, root->parent()->section);
2069     QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2070     fullName=stripAnonymousNamespaceScope(fullName);
2071     fullName=stripTemplateSpecifiersFromScope(fullName);
2072     ClassDefMutable *cd = getClassMutable(fullName);
2073     if (cd)
2074     {
2075       AUTO_TRACE_ADD("found class '{}'",cd->name());
2076       int i=root->name.find("::");
2077       if (i!=-1)
2078       {
2079         QCString scope=root->name.left(i);
2080         QCString memName=root->name.right(root->name.length()-i-2);
2081         SymbolResolver resolver;
2082         const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2083         if (bcd && bcd!=cd)
2084         {
2085           AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2086           const MemberNameInfoLinkedMap &mnlm=bcd->memberNameInfoLinkedMap();
2087           const MemberNameInfo *mni = mnlm.find(memName);
2088           if (mni)
2089           {
2090             for (auto &mi : *mni)
2091             {
2092               const MemberDef *md = mi->memberDef();
2093               if (md && md->protection()!=Protection::Private)
2094               {
2095                 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2096                 QCString fileName = root->fileName;
2097                 if (fileName.isEmpty() && root->tagInfo())
2098                 {
2099                   fileName = root->tagInfo()->tagName;
2100                 }
2101                 const ArgumentList &templAl = md->templateArguments();
2102                 const ArgumentList &al = md->argumentList();
2103                 auto newMd = createMemberDef(
2104                     fileName,root->startLine,root->startColumn,
2105                     md->typeString(),memName,md->argsString(),
2106                     md->excpString(),root->protection,root->virt,
2107                     md->isStatic(),Relationship::Member,md->memberType(),
2108                     templAl,al,root->metaData
2109                     );
2110                 auto newMmd = toMemberDefMutable(newMd.get());
2111                 newMmd->setMemberClass(cd);
2112                 cd->insertMember(newMd.get());
2113                 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2114                 {
2115                   newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2116                   newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2117                   newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2118                 }
2119                 else
2120                 {
2121                   newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2122                   newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2123                   newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2124                 }
2125                 newMmd->setDefinition(md->definition());
2126                 newMmd->enableCallGraph(root->callGraph);
2127                 newMmd->enableCallerGraph(root->callerGraph);
2128                 newMmd->enableReferencedByRelation(root->referencedByRelation);
2129                 newMmd->enableReferencesRelation(root->referencesRelation);
2130                 newMmd->addQualifiers(root->qualifiers);
2131                 newMmd->setBitfields(md->bitfieldString());
2132                 newMmd->addSectionsToDefinition(root->anchors);
2133                 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2134                 newMmd->setBodyDef(md->getBodyDef());
2135                 newMmd->setInitializer(md->initializer());
2136                 newMmd->setRequiresClause(md->requiresClause());
2137                 newMmd->setMaxInitLines(md->initializerLines());
2138                 newMmd->setMemberGroupId(root->mGrpId);
2139                 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2140                 newMmd->setLanguage(root->lang);
2141                 newMmd->setId(root->id);
2142                 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2143                 mn->push_back(std::move(newMd));
2144               }
2145             }
2146           }
2147         }
2148       }
2149     }
2150
2151   }
2152   for (const auto &e : root->children()) findUsingDeclImports(e.get());
2153 }
2154
2155 //----------------------------------------------------------------------
2156
2157 static void findIncludedUsingDirectives()
2158 {
2159   FileDefSet visitedFiles;
2160   // then recursively add using directives found in #include files
2161   // to files that have not been visited.
2162   for (const auto &fn : *Doxygen::inputNameLinkedMap)
2163   {
2164     for (const auto &fd : *fn)
2165     {
2166       //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2167       fd->addIncludedUsingDirectives(visitedFiles);
2168     }
2169   }
2170 }
2171
2172 //----------------------------------------------------------------------
2173
2174 static MemberDef *addVariableToClass(
2175     const Entry *root,
2176     ClassDefMutable *cd,
2177     MemberType mtype,
2178     const QCString &type,
2179     const QCString &name,
2180     const QCString &args,
2181     bool fromAnnScope,
2182     MemberDef *fromAnnMemb,
2183     Protection prot,
2184     Relationship related)
2185 {
2186   QCString qualScope = cd->qualifiedNameWithTemplateParameters();
2187   QCString scopeSeparator="::";
2188   SrcLangExt lang = cd->getLanguage();
2189   if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2190   {
2191     qualScope = substitute(qualScope,"::",".");
2192     scopeSeparator=".";
2193   }
2194   AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2195       root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2196
2197   QCString def;
2198   if (!type.isEmpty())
2199   {
2200     if (related!=Relationship::Member || mtype==MemberType_Friend || Config_getBool(HIDE_SCOPE_NAMES))
2201     {
2202       if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2203       {
2204         def="using "+name+" = "+type.mid(7);
2205       }
2206       else
2207       {
2208         def=type+" "+name+args;
2209       }
2210     }
2211     else
2212     {
2213       if (root->spec&Entry::Alias) // turn 'typedef B C::A' into 'using C::A = B'
2214       {
2215         def="using "+qualScope+scopeSeparator+name+" = "+type.mid(7);
2216       }
2217       else
2218       {
2219         def=type+" "+qualScope+scopeSeparator+name+args;
2220       }
2221     }
2222   }
2223   else
2224   {
2225     if (Config_getBool(HIDE_SCOPE_NAMES))
2226     {
2227       def=name+args;
2228     }
2229     else
2230     {
2231       def=qualScope+scopeSeparator+name+args;
2232     }
2233   }
2234   def.stripPrefix("static ");
2235
2236   // see if the member is already found in the same scope
2237   // (this may be the case for a static member that is initialized
2238   //  outside the class)
2239   MemberName *mn=Doxygen::memberNameLinkedMap->find(name);
2240   if (mn)
2241   {
2242     for (const auto &imd : *mn)
2243     {
2244       //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2245       //    md->getClassDef(),cd,qPrint(type),md->typeString());
2246       MemberDefMutable *md = toMemberDefMutable(imd.get());
2247       if (md &&
2248           md->getClassDef()==cd &&
2249           removeRedundantWhiteSpace(type)==md->typeString())
2250         // member already in the scope
2251       {
2252
2253         if (root->lang==SrcLangExt_ObjC &&
2254             root->mtype==MethodTypes::Property &&
2255             md->memberType()==MemberType_Variable)
2256         { // Objective-C 2.0 property
2257           // turn variable into a property
2258           md->setProtection(root->protection);
2259           cd->reclassifyMember(md,MemberType_Property);
2260         }
2261         addMemberDocs(root,md,def,0,FALSE,root->spec);
2262         AUTO_TRACE_ADD("Member already found!");
2263         return md;
2264       }
2265     }
2266   }
2267
2268   QCString fileName = root->fileName;
2269   if (fileName.isEmpty() && root->tagInfo())
2270   {
2271     fileName = root->tagInfo()->tagName;
2272   }
2273
2274   // new member variable, typedef or enum value
2275   auto md = createMemberDef(
2276       fileName,root->startLine,root->startColumn,
2277       type,name,args,root->exception,
2278       prot,Specifier::Normal,root->isStatic,related,
2279       mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2280       ArgumentList(), root->metaData);
2281   auto mmd = toMemberDefMutable(md.get());
2282   mmd->setTagInfo(root->tagInfo());
2283   mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2284   mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2285   mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2286   mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2287   mmd->setDefinition(def);
2288   mmd->setBitfields(root->bitfields);
2289   mmd->addSectionsToDefinition(root->anchors);
2290   mmd->setFromAnonymousScope(fromAnnScope);
2291   mmd->setFromAnonymousMember(fromAnnMemb);
2292   //md->setIndentDepth(indentDepth);
2293   mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2294   std::string init = root->initializer.str();
2295   mmd->setInitializer(init.c_str());
2296   mmd->setMaxInitLines(root->initLines);
2297   mmd->setMemberGroupId(root->mGrpId);
2298   mmd->setMemberSpecifiers(root->spec);
2299   mmd->setReadAccessor(root->read);
2300   mmd->setWriteAccessor(root->write);
2301   mmd->enableCallGraph(root->callGraph);
2302   mmd->enableCallerGraph(root->callerGraph);
2303   mmd->enableReferencedByRelation(root->referencedByRelation);
2304   mmd->enableReferencesRelation(root->referencesRelation);
2305   mmd->setHidden(root->hidden);
2306   mmd->setArtificial(root->artificial);
2307   mmd->setLanguage(root->lang);
2308   mmd->setId(root->id);
2309   addMemberToGroups(root,md.get());
2310   ModuleManager::instance().addMemberToModule(root,md.get());
2311   mmd->setBodyDef(root->fileDef());
2312   mmd->addQualifiers(root->qualifiers);
2313
2314   AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2315   cd->insertMember(md.get());
2316   mmd->setRefItems(root->sli);
2317
2318   //TODO: insert FileDef instead of filename strings.
2319   cd->insertUsedFile(root->fileDef());
2320   root->markAsProcessed();
2321
2322   // add the member to the global list
2323   MemberDef *result = md.get();
2324   mn = Doxygen::memberNameLinkedMap->add(name);
2325   mn->push_back(std::move(md));
2326
2327   return result;
2328 }
2329
2330 //----------------------------------------------------------------------
2331
2332 static MemberDef *addVariableToFile(
2333     const Entry *root,
2334     MemberType mtype,
2335     const QCString &scope,
2336     const QCString &type,
2337     const QCString &name,
2338     const QCString &args,
2339     bool fromAnnScope,
2340     MemberDef *fromAnnMemb)
2341 {
2342   AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2343       root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2344
2345   FileDef *fd = root->fileDef();
2346
2347   // see if we have a typedef that should hide a struct or union
2348   if (mtype==MemberType_Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2349   {
2350     QCString ttype = type;
2351     ttype.stripPrefix("typedef ");
2352     if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2353     {
2354       static const reg::Ex re(R"(\a\w*)");
2355       reg::Match match;
2356       std::string typ = ttype.str();
2357       if (reg::search(typ,match,re))
2358       {
2359         QCString typeValue = match.str();
2360         ClassDefMutable *cd = getClassMutable(typeValue);
2361         if (cd)
2362         {
2363           // this typedef should hide compound name cd, so we
2364           // change the name that is displayed from cd.
2365           cd->setClassName(name);
2366           cd->setDocumentation(root->doc,root->docFile,root->docLine);
2367           cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2368           return 0;
2369         }
2370       }
2371     }
2372   }
2373
2374   // see if the function is inside a namespace
2375   NamespaceDefMutable *nd = 0;
2376   if (!scope.isEmpty())
2377   {
2378     if (scope.find('@')!=-1) return 0; // anonymous scope!
2379     nd = getResolvedNamespaceMutable(scope);
2380   }
2381   QCString def;
2382
2383   // determine the definition of the global variable
2384   if (nd && !nd->isAnonymous() &&
2385       !Config_getBool(HIDE_SCOPE_NAMES)
2386      )
2387     // variable is inside a namespace, so put the scope before the name
2388   {
2389     SrcLangExt lang = nd->getLanguage();
2390     QCString sep=getLanguageSpecificSeparator(lang);
2391
2392     if (!type.isEmpty())
2393     {
2394       if (root->spec&Entry::Alias) // turn 'typedef B NS::A' into 'using NS::A = B'
2395       {
2396         def="using "+nd->name()+sep+name+" = "+type;
2397       }
2398       else // normal member
2399       {
2400         def=type+" "+nd->name()+sep+name+args;
2401       }
2402     }
2403     else
2404     {
2405       def=nd->name()+sep+name+args;
2406     }
2407   }
2408   else
2409   {
2410     if (!type.isEmpty() && !root->name.isEmpty())
2411     {
2412       if (name.at(0)=='@') // dummy variable representing anonymous union
2413       {
2414         def=type;
2415       }
2416       else
2417       {
2418         if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2419         {
2420           def="using "+root->name+" = "+type.mid(7);
2421         }
2422         else // normal member
2423         {
2424           def=type+" "+name+args;
2425         }
2426       }
2427     }
2428     else
2429     {
2430       def=name+args;
2431     }
2432   }
2433   def.stripPrefix("static ");
2434
2435   MemberName *mn=Doxygen::functionNameLinkedMap->find(name);
2436   if (mn)
2437   {
2438     //QCString nscope=removeAnonymousScopes(scope);
2439     //NamespaceDef *nd=0;
2440     //if (!nscope.isEmpty())
2441     if (!scope.isEmpty())
2442     {
2443       nd = getResolvedNamespaceMutable(scope);
2444     }
2445     for (const auto &imd : *mn)
2446     {
2447       MemberDefMutable *md = toMemberDefMutable(imd.get());
2448       if (md &&
2449           ((nd==0 && md->getNamespaceDef()==0 && md->getFileDef() &&
2450             root->fileName==md->getFileDef()->absFilePath()
2451            ) // both variable names in the same file
2452            || (nd!=0 && md->getNamespaceDef()==nd) // both in same namespace
2453           )
2454           && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2455           && !md->isEnumerate() // in C# an enum value and enum can have the same name
2456          )
2457         // variable already in the scope
2458       {
2459         bool isPHPArray = md->getLanguage()==SrcLangExt_PHP &&
2460                           md->argsString()!=args &&
2461                           args.find('[')!=-1;
2462         bool staticsInDifferentFiles =
2463                           root->isStatic && md->isStatic() &&
2464                           root->fileName!=md->getDefFileName();
2465
2466         if (md->getFileDef() &&
2467             !isPHPArray && // not a php array
2468             !staticsInDifferentFiles
2469            )
2470           // not a php array variable
2471         {
2472           AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2473           addMemberDocs(root,md,def,0,FALSE,root->spec);
2474           md->setRefItems(root->sli);
2475           // if md is a variable forward declaration and root is the definition that
2476           // turn md into the definition
2477           if (!root->explicitExternal && md->isExternal())
2478           {
2479             md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2480             md->setExplicitExternal(FALSE,root->fileName,root->startLine,root->startColumn);
2481           }
2482           // if md is the definition and root point at a declaration, then add the
2483           // declaration info
2484           else if (root->explicitExternal && !md->isExternal())
2485           {
2486             md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2487           }
2488           return md;
2489         }
2490       }
2491     }
2492   }
2493
2494   QCString fileName = root->fileName;
2495   if (fileName.isEmpty() && root->tagInfo())
2496   {
2497     fileName = root->tagInfo()->tagName;
2498   }
2499
2500   AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2501   // new global variable, enum value or typedef
2502   auto md =  createMemberDef(
2503       fileName,root->startLine,root->startColumn,
2504       type,name,args,QCString(),
2505       root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2506       mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2507       root->argList, root->metaData);
2508   auto mmd = toMemberDefMutable(md.get());
2509   mmd->setTagInfo(root->tagInfo());
2510   mmd->setMemberSpecifiers(root->spec);
2511   mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2512   mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2513   mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2514   mmd->addSectionsToDefinition(root->anchors);
2515   mmd->setFromAnonymousScope(fromAnnScope);
2516   mmd->setFromAnonymousMember(fromAnnMemb);
2517   std::string init = root->initializer.str();
2518   mmd->setInitializer(init.c_str());
2519   mmd->setMaxInitLines(root->initLines);
2520   mmd->setMemberGroupId(root->mGrpId);
2521   mmd->setDefinition(def);
2522   mmd->setLanguage(root->lang);
2523   mmd->setId(root->id);
2524   mmd->enableCallGraph(root->callGraph);
2525   mmd->enableCallerGraph(root->callerGraph);
2526   mmd->enableReferencedByRelation(root->referencedByRelation);
2527   mmd->enableReferencesRelation(root->referencesRelation);
2528   mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2529   mmd->addQualifiers(root->qualifiers);
2530   //md->setOuterScope(fd);
2531   if (!root->explicitExternal)
2532   {
2533     mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2534     mmd->setBodyDef(fd);
2535   }
2536   addMemberToGroups(root,md.get());
2537   ModuleManager::instance().addMemberToModule(root,md.get());
2538
2539   mmd->setRefItems(root->sli);
2540   if (nd && !nd->isAnonymous())
2541   {
2542     mmd->setNamespace(nd);
2543     nd->insertMember(md.get());
2544   }
2545
2546   // add member to the file (we do this even if we have already inserted
2547   // it into the namespace.
2548   if (fd)
2549   {
2550     mmd->setFileDef(fd);
2551     fd->insertMember(md.get());
2552   }
2553
2554   root->markAsProcessed();
2555
2556   // add member definition to the list of globals
2557   MemberDef *result = md.get();
2558   mn = Doxygen::functionNameLinkedMap->add(name);
2559   mn->push_back(std::move(md));
2560
2561   return result;
2562 }
2563
2564 /*! See if the return type string \a type is that of a function pointer
2565  *  \returns -1 if this is not a function pointer variable or
2566  *           the index at which the closing brace of (...*name) was found.
2567  */
2568 static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=0)
2569 {
2570   AUTO_TRACE("type='{}' lang={}",type,lang);
2571   if (lang == SrcLangExt_Fortran || lang == SrcLangExt_VHDL)
2572   {
2573     return -1; // Fortran and VHDL do not have function pointers
2574   }
2575
2576   static const reg::Ex re(R"(\([^)]*[*^][^)]*\))");
2577   reg::Match match;
2578   size_t i=std::string::npos;
2579   size_t l=0;
2580   if (reg::search(type,match,re)) // contains (...*...)
2581   {
2582     i = match.position();
2583     l = match.length();
2584   }
2585   size_t bb=type.find('<');
2586   size_t be=type.rfind('>');
2587
2588   if (!type.empty()                            &&  // return type is non-empty
2589       i!=std::string::npos                     &&   // contains (...*...)
2590       type.find("operator")==std::string::npos &&   // not an operator
2591       (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2592                                                     // not a function pointer return type
2593       !(bb<i && i<be) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2594      )
2595   {
2596     if (pLength) *pLength=static_cast<int>(l);
2597     //printf("findFunctionPtr=%d\n",(int)i);
2598     AUTO_TRACE_EXIT("result={}",i);
2599     return static_cast<int>(i);
2600   }
2601   else
2602   {
2603     //printf("findFunctionPtr=%d\n",-1);
2604     AUTO_TRACE_EXIT("result=-1");
2605     return -1;
2606   }
2607 }
2608
2609
2610 /*! Returns TRUE iff \a type is a class within scope \a context.
2611  *  Used to detect variable declarations that look like function prototypes.
2612  */
2613 static bool isVarWithConstructor(const Entry *root)
2614 {
2615   bool result      = false;
2616   bool typeIsClass = false;
2617   bool typePtrType = false;
2618   QCString type;
2619   Definition *ctx = 0;
2620   FileDef *fd = root->fileDef();
2621   int ti;
2622   SymbolResolver resolver(fd);
2623
2624   AUTO_TRACE("isVarWithConstructor({})",root->name);
2625   if (root->parent()->section & Entry::COMPOUND_MASK)
2626   { // inside a class
2627     result=FALSE;
2628     AUTO_TRACE_EXIT("inside class: result={}",result);
2629     return result;
2630   }
2631   else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2632   { // inside a .c file
2633     result=FALSE;
2634     AUTO_TRACE_EXIT("inside C file: result={}",result);
2635     return result;
2636   }
2637   if (root->type.isEmpty())
2638   {
2639     result=FALSE;
2640     AUTO_TRACE_EXIT("no type: result={}",result);
2641     return result;
2642   }
2643   if (!root->parent()->name.isEmpty())
2644   {
2645     ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2646   }
2647   type = root->type;
2648   // remove qualifiers
2649   findAndRemoveWord(type,"const");
2650   findAndRemoveWord(type,"static");
2651   findAndRemoveWord(type,"volatile");
2652   typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2653   if (!typePtrType)
2654   {
2655     typeIsClass = resolver.resolveClass(ctx,type)!=0;
2656     if (!typeIsClass && (ti=type.find('<'))!=-1)
2657     {
2658       typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=0;
2659     }
2660   }
2661   if (typeIsClass) // now we still have to check if the arguments are
2662                    // types or values. Since we do not have complete type info
2663                    // we need to rely on heuristics :-(
2664   {
2665     if (root->argList.empty())
2666     {
2667       result=FALSE; // empty arg list -> function prototype.
2668       AUTO_TRACE_EXIT("empty arg list: result={}",result);
2669       return result;
2670     }
2671     for (const Argument &a : root->argList)
2672     {
2673       static const reg::Ex initChars(R"([\d"'&*!^]+)");
2674       reg::Match match;
2675       if (!a.name.isEmpty() || !a.defval.isEmpty())
2676       {
2677         std::string name = a.name.str();
2678         if (reg::search(name,match,initChars) && match.position()==0)
2679         {
2680           result=TRUE;
2681         }
2682         else
2683         {
2684           result=FALSE; // arg has (type,name) pair -> function prototype
2685         }
2686         AUTO_TRACE_EXIT("function prototype: result={}",result);
2687         return result;
2688       }
2689       if (!a.type.isEmpty() &&
2690           (a.type.at(a.type.length()-1)=='*' ||
2691            a.type.at(a.type.length()-1)=='&'))
2692            // type ends with * or & => pointer or reference
2693       {
2694         result=FALSE;
2695         AUTO_TRACE_EXIT("pointer or reference: result={}",result);
2696         return result;
2697       }
2698       if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=0)
2699       {
2700         result=FALSE; // arg type is a known type
2701         AUTO_TRACE_EXIT("known type: result={}",result);
2702         return result;
2703       }
2704       if (checkIfTypedef(ctx,fd,a.type))
2705       {
2706         result=FALSE; // argument is a typedef
2707         AUTO_TRACE_EXIT("typedef: result={}",result);
2708         return result;
2709       }
2710       std::string atype = a.type.str();
2711       if (reg::search(atype,match,initChars) && match.position()==0)
2712       {
2713         result=TRUE; // argument type starts with typical initializer char
2714         AUTO_TRACE_EXIT("argument with init char: result={}",result);
2715         return result;
2716       }
2717       std::string resType=resolveTypeDef(ctx,a.type).str();
2718       if (resType.empty()) resType=atype;
2719       static const reg::Ex idChars(R"(\a\w*)");
2720       if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2721       {
2722         resType=match.str();
2723         if (resType=="int"    || resType=="long"     ||
2724             resType=="float"  || resType=="double"   ||
2725             resType=="char"   || resType=="void"     ||
2726             resType=="signed" || resType=="unsigned" ||
2727             resType=="const"  || resType=="volatile" )
2728         {
2729           result=FALSE; // type keyword -> function prototype
2730           AUTO_TRACE_EXIT("type keyword: result={}",result);
2731           return result;
2732         }
2733       }
2734     }
2735     result=TRUE;
2736   }
2737
2738   AUTO_TRACE_EXIT("end: result={}",result);
2739   return result;
2740 }
2741
2742 static void addVariable(const Entry *root,int isFuncPtr=-1)
2743 {
2744   bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
2745
2746   AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
2747       root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
2748   //printf("root->parent->name=%s\n",qPrint(root->parent->name));
2749
2750   QCString type = root->type;
2751   QCString name = root->name;
2752   QCString args = root->args;
2753   if (type.isEmpty() && name.find("operator")==-1 &&
2754       (name.find('*')!=-1 || name.find('&')!=-1))
2755   {
2756     // recover from parse error caused by redundant braces
2757     // like in "int *(var[10]);", which is parsed as
2758     // type="" name="int *" args="(var[10])"
2759
2760     type=name;
2761     std::string sargs = args.str();
2762     static const reg::Ex reName(R"(\a\w*)");
2763     reg::Match match;
2764     if (reg::search(sargs,match,reName))
2765     {
2766       name  = match.str();           // e.g. 'var'  in '(var[10])'
2767       sargs = match.suffix().str();  // e.g. '[10]) in '(var[10])'
2768       size_t j = sargs.find(')');
2769       if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
2770     }
2771   }
2772   else
2773   {
2774     int i=isFuncPtr;
2775     if (i==-1 && (root->spec&Entry::Alias)==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
2776     AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
2777     if (i>=0) // function pointer
2778     {
2779       int ai = type.find('[',i);
2780       if (ai>i) // function pointer array
2781       {
2782         args.prepend(type.right(type.length()-ai));
2783         type=type.left(ai);
2784       }
2785       else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
2786       {
2787         type=type.left(type.length()-1);
2788         args.prepend(") ");
2789       }
2790     }
2791   }
2792   AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
2793
2794   QCString scope;
2795   name=removeRedundantWhiteSpace(name);
2796
2797   // find the scope of this variable
2798   int index = computeQualifiedIndex(name);
2799   if (index!=-1 && root->parent()->section==Entry::GROUPDOC_SEC && root->parent()->tagInfo())
2800     // grouped members are stored with full scope
2801   {
2802     buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
2803     scope=name.left(index);
2804     name=name.mid(index+2);
2805   }
2806   else
2807   {
2808     Entry *p = root->parent();
2809     while ((p->section & Entry::SCOPE_MASK))
2810     {
2811       QCString scopeName = p->name;
2812       if (!scopeName.isEmpty())
2813       {
2814         scope.prepend(scopeName);
2815         break;
2816       }
2817       p=p->parent();
2818     }
2819   }
2820
2821   MemberType mtype;
2822   type=type.stripWhiteSpace();
2823   ClassDefMutable *cd=0;
2824   bool isRelated=FALSE;
2825   bool isMemberOf=FALSE;
2826
2827   QCString classScope=stripAnonymousNamespaceScope(scope);
2828   classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
2829   QCString annScopePrefix=scope.left(scope.length()-classScope.length());
2830
2831   if (name.findRev("::")!=-1)
2832   {
2833     if (type=="friend class" || type=="friend struct" ||
2834         type=="friend union")
2835     {
2836       cd=getClassMutable(scope);
2837       if (cd)
2838       {
2839         addVariableToClass(root,  // entry
2840             cd,    // class to add member to
2841             MemberType_Friend, // type of member
2842             type,   // type value as string
2843             name,   // name of the member
2844             args,   // arguments as string
2845             FALSE,  // from Anonymous scope
2846             0,      // anonymous member
2847             Protection::Public,   // protection
2848             Relationship::Member  // related to a class
2849             );
2850       }
2851     }
2852     if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
2853     {
2854       Doxygen::staticInitMap.insert(std::make_pair(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine}));
2855     }
2856
2857
2858     AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
2859     return;  /* skip this member, because it is a
2860               * static variable definition (always?), which will be
2861               * found in a class scope as well, but then we know the
2862               * correct protection level, so only then it will be
2863               * inserted in the correct list!
2864               */
2865   }
2866
2867   if (type=="@")
2868     mtype=MemberType_EnumValue;
2869   else if (type.startsWith("typedef "))
2870     mtype=MemberType_Typedef;
2871   else if (type.startsWith("friend "))
2872     mtype=MemberType_Friend;
2873   else if (root->mtype==MethodTypes::Property)
2874     mtype=MemberType_Property;
2875   else if (root->mtype==MethodTypes::Event)
2876     mtype=MemberType_Event;
2877   else if (type.find("sequence<") != -1)
2878     mtype=sliceOpt ? MemberType_Sequence : MemberType_Typedef;
2879   else if (type.find("dictionary<") != -1)
2880     mtype=sliceOpt ? MemberType_Dictionary : MemberType_Typedef;
2881   else
2882     mtype=MemberType_Variable;
2883
2884   if (!root->relates.isEmpty()) // related variable
2885   {
2886     isRelated=TRUE;
2887     isMemberOf=(root->relatesType==RelatesType::MemberOf);
2888     if (getClass(root->relates)==0 && !scope.isEmpty())
2889       scope=mergeScopes(scope,root->relates);
2890     else
2891       scope=root->relates;
2892   }
2893
2894   cd=getClassMutable(scope);
2895   if (cd==0 && classScope!=scope) cd=getClassMutable(classScope);
2896   if (cd)
2897   {
2898     MemberDef *md=0;
2899
2900     // if cd is an anonymous (=tag less) scope we insert the member
2901     // into a non-anonymous parent scope as well. This is needed to
2902     // be able to refer to it using \var or \fn
2903
2904     //int indentDepth=0;
2905     int si=scope.find('@');
2906     //int anonyScopes = 0;
2907     //bool added=FALSE;
2908
2909     bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
2910     Relationship relationship = isMemberOf ? Relationship::Foreign :
2911       isRelated  ? Relationship::Related :
2912       Relationship::Member  ;
2913     if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
2914     {
2915       QCString pScope;
2916       ClassDefMutable *pcd=0;
2917       pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
2918       if (!pScope.isEmpty())
2919         pScope.prepend(annScopePrefix);
2920       else if (annScopePrefix.length()>2)
2921         pScope=annScopePrefix.left(annScopePrefix.length()-2);
2922       if (name.at(0)!='@')
2923       {
2924         if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
2925         {
2926           AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
2927           md=addVariableToClass(root,  // entry
2928               pcd,   // class to add member to
2929               mtype, // member type
2930               type,  // type value as string
2931               name,  // member name
2932               args,  // arguments as string
2933               TRUE,  // from anonymous scope
2934               0,     // from anonymous member
2935               root->protection,
2936               relationship
2937               );
2938           //added=TRUE;
2939         }
2940         else // anonymous scope inside namespace or file => put variable in the global scope
2941         {
2942           if (mtype==MemberType_Variable)
2943           {
2944             AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
2945             md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,0);
2946           }
2947           //added=TRUE;
2948         }
2949       }
2950     }
2951
2952     addVariableToClass(root,   // entry
2953         cd,     // class to add member to
2954         mtype,  // member type
2955         type,   // type value as string
2956         name,   // name of the member
2957         args,   // arguments as string
2958         FALSE,  // from anonymous scope
2959         md,     // from anonymous member
2960         root->protection,
2961         relationship
2962         );
2963   }
2964   else if (!name.isEmpty()) // global variable
2965   {
2966     addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*0,*/0);
2967   }
2968
2969 }
2970
2971 //----------------------------------------------------------------------
2972 // Searches the Entry tree for typedef documentation sections.
2973 // If found they are stored in their class or in the global list.
2974 static void buildTypedefList(const Entry *root)
2975 {
2976   //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
2977   if (!root->name.isEmpty() &&
2978       root->section==Entry::VARIABLE_SEC &&
2979       root->type.find("typedef ")!=-1 // its a typedef
2980      )
2981   {
2982     AUTO_TRACE();
2983     addVariable(root);
2984   }
2985   for (const auto &e : root->children())
2986     if (e->section!=Entry::ENUM_SEC)
2987       buildTypedefList(e.get());
2988 }
2989
2990 //----------------------------------------------------------------------
2991 // Searches the Entry tree for sequence documentation sections.
2992 // If found they are stored in the global list.
2993 static void buildSequenceList(const Entry *root)
2994 {
2995   if (!root->name.isEmpty() &&
2996       root->section==Entry::VARIABLE_SEC &&
2997       root->type.find("sequence<")!=-1 // it's a sequence
2998      )
2999   {
3000     AUTO_TRACE();
3001     addVariable(root);
3002   }
3003   for (const auto &e : root->children())
3004     if (e->section!=Entry::ENUM_SEC)
3005       buildSequenceList(e.get());
3006 }
3007
3008 //----------------------------------------------------------------------
3009 // Searches the Entry tree for dictionary documentation sections.
3010 // If found they are stored in the global list.
3011 static void buildDictionaryList(const Entry *root)
3012 {
3013   if (!root->name.isEmpty() &&
3014       root->section==Entry::VARIABLE_SEC &&
3015       root->type.find("dictionary<")!=-1 // it's a dictionary
3016      )
3017   {
3018     AUTO_TRACE();
3019     addVariable(root);
3020   }
3021   for (const auto &e : root->children())
3022     if (e->section!=Entry::ENUM_SEC)
3023       buildDictionaryList(e.get());
3024 }
3025
3026 //----------------------------------------------------------------------
3027 // Searches the Entry tree for Variable documentation sections.
3028 // If found they are stored in their class or in the global list.
3029
3030 static void buildVarList(const Entry *root)
3031 {
3032   //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3033   int isFuncPtr=-1;
3034   if (!root->name.isEmpty() &&
3035       (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3036       (
3037        (root->section==Entry::VARIABLE_SEC    // it's a variable
3038        ) ||
3039        (root->section==Entry::FUNCTION_SEC && // or maybe a function pointer variable
3040         (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3041        ) ||
3042        (root->section==Entry::FUNCTION_SEC && // class variable initialized by constructor
3043         isVarWithConstructor(root)
3044        )
3045       )
3046      ) // documented variable
3047   {
3048     AUTO_TRACE();
3049     addVariable(root,isFuncPtr);
3050   }
3051   for (const auto &e : root->children())
3052     if (e->section!=Entry::ENUM_SEC)
3053       buildVarList(e.get());
3054 }
3055
3056 //----------------------------------------------------------------------
3057 // Searches the Entry tree for Interface sections (UNO IDL only).
3058 // If found they are stored in their service or in the global list.
3059 //
3060
3061 static void addInterfaceOrServiceToServiceOrSingleton(
3062         const Entry *root,
3063         ClassDefMutable *cd,
3064         QCString const& rname)
3065 {
3066   FileDef *fd = root->fileDef();
3067   enum MemberType type = (root->section==Entry::EXPORTED_INTERFACE_SEC)
3068       ? MemberType_Interface
3069       : MemberType_Service;
3070   QCString fileName = root->fileName;
3071   if (fileName.isEmpty() && root->tagInfo())
3072   {
3073     fileName = root->tagInfo()->tagName;
3074   }
3075   auto md = createMemberDef(
3076       fileName, root->startLine, root->startColumn, root->type, rname,
3077       "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3078       type, ArgumentList(), root->argList, root->metaData);
3079   auto mmd = toMemberDefMutable(md.get());
3080   mmd->setTagInfo(root->tagInfo());
3081   mmd->setMemberClass(cd);
3082   mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3083   mmd->setDocsForDefinition(false);
3084   mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3085   mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3086   mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3087   mmd->setMemberSpecifiers(root->spec);
3088   mmd->setMemberGroupId(root->mGrpId);
3089   mmd->setTypeConstraints(root->typeConstr);
3090   mmd->setLanguage(root->lang);
3091   mmd->setBodyDef(fd);
3092   mmd->setFileDef(fd);
3093   mmd->addSectionsToDefinition(root->anchors);
3094   QCString const def = root->type + " " + rname;
3095   mmd->setDefinition(def);
3096   mmd->enableCallGraph(root->callGraph);
3097   mmd->enableCallerGraph(root->callerGraph);
3098   mmd->enableReferencedByRelation(root->referencedByRelation);
3099   mmd->enableReferencesRelation(root->referencesRelation);
3100   mmd->addQualifiers(root->qualifiers);
3101
3102   AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3103       fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3104
3105   // add member to the class cd
3106   cd->insertMember(md.get());
3107   // also add the member as a "base" (to get nicer diagrams)
3108   // "optional" interface/service get Protected which turns into dashed line
3109   BaseInfo base(rname,
3110           (root->spec & (Entry::Optional)) ? Protection::Protected : Protection::Public, Specifier::Normal);
3111   TemplateNameMap templateNames;
3112   findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3113        findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3114   // add file to list of used files
3115   cd->insertUsedFile(fd);
3116
3117   addMemberToGroups(root,md.get());
3118   ModuleManager::instance().addMemberToModule(root,md.get());
3119   root->markAsProcessed();
3120   mmd->setRefItems(root->sli);
3121
3122   // add member to the global list of all members
3123   MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3124   mn->push_back(std::move(md));
3125 }
3126
3127 static void buildInterfaceAndServiceList(const Entry *root)
3128 {
3129   if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
3130       root->section==Entry::INCLUDED_SERVICE_SEC)
3131   {
3132     AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3133                " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3134                " mGrpId={} spec={:#x} proto={} docFile='{}'",
3135                  root->type, root->parent()->name, root->name, root->args,
3136                  root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3137                  root->mGrpId, root->spec, root->proto, root->docFile);
3138
3139     QCString rname = removeRedundantWhiteSpace(root->name);
3140
3141     if (!rname.isEmpty())
3142     {
3143       QCString scope = root->parent()->name;
3144       ClassDefMutable *cd = getClassMutable(scope);
3145       assert(cd);
3146       if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3147                  (ClassDef::Service   == cd->compoundType()) ||
3148                  (ClassDef::Singleton == cd->compoundType())))
3149       {
3150         addInterfaceOrServiceToServiceOrSingleton(root,cd,rname);
3151       }
3152       else
3153       {
3154         assert(false); // was checked by scanner.l
3155       }
3156     }
3157     else if (rname.isEmpty())
3158     {
3159       warn(root->fileName,root->startLine,
3160            "Illegal member name found.");
3161     }
3162   }
3163   // can only have these in IDL anyway
3164   switch (root->lang)
3165   {
3166     case SrcLangExt_Unknown: // fall through (root node always is Unknown)
3167     case SrcLangExt_IDL:
3168         for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3169         break;
3170     default:
3171         return; // nothing to do here
3172   }
3173 }
3174
3175
3176 //----------------------------------------------------------------------
3177 // Searches the Entry tree for Function sections.
3178 // If found they are stored in their class or in the global list.
3179
3180 static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3181                   const QCString &rtype,const QCString &rname,const QCString &rargs,
3182                   bool isFriend,
3183                   Protection protection,bool stat,Specifier virt,uint64_t spec,
3184                   const QCString &relates
3185                   )
3186 {
3187   FileDef *fd=root->fileDef();
3188
3189   QCString type = rtype;
3190   QCString args = rargs;
3191
3192   QCString name=removeRedundantWhiteSpace(rname);
3193   name.stripPrefix("::");
3194
3195   MemberType mtype;
3196   if (isFriend)                              mtype=MemberType_Friend;
3197   else if (root->mtype==MethodTypes::Signal) mtype=MemberType_Signal;
3198   else if (root->mtype==MethodTypes::Slot)   mtype=MemberType_Slot;
3199   else if (root->mtype==MethodTypes::DCOP)   mtype=MemberType_DCOP;
3200   else                                       mtype=MemberType_Function;
3201
3202   // strip redundant template specifier for constructors
3203   int i = -1;
3204   int j = -1;
3205   if ((fd==0 || fd->getLanguage()==SrcLangExt_Cpp) &&
3206       !name.startsWith("operator ") &&   // not operator
3207       (i=name.find('<'))!=-1    &&   // containing <
3208       (j=name.find('>'))!=-1    &&   // or >
3209       (j!=i+2 || name.at(i+1)!='=')  // but not the C++20 spaceship operator <=>
3210      )
3211   {
3212     name=name.left(i);
3213   }
3214
3215   QCString fileName = root->fileName;
3216   if (fileName.isEmpty() && root->tagInfo())
3217   {
3218     fileName = root->tagInfo()->tagName;
3219   }
3220
3221   //printf("root->name='%s; args='%s' root->argList='%s'\n",
3222   //    qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3223   //   );
3224
3225   // adding class member
3226   Relationship relationship = relates.isEmpty() ?                        Relationship::Member  :
3227                               root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3228                                                                          Relationship::Related ;
3229   auto md = createMemberDef(
3230       fileName,root->startLine,root->startColumn,
3231       type,name,args,root->exception,
3232       protection,virt,
3233       stat && root->relatesType!=RelatesType::MemberOf,
3234       relationship,
3235       mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3236       root->argList, root->metaData);
3237   auto mmd = toMemberDefMutable(md.get());
3238   mmd->setTagInfo(root->tagInfo());
3239   mmd->setMemberClass(cd);
3240   mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3241   mmd->setDocsForDefinition(!root->proto);
3242   mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3243   mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3244   mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3245   mmd->setMemberSpecifiers(spec);
3246   mmd->setMemberGroupId(root->mGrpId);
3247   mmd->setTypeConstraints(root->typeConstr);
3248   mmd->setLanguage(root->lang);
3249   mmd->setRequiresClause(root->req);
3250   mmd->setId(root->id);
3251   mmd->setBodyDef(fd);
3252   mmd->setFileDef(fd);
3253   mmd->addSectionsToDefinition(root->anchors);
3254   QCString def;
3255   QCString qualScope = cd->qualifiedNameWithTemplateParameters();
3256   SrcLangExt lang = cd->getLanguage();
3257   QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3258   if (scopeSeparator!="::")
3259   {
3260     qualScope = substitute(qualScope,"::",scopeSeparator);
3261   }
3262   if (lang==SrcLangExt_PHP)
3263   {
3264     // for PHP we use Class::method and Namespace\method
3265     scopeSeparator="::";
3266   }
3267 //  QCString optArgs = root->argList.empty() ? args : QCString();
3268   if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3269   {
3270     if (!type.isEmpty())
3271     {
3272       def=type+" "+name; //+optArgs;
3273     }
3274     else
3275     {
3276       def=name; //+optArgs;
3277     }
3278   }
3279   else
3280   {
3281     if (!type.isEmpty())
3282     {
3283       def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3284     }
3285     else
3286     {
3287       def=qualScope+scopeSeparator+name; //+optArgs;
3288     }
3289   }
3290   def.stripPrefix("friend ");
3291   mmd->setDefinition(def);
3292   mmd->enableCallGraph(root->callGraph);
3293   mmd->enableCallerGraph(root->callerGraph);
3294   mmd->enableReferencedByRelation(root->referencedByRelation);
3295   mmd->enableReferencesRelation(root->referencesRelation);
3296   mmd->addQualifiers(root->qualifiers);
3297
3298   AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3299              type, qualScope, rname, args, root->proto, def);
3300
3301   // add member to the class cd
3302   cd->insertMember(md.get());
3303   // add file to list of used files
3304   cd->insertUsedFile(fd);
3305
3306   addMemberToGroups(root,md.get());
3307   ModuleManager::instance().addMemberToModule(root,md.get());
3308   root->markAsProcessed();
3309   mmd->setRefItems(root->sli);
3310
3311   // add member to the global list of all members
3312   //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3313   MemberName *mn = Doxygen::memberNameLinkedMap->add(name);
3314   mn->push_back(std::move(md));
3315 }
3316
3317 //------------------------------------------------------------------------------------------
3318
3319 static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3320 {
3321   QCString scope = sc;
3322
3323   // new global function
3324   QCString name=removeRedundantWhiteSpace(rname);
3325   auto md = createMemberDef(
3326       root->fileName,root->startLine,root->startColumn,
3327       root->type,name,root->args,root->exception,
3328       root->protection,root->virt,root->isStatic,Relationship::Member,
3329       MemberType_Function,
3330       !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3331       root->argList,root->metaData);
3332   auto mmd = toMemberDefMutable(md.get());
3333   mmd->setTagInfo(root->tagInfo());
3334   mmd->setLanguage(root->lang);
3335   mmd->setId(root->id);
3336   mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3337   mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3338   mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3339   mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3340   mmd->setDocsForDefinition(!root->proto);
3341   mmd->setTypeConstraints(root->typeConstr);
3342   //md->setBody(root->body);
3343   mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3344   FileDef *fd=root->fileDef();
3345   mmd->setBodyDef(fd);
3346   mmd->addSectionsToDefinition(root->anchors);
3347   mmd->setMemberSpecifiers(root->spec);
3348   mmd->setMemberGroupId(root->mGrpId);
3349   mmd->setRequiresClause(root->req);
3350   mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3351
3352   NamespaceDefMutable *nd = 0;
3353   // see if the function is inside a namespace that was not part of
3354   // the name already (in that case nd should be non-zero already)
3355   if (root->parent()->section == Entry::NAMESPACE_SEC )
3356   {
3357     //QCString nscope=removeAnonymousScopes(root->parent()->name);
3358     QCString nscope=root->parent()->name;
3359     if (!nscope.isEmpty())
3360     {
3361       nd = getResolvedNamespaceMutable(nscope);
3362     }
3363   }
3364   else if (root->parent()->section==Entry::GROUPDOC_SEC && !scope.isEmpty())
3365   {
3366     nd = getResolvedNamespaceMutable(sc);
3367   }
3368
3369   if (!scope.isEmpty())
3370   {
3371     QCString sep = getLanguageSpecificSeparator(root->lang);
3372     if (sep!="::")
3373     {
3374       scope = substitute(scope,"::",sep);
3375     }
3376     scope+=sep;
3377   }
3378
3379   if (Config_getBool(HIDE_SCOPE_NAMES)) scope = "";
3380   QCString def;
3381   //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3382   if (!root->type.isEmpty())
3383   {
3384     def=root->type+" "+scope+name; //+optArgs;
3385   }
3386   else
3387   {
3388     def=scope+name; //+optArgs;
3389   }
3390   AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3391               root->type,scope,rname,root->args,root->proto,def);
3392   mmd->setDefinition(def);
3393   mmd->enableCallGraph(root->callGraph);
3394   mmd->enableCallerGraph(root->callerGraph);
3395   mmd->enableReferencedByRelation(root->referencedByRelation);
3396   mmd->enableReferencesRelation(root->referencesRelation);
3397   mmd->addQualifiers(root->qualifiers);
3398
3399   mmd->setRefItems(root->sli);
3400   if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3401   {
3402     // add member to namespace
3403     mmd->setNamespace(nd);
3404     nd->insertMember(md.get());
3405   }
3406   if (fd)
3407   {
3408     // add member to the file (we do this even if we have already
3409     // inserted it into the namespace)
3410     mmd->setFileDef(fd);
3411     fd->insertMember(md.get());
3412   }
3413
3414   addMemberToGroups(root,md.get());
3415   ModuleManager::instance().addMemberToModule(root,md.get());
3416   if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3417                                                 // allow find Member to pick it up
3418   {
3419     root->markAsProcessed(); // Otherwise we have finished with this entry.
3420   }
3421
3422   // add member to the list of file members
3423   MemberName *mn = Doxygen::functionNameLinkedMap->add(name);
3424   mn->push_back(std::move(md));
3425 }
3426
3427 //------------------------------------------------------------------------------------------
3428
3429 static void buildFunctionList(const Entry *root)
3430 {
3431   if (root->section==Entry::FUNCTION_SEC)
3432   {
3433     AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3434                " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3435                " spec={:#x} proto={} docFile='{}'",
3436                root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3437                root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3438                root->spec, root->proto, root->docFile);
3439
3440     bool isFriend=root->type.find("friend ")!=-1;
3441     QCString rname = removeRedundantWhiteSpace(root->name);
3442     //printf("rname=%s\n",qPrint(rname));
3443
3444     QCString scope;
3445     int index = computeQualifiedIndex(rname);
3446     if (index!=-1 && root->parent()->section==Entry::GROUPDOC_SEC && root->parent()->tagInfo())
3447       // grouped members are stored with full scope
3448     {
3449       buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3450       scope=rname.left(index);
3451       rname=rname.mid(index+2);
3452     }
3453     else
3454     {
3455       scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3456     }
3457     if (!rname.isEmpty() && scope.find('@')==-1)
3458     {
3459       ClassDefMutable *cd=0;
3460       // check if this function's parent is a class
3461       scope=stripTemplateSpecifiersFromScope(scope,FALSE);
3462
3463       FileDef *rfd=root->fileDef();
3464
3465       int memIndex=rname.findRev("::");
3466
3467       cd=getClassMutable(scope);
3468       if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3469       {
3470         // strip scope from name
3471         rname=rname.right(rname.length()-root->parent()->name.length()-2);
3472       }
3473
3474       bool isMember=FALSE;
3475       if (memIndex!=-1)
3476       {
3477         int ts=rname.find('<');
3478         int te=rname.find('>');
3479         if (memIndex>0 && (ts==-1 || te==-1))
3480         {
3481           // note: the following code was replaced by inMember=TRUE to deal with a
3482           // function rname='X::foo' of class X inside a namespace also called X...
3483           // bug id 548175
3484           //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3485           //isMember = nd==0;
3486           //if (nd)
3487           //{
3488           //  // strip namespace scope from name
3489           //  scope=rname.left(memIndex);
3490           //  rname=rname.right(rname.length()-memIndex-2);
3491           //}
3492           isMember = TRUE;
3493         }
3494         else
3495         {
3496           isMember=memIndex<ts || memIndex>te;
3497         }
3498       }
3499
3500       if (!root->parent()->name.isEmpty() &&
3501           (root->parent()->section & Entry::COMPOUND_MASK) &&
3502           cd
3503          )
3504       {
3505         AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
3506         addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3507                          root->protection,root->isStatic,root->virt,root->spec,root->relates);
3508       }
3509       else if (!((root->parent()->section & Entry::COMPOUND_MASK)
3510                  || root->parent()->section==Entry::OBJCIMPL_SEC
3511                 ) &&
3512                !isMember &&
3513                (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
3514                !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
3515               )
3516       // no member => unrelated function
3517       {
3518         /* check the uniqueness of the function name in the file.
3519          * A file could contain a function prototype and a function definition
3520          * or even multiple function prototypes.
3521          */
3522         bool found=FALSE;
3523         MemberName *mn;
3524         MemberDef *md_found=0;
3525         if ((mn=Doxygen::functionNameLinkedMap->find(rname)))
3526         {
3527           AUTO_TRACE_ADD("function '{}' already found",rname);
3528           for (const auto &imd : *mn)
3529           {
3530             MemberDefMutable *md = toMemberDefMutable(imd.get());
3531             if (md)
3532             {
3533               const NamespaceDef *mnd = md->getNamespaceDef();
3534               NamespaceDef *rnd = 0;
3535               //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
3536               QCString fullScope = scope;
3537               QCString parentScope = root->parent()->name;
3538               if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
3539               {
3540                 if (!scope.isEmpty()) fullScope.prepend("::");
3541                 fullScope.prepend(parentScope);
3542               }
3543               //printf("fullScope=%s\n",qPrint(fullScope));
3544               rnd = getResolvedNamespace(fullScope);
3545               const FileDef *mfd = md->getFileDef();
3546               QCString nsName,rnsName;
3547               if (mnd)  nsName = mnd->name();
3548               if (rnd) rnsName = rnd->name();
3549               //printf("matching arguments for %s%s %s%s\n",
3550               //    qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
3551               const ArgumentList &mdAl = md->argumentList();
3552               const ArgumentList &mdTempl = md->templateArguments();
3553
3554               // in case of template functions, we need to check if the
3555               // functions have the same number of template parameters
3556               bool sameNumTemplateArgs = TRUE;
3557               bool matchingReturnTypes = TRUE;
3558               bool sameRequiresClause = TRUE;
3559               if (!mdTempl.empty() && !root->tArgLists.empty())
3560               {
3561                 if (mdTempl.size()!=root->tArgLists.back().size())
3562                 {
3563                   sameNumTemplateArgs = FALSE;
3564                 }
3565                 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
3566                 {
3567                   matchingReturnTypes = FALSE;
3568                 }
3569                 if (md->requiresClause()!=root->req)
3570                 {
3571                   sameRequiresClause = FALSE;
3572                 }
3573               }
3574               else if (!mdTempl.empty() || !root->tArgLists.empty())
3575               { // if one has template parameters and the other doesn't then that also counts as a
3576                 // difference
3577                 sameNumTemplateArgs = FALSE;
3578               }
3579
3580               bool staticsInDifferentFiles =
3581                 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
3582
3583               if (
3584                   matchArguments2(md->getOuterScope(),mfd,&mdAl,
3585                     rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
3586                     FALSE,root->lang) &&
3587                   sameNumTemplateArgs &&
3588                   matchingReturnTypes &&
3589                   sameRequiresClause &&
3590                   !staticsInDifferentFiles
3591                  )
3592               {
3593                 GroupDef *gd=0;
3594                 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
3595                 {
3596                   gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
3597                 }
3598                 //printf("match!\n");
3599                 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
3600                 // see if we need to create a new member
3601                 found=(mnd && rnd && nsName==rnsName) ||   // members are in the same namespace
3602                   ((mnd==0 && rnd==0 && mfd!=0 &&       // no external reference and
3603                     mfd->absFilePath()==root->fileName // prototype in the same file
3604                    )
3605                   );
3606                 // otherwise, allow a duplicate global member with the same argument list
3607                 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
3608                 {
3609                   // member is already in the group, so we don't want to add it again.
3610                   found=TRUE;
3611                 }
3612
3613                 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
3614
3615                 if (found)
3616                 {
3617                   // merge argument lists
3618                   ArgumentList mergedArgList = root->argList;
3619                   mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
3620                   // merge documentation
3621                   if (md->documentation().isEmpty() && !root->doc.isEmpty())
3622                   {
3623                     if (root->proto)
3624                     {
3625                       md->moveDeclArgumentList(stringToArgumentList(root->lang,root->args));
3626                     }
3627                     else
3628                     {
3629                       md->moveArgumentList(stringToArgumentList(root->lang,root->args));
3630                     }
3631                   }
3632
3633                   md->setDocumentation(root->doc,root->docFile,root->docLine);
3634                   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3635                   md->setDocsForDefinition(!root->proto);
3636                   if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
3637                   {
3638                     md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3639                     md->setBodyDef(rfd);
3640                   }
3641
3642                   if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
3643                   {
3644                     md->setArgsString(root->args);
3645                   }
3646                   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3647
3648                   md->addSectionsToDefinition(root->anchors);
3649
3650                   md->enableCallGraph(md->hasCallGraph() || root->callGraph);
3651                   md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
3652                   md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
3653                   md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
3654                   md->addQualifiers(root->qualifiers);
3655
3656                   // merge ingroup specifiers
3657                   if (md->getGroupDef()==0 && !root->groups.empty())
3658                   {
3659                     addMemberToGroups(root,md);
3660                   }
3661                   else if (md->getGroupDef()!=0 && root->groups.empty())
3662                   {
3663                     //printf("existing member is grouped, new member not\n");
3664                   }
3665                   else if (md->getGroupDef()!=0 && !root->groups.empty())
3666                   {
3667                     //printf("both members are grouped\n");
3668                   }
3669                   ModuleManager::instance().addMemberToModule(root,md);
3670
3671                   // if md is a declaration and root is the corresponding
3672                   // definition, then turn md into a definition.
3673                   if (md->isPrototype() && !root->proto)
3674                   {
3675                     md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
3676                     md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
3677                   }
3678                   // if md is already the definition, then add the declaration info
3679                   else if (!md->isPrototype() && root->proto)
3680                   {
3681                     md->setDeclFile(root->fileName,root->startLine,root->startColumn);
3682                   }
3683                 }
3684               }
3685             }
3686             if (found)
3687             {
3688               md_found = md;
3689               break;
3690             }
3691           }
3692         }
3693         if (!found) /* global function is unique with respect to the file */
3694         {
3695           addGlobalFunction(root,rname,scope);
3696         }
3697         else
3698         {
3699           FileDef *fd=root->fileDef();
3700           if (fd)
3701           {
3702             // add member to the file (we do this even if we have already
3703             // inserted it into the namespace)
3704             fd->insertMember(md_found);
3705           }
3706         }
3707
3708         AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
3709       }
3710       else
3711       {
3712         AUTO_TRACE_ADD("function '{}' is not processed",rname);
3713       }
3714     }
3715     else if (rname.isEmpty())
3716     {
3717         warn(root->fileName,root->startLine,
3718              "Illegal member name found."
3719             );
3720     }
3721   }
3722   for (const auto &e : root->children()) buildFunctionList(e.get());
3723 }
3724
3725 //----------------------------------------------------------------------
3726
3727 static void findFriends()
3728 {
3729   AUTO_TRACE();
3730   for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
3731   {
3732     MemberName *mn;
3733     if ((mn=Doxygen::memberNameLinkedMap->find(fn->memberName())))
3734     { // there are members with the same name
3735       // for each function with that name
3736       for (const auto &ifmd : *fn)
3737       {
3738         MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
3739         // for each member with that name
3740         for (const auto &immd : *mn)
3741         {
3742            MemberDefMutable *mmd = toMemberDefMutable(immd.get());
3743           //printf("Checking for matching arguments
3744           //        mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
3745           //    mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
3746           if (fmd && mmd &&
3747               (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
3748               matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
3749                               fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
3750                               TRUE,mmd->getLanguage()
3751                              )
3752
3753              ) // if the member is related and the arguments match then the
3754                // function is actually a friend.
3755           {
3756             AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
3757                 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
3758             const ArgumentList &mmdAl = mmd->argumentList();
3759             const ArgumentList &fmdAl = fmd->argumentList();
3760             mergeArguments(const_cast<ArgumentList&>(mmdAl),const_cast<ArgumentList&>(fmdAl));
3761             if (!fmd->documentation().isEmpty())
3762             {
3763               mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
3764             }
3765             else if (!mmd->documentation().isEmpty())
3766             {
3767               fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
3768             }
3769             if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3770             {
3771               mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
3772             }
3773             else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3774             {
3775               fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
3776             }
3777             if (!fmd->inbodyDocumentation().isEmpty())
3778             {
3779               mmd->setInbodyDocumentation(fmd->inbodyDocumentation(),fmd->inbodyFile(),fmd->inbodyLine());
3780             }
3781             else if (!mmd->inbodyDocumentation().isEmpty())
3782             {
3783               fmd->setInbodyDocumentation(mmd->inbodyDocumentation(),mmd->inbodyFile(),mmd->inbodyLine());
3784             }
3785             //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
3786             if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
3787             {
3788               mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
3789               mmd->setBodyDef(fmd->getBodyDef());
3790               //mmd->setBodyMember(fmd);
3791             }
3792             else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
3793             {
3794               fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
3795               fmd->setBodyDef(mmd->getBodyDef());
3796               //fmd->setBodyMember(mmd);
3797             }
3798             mmd->setDocsForDefinition(fmd->isDocsForDefinition());
3799
3800             mmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3801             mmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3802             mmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3803             mmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3804             mmd->addQualifiers(fmd->getQualifiers());
3805
3806             fmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3807             fmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3808             fmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3809             fmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3810             fmd->addQualifiers(mmd->getQualifiers());
3811           }
3812         }
3813       }
3814     }
3815   }
3816 }
3817
3818 //----------------------------------------------------------------------
3819
3820 static void transferFunctionDocumentation()
3821 {
3822   AUTO_TRACE();
3823
3824   // find matching function declaration and definitions.
3825   for (const auto &mn : *Doxygen::functionNameLinkedMap)
3826   {
3827     //printf("memberName=%s count=%d\n",mn->memberName(),mn->count());
3828     /* find a matching function declaration and definition for this function */
3829     for (const auto &imdec : *mn)
3830     {
3831       MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
3832       if (mdec &&
3833           (mdec->isPrototype() ||
3834            (mdec->isVariable() && mdec->isExternal())
3835          ))
3836       {
3837         for (const auto &imdef : *mn)
3838         {
3839           MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
3840           if (mdef && mdec!=mdef &&
3841               mdec->getNamespaceDef()==mdef->getNamespaceDef())
3842           {
3843             combineDeclarationAndDefinition(mdec,mdef);
3844           }
3845         }
3846       }
3847     }
3848   }
3849 }
3850
3851 //----------------------------------------------------------------------
3852
3853 static void transferFunctionReferences()
3854 {
3855   AUTO_TRACE();
3856   for (const auto &mn : *Doxygen::functionNameLinkedMap)
3857   {
3858     MemberDefMutable *mdef=0,*mdec=0;
3859     /* find a matching function declaration and definition for this function */
3860     for (const auto &imd : *mn)
3861     {
3862       MemberDefMutable *md = toMemberDefMutable(imd.get());
3863       if (md)
3864       {
3865         if (md->isPrototype())
3866           mdec=md;
3867         else if (md->isVariable() && md->isExternal())
3868           mdec=md;
3869
3870         if (md->isFunction() && !md->isStatic() && !md->isPrototype())
3871           mdef=md;
3872         else if (md->isVariable() && !md->isExternal() && !md->isStatic())
3873           mdef=md;
3874       }
3875
3876       if (mdef && mdec) break;
3877     }
3878     if (mdef && mdec)
3879     {
3880       const ArgumentList &mdefAl = mdef->argumentList();
3881       const ArgumentList &mdecAl = mdec->argumentList();
3882       if (
3883           matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
3884                           mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
3885                           TRUE,mdef->getLanguage()
3886             )
3887          ) /* match found */
3888       {
3889         AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
3890         mdef->mergeReferences(mdec);
3891         mdec->mergeReferences(mdef);
3892         mdef->mergeReferencedBy(mdec);
3893         mdec->mergeReferencedBy(mdef);
3894       }
3895     }
3896   }
3897 }
3898
3899 //----------------------------------------------------------------------
3900
3901 static void transferRelatedFunctionDocumentation()
3902 {
3903   AUTO_TRACE();
3904   // find match between function declaration and definition for
3905   // related functions
3906   for (const auto &mn : *Doxygen::functionNameLinkedMap)
3907   {
3908     /* find a matching function declaration and definition for this function */
3909     // for each global function
3910     for (const auto &imd : *mn)
3911     {
3912       MemberDefMutable *md = toMemberDefMutable(imd.get());
3913       if (md)
3914       {
3915         //printf("  Function '%s'\n",qPrint(md->name()));
3916         MemberName *rmn;
3917         if ((rmn=Doxygen::memberNameLinkedMap->find(md->name()))) // check if there is a member with the same name
3918         {
3919           //printf("  Member name found\n");
3920           // for each member with the same name
3921           for (const auto &irmd : *rmn)
3922           {
3923             MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
3924             //printf("  Member found: related='%d'\n",rmd->isRelated());
3925             if (rmd &&
3926                 (rmd->isRelated() || rmd->isForeign()) && // related function
3927                 matchArguments2( md->getOuterScope(), md->getFileDef(), &md->argumentList(),
3928                   rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
3929                   TRUE,md->getLanguage()
3930                   )
3931                )
3932             {
3933               AUTO_TRACE_ADD("Found related member '{}'",md->name());
3934               if (rmd->relatedAlso())
3935                 md->setRelatedAlso(rmd->relatedAlso());
3936               else if (rmd->isForeign())
3937                 md->makeForeign();
3938               else
3939                 md->makeRelated();
3940             }
3941           }
3942         }
3943       }
3944     }
3945   }
3946 }
3947
3948 //----------------------------------------------------------------------
3949
3950 void transferStaticInstanceInitializers()
3951 {
3952   AUTO_TRACE();
3953   for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
3954   {
3955     size_t i=qualifiedName.rfind("::");
3956     if (i!=std::string::npos)
3957     {
3958       QCString scope = qualifiedName.substr(0,i);
3959       QCString name  = qualifiedName.substr(i+2);
3960       MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
3961       if (mn)
3962       {
3963         for (const auto &imd : *mn)
3964         {
3965           MemberDefMutable *md = toMemberDefMutable(imd.get());
3966           if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
3967           {
3968             AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
3969               md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
3970             md->setBodySegment(bodyInfo.defLine,
3971                                bodyInfo.startLine,
3972                                bodyInfo.endLine);
3973           }
3974         }
3975       }
3976     }
3977   }
3978 }
3979
3980 //----------------------------------------------------------------------
3981
3982 /*! make a dictionary of all template arguments of class cd
3983  * that are part of the base class name.
3984  * Example: A template class A with template arguments <R,S,T>
3985  * that inherits from B<T,T,S> will have T and S in the dictionary.
3986  */
3987 static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
3988 {
3989   std::map<std::string,int> templateNames;
3990   int count=0;
3991   for (const Argument &arg : templateArguments)
3992   {
3993     static const reg::Ex re(R"(\a[\w:]*)");
3994     reg::Iterator it(name,re);
3995     reg::Iterator end;
3996     for (; it!=end ; ++it)
3997     {
3998       const auto &match = *it;
3999       std::string n = match.str();
4000       if (n==arg.name.str())
4001       {
4002         if (templateNames.find(n)==templateNames.end())
4003         {
4004           templateNames.insert(std::make_pair(n,count));
4005         }
4006       }
4007     }
4008   }
4009   return templateNames;
4010 }
4011
4012 /*! Searches a class from within \a context and \a cd and returns its
4013  *  definition if found (otherwise 0 is returned).
4014  */
4015 static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,const QCString &name)
4016 {
4017   ClassDef *result=0;
4018   if (cd==0)
4019   {
4020     return result;
4021   }
4022   FileDef *fd=cd->getFileDef();
4023   SymbolResolver resolver(fd);
4024   if (context && cd!=context)
4025   {
4026     result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4027   }
4028   //printf("1. result=%p\n",result);
4029   if (result==0)
4030   {
4031     result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4032   }
4033   //printf("2. result=%p\n",result);
4034   if (result==0) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4035   {
4036     result = getClass(name);
4037   }
4038   //printf("3. result=%p\n",result);
4039   //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4040   //       qPrint(name),
4041   //       context ? qPrint(context->name()) : "<none>",
4042   //       cd      ? qPrint(cd->name())      : "<none>",
4043   //       result  ? qPrint(result->name())  : "<none>",
4044   //       Doxygen::classLinkedMap->find(name)
4045   //      );
4046   return result;
4047 }
4048
4049
4050 static void findUsedClassesForClass(const Entry *root,
4051                            Definition *context,
4052                            ClassDefMutable *masterCd,
4053                            ClassDefMutable *instanceCd,
4054                            bool isArtificial,
4055                            const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4056                            const TemplateNameMap &templateNames = TemplateNameMap()
4057                            )
4058 {
4059   AUTO_TRACE();
4060   const ArgumentList &formalArgs = masterCd->templateArguments();
4061   for (auto &mni : masterCd->memberNameInfoLinkedMap())
4062   {
4063     for (auto &mi : *mni)
4064     {
4065       const MemberDef *md=mi->memberDef();
4066       if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4067       {
4068         AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4069         QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4070         QCString typedefValue = md->getLanguage()==SrcLangExt_Java ? type : resolveTypeDef(masterCd,type);
4071         if (!typedefValue.isEmpty())
4072         {
4073           type = typedefValue;
4074         }
4075         int pos=0;
4076         QCString usedClassName;
4077         QCString templSpec;
4078         bool found=FALSE;
4079         // the type can contain template variables, replace them if present
4080         type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4081
4082         //printf("      template substitution gives=%s\n",qPrint(type));
4083         while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4084         {
4085           // find the type (if any) that matches usedClassName
4086           SymbolResolver resolver(masterCd->getFileDef());
4087           const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4088           //printf("====>  usedClassName=%s -> typeCd=%s\n",
4089           //     qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4090           if (typeCd)
4091           {
4092             usedClassName = typeCd->name();
4093           }
4094
4095           int sp=usedClassName.find('<');
4096           if (sp==-1) sp=0;
4097           int si=usedClassName.findRev("::",sp);
4098           if (si!=-1)
4099           {
4100             // replace any namespace aliases
4101             replaceNamespaceAliases(usedClassName,si);
4102           }
4103           // add any template arguments to the class
4104           QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4105           //printf("    usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4106
4107           TemplateNameMap formTemplateNames;
4108           if (templateNames.empty())
4109           {
4110             formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4111           }
4112           BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4113           findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4114
4115           for (const Argument &arg : masterCd->templateArguments())
4116           {
4117             if (arg.name==usedName) // type is a template argument
4118             {
4119               ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4120               ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4121               if (usedCd==0)
4122               {
4123                 usedCdm = toClassDefMutable(
4124                   Doxygen::hiddenClassLinkedMap->add(usedName,
4125                     createClassDef(
4126                       masterCd->getDefFileName(),masterCd->getDefLine(),
4127                       masterCd->getDefColumn(),
4128                       usedName,
4129                       ClassDef::Class)));
4130                 if (usedCdm)
4131                 {
4132                   //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4133                   usedCdm->makeTemplateArgument();
4134                   usedCdm->setUsedOnly(TRUE);
4135                   usedCdm->setLanguage(masterCd->getLanguage());
4136                   usedCd = usedCdm;
4137                 }
4138               }
4139               if (usedCd)
4140               {
4141                 found=TRUE;
4142                 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4143                 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4144                 if (usedCdm)
4145                 {
4146                   if (isArtificial) usedCdm->setArtificial(TRUE);
4147                   usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4148                 }
4149               }
4150             }
4151           }
4152
4153           if (!found)
4154           {
4155             ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4156             //printf("Looking for used class %s: result=%s master=%s\n",
4157             //    qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4158
4159             if (usedCd)
4160             {
4161               found=TRUE;
4162               AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4163               instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4164               ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4165               if (usedCdm)
4166               {
4167                 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4168               }
4169             }
4170           }
4171         }
4172         if (!found && !type.isEmpty()) // used class is not documented in any scope
4173         {
4174           ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4175           ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4176           if (usedCd==0 && !Config_getBool(HIDE_UNDOC_RELATIONS))
4177           {
4178             if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4179             {
4180               type+=md->argsString();
4181             }
4182             AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4183             usedCdm = toClassDefMutable(
4184                         Doxygen::hiddenClassLinkedMap->add(type,
4185                           createClassDef(
4186                             masterCd->getDefFileName(),masterCd->getDefLine(),
4187                             masterCd->getDefColumn(),
4188                             type,ClassDef::Class)));
4189             if (usedCdm)
4190             {
4191               usedCdm->setUsedOnly(TRUE);
4192               usedCdm->setLanguage(masterCd->getLanguage());
4193               usedCd = usedCdm;
4194             }
4195           }
4196           if (usedCd)
4197           {
4198             AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4199             instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4200             if (usedCdm)
4201             {
4202               if (isArtificial) usedCdm->setArtificial(TRUE);
4203               usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4204             }
4205           }
4206         }
4207       }
4208     }
4209   }
4210 }
4211
4212 static void findBaseClassesForClass(
4213       const Entry *root,
4214       Definition *context,
4215       ClassDefMutable *masterCd,
4216       ClassDefMutable *instanceCd,
4217       FindBaseClassRelation_Mode mode,
4218       bool isArtificial,
4219       const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4220       const TemplateNameMap &templateNames=TemplateNameMap()
4221     )
4222 {
4223   AUTO_TRACE("name={}",root->name);
4224   // The base class could ofcouse also be a non-nested class
4225   const ArgumentList &formalArgs = masterCd->templateArguments();
4226   for (const BaseInfo &bi : root->extends)
4227   {
4228     //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4229     //    qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4230     TemplateNameMap formTemplateNames;
4231     if (templateNames.empty())
4232     {
4233       formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4234     }
4235     BaseInfo tbi = bi;
4236     tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4237     //printf("bi->name=%s tbi.name=%s\n",qPrint(bi->name),qPrint(tbi.name));
4238
4239     if (mode==DocumentedOnly)
4240     {
4241       // find a documented base class in the correct scope
4242       if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4243       {
4244         // 1.8.2: decided to show inheritance relations even if not documented,
4245         //        we do make them artificial, so they do not appear in the index
4246         //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4247         bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4248         //{
4249           // no documented base class -> try to find an undocumented one
4250           findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4251         //}
4252       }
4253     }
4254     else if (mode==TemplateInstances)
4255     {
4256       findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4257     }
4258   }
4259 }
4260
4261 //----------------------------------------------------------------------
4262
4263 static void findTemplateInstanceRelation(const Entry *root,
4264             Definition *context,
4265             ClassDefMutable *templateClass,const QCString &templSpec,
4266             const TemplateNameMap &templateNames,
4267             bool isArtificial)
4268 {
4269   AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4270          templateClass->name(),templSpec,isArtificial);
4271
4272   bool existingClass = (templSpec ==
4273                         tempArgListToString(templateClass->templateArguments(),root->lang,false)
4274                        );
4275   if (existingClass) return;
4276
4277   bool freshInstance=FALSE;
4278   ClassDefMutable *instanceClass = toClassDefMutable(
4279                      templateClass->insertTemplateInstance(
4280                      root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4281   if (instanceClass)
4282   {
4283     instanceClass->setArtificial(TRUE);
4284     instanceClass->setLanguage(root->lang);
4285
4286     if (freshInstance)
4287     {
4288       AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4289       instanceClass->setTemplateBaseClassNames(templateNames);
4290
4291       // search for new template instances caused by base classes of
4292       // instanceClass
4293       auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4294       for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4295       {
4296         const Entry *templateRoot = it->second;
4297         AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4298         std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4299         findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4300             TemplateInstances,isArtificial,templArgs,templateNames);
4301
4302         findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4303             isArtificial,templArgs,templateNames);
4304       }
4305     }
4306     else
4307     {
4308       AUTO_TRACE_ADD("instance already exists");
4309     }
4310   }
4311 }
4312
4313 static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4314 {
4315   QCString n=name;
4316   int index=n.find('<');
4317   if (index!=-1)
4318   {
4319     n=n.left(index);
4320   }
4321   bool result = rightScopeMatch(scope,n);
4322   return result;
4323 }
4324
4325 /*! Searches for the end of a template in prototype \a s starting from
4326  *  character position \a startPos. If the end was found the position
4327  *  of the closing \> is returned, otherwise -1 is returned.
4328  *
4329  *  Handles exotic cases such as
4330  *  \code
4331  *    Class<(id<0)>
4332  *    Class<bits<<2>
4333  *    Class<"<">
4334  *    Class<'<'>
4335  *    Class<(")<")>
4336  *  \endcode
4337  */
4338 static int findEndOfTemplate(const QCString &s,int startPos)
4339 {
4340   // locate end of template
4341   int e=startPos;
4342   int brCount=1;
4343   int roundCount=0;
4344   int len = s.length();
4345   bool insideString=FALSE;
4346   bool insideChar=FALSE;
4347   char pc = 0;
4348   while (e<len && brCount!=0)
4349   {
4350     char c=s.at(e);
4351     switch(c)
4352     {
4353       case '<':
4354         if (!insideString && !insideChar)
4355         {
4356           if (e<len-1 && s.at(e+1)=='<')
4357             e++;
4358           else if (roundCount==0)
4359             brCount++;
4360         }
4361         break;
4362       case '>':
4363         if (!insideString && !insideChar)
4364         {
4365           if (e<len-1 && s.at(e+1)=='>')
4366             e++;
4367           else if (roundCount==0)
4368             brCount--;
4369         }
4370         break;
4371       case '(':
4372         if (!insideString && !insideChar)
4373           roundCount++;
4374         break;
4375       case ')':
4376         if (!insideString && !insideChar)
4377           roundCount--;
4378         break;
4379       case '"':
4380         if (!insideChar)
4381         {
4382           if (insideString && pc!='\\')
4383             insideString=FALSE;
4384           else
4385             insideString=TRUE;
4386         }
4387         break;
4388       case '\'':
4389         if (!insideString)
4390         {
4391           if (insideChar && pc!='\\')
4392             insideChar=FALSE;
4393           else
4394             insideChar=TRUE;
4395         }
4396         break;
4397     }
4398     pc = c;
4399     e++;
4400   }
4401   return brCount==0 ? e : -1;
4402 }
4403
4404 static int findTemplateSpecializationPosition(const QCString &name)
4405 {
4406   if (name.isEmpty()) return 0;
4407   int l = static_cast<int>(name.length());
4408   if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4409   {
4410     int count=1;
4411     int i=l-2;
4412     char insideQuote=0;
4413     while (count>0 && i>=0)
4414     {
4415       char c = name[i--];
4416       switch (c)
4417       {
4418         case '>':  if (!insideQuote) count++; break;
4419         case '<':  if (!insideQuote) count--; break;
4420         case '\'': if (!insideQuote) insideQuote=c;
4421                    else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4422                    break;
4423         case '"':  if (!insideQuote) insideQuote=c;
4424                    else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4425                    break;
4426         default: break;
4427       }
4428     }
4429     if (i>=0) l=i+1;
4430   }
4431   return l;
4432 }
4433
4434 static bool findClassRelation(
4435                            const Entry *root,
4436                            Definition *context,
4437                            ClassDefMutable *cd,
4438                            const BaseInfo *bi,
4439                            const TemplateNameMap &templateNames,
4440                            FindBaseClassRelation_Mode mode,
4441                            bool isArtificial
4442                           )
4443 {
4444   AUTO_TRACE("name={} base={} isArtificial={}",cd->name(),bi->name,isArtificial);
4445
4446   QCString biName=bi->name;
4447   bool explicitGlobalScope=FALSE;
4448   if (biName.startsWith("::")) // explicit global scope
4449   {
4450      biName=biName.right(biName.length()-2);
4451      explicitGlobalScope=TRUE;
4452   }
4453
4454   Entry *parentNode=root->parent();
4455   bool lastParent=FALSE;
4456   do // for each parent scope, starting with the largest scope
4457      // (in case of nested classes)
4458   {
4459     QCString scopeName= parentNode ? parentNode->name : QCString();
4460     int scopeOffset=explicitGlobalScope ? 0 : scopeName.length();
4461     do // try all parent scope prefixes, starting with the largest scope
4462     {
4463       //printf("scopePrefix='%s' biName='%s'\n",
4464       //    qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4465
4466       QCString baseClassName=biName;
4467       if (scopeOffset>0)
4468       {
4469         baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4470       }
4471       //QCString stripped;
4472       //baseClassName=stripTemplateSpecifiersFromScope
4473       //                    (removeRedundantWhiteSpace(baseClassName),TRUE,
4474       //                    &stripped);
4475       SymbolResolver resolver(cd->getFileDef());
4476       ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4477                                            baseClassName,
4478                                            mode==Undocumented,
4479                                            true
4480                                           );
4481       const MemberDef *baseClassTypeDef = resolver.getTypedef();
4482       QCString templSpec = resolver.getTemplateSpec();
4483       //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4484       //    qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4485       //printf("    scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4486       //                    cd ? qPrint(cd->name()):"<none>",
4487       //                    qPrint(baseClassName),
4488       //                    baseClass?qPrint(baseClass->name()):"<none>",
4489       //                    qPrint(templSpec)
4490       //      );
4491       //if (baseClassName.left(root->name.length())!=root->name ||
4492       //    baseClassName.at(root->name.length())!='<'
4493       //   ) // Check for base class with the same name.
4494       //     // If found then look in the outer scope for a match
4495       //     // and prevent recursion.
4496       if (!isRecursiveBaseClass(root->name,baseClassName)
4497           || explicitGlobalScope
4498           // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4499           // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4500           || (root->lang==SrcLangExt_IDL &&
4501               (root->section==Entry::EXPORTED_INTERFACE_SEC ||
4502                root->section==Entry::INCLUDED_SERVICE_SEC)))
4503       {
4504         AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4505             baseClassName, root->name, bi->prot, bi->virt, templSpec);
4506
4507         int i=findTemplateSpecializationPosition(baseClassName);
4508         int si=baseClassName.findRev("::",i);
4509         if (si==-1) si=0;
4510         if (baseClass==0 && static_cast<uint32_t>(i)!=baseClassName.length())
4511           // base class has template specifiers
4512         {
4513           // TODO: here we should try to find the correct template specialization
4514           // but for now, we only look for the unspecialized base class.
4515           int e=findEndOfTemplate(baseClassName,i+1);
4516           //printf("baseClass==0 i=%d e=%d\n",i,e);
4517           if (e!=-1) // end of template was found at e
4518           {
4519             templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4520             baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4521             baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4522                   baseClassName,
4523                   mode==Undocumented,
4524                   true
4525                   );
4526             baseClassTypeDef = resolver.getTypedef();
4527             //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4528             //      baseClass,qPrint(baseClassName),qPrint(templSpec));
4529           }
4530         }
4531         else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4532                                                     // know it is a template, so see if
4533                                                     // we can also link to the explicit
4534                                                     // instance (for instance if a class
4535                                                     // derived from a template argument)
4536         {
4537           //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4538           ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4539           if (templClass)
4540           {
4541             // use the template instance instead of the template base.
4542             baseClass = templClass;
4543             templSpec.resize(0);
4544           }
4545         }
4546
4547         //printf("cd=%p baseClass=%p\n",cd,baseClass);
4548         bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances);
4549         //printf("1. found=%d\n",found);
4550         if (!found && si!=-1)
4551         {
4552           // replace any namespace aliases
4553           replaceNamespaceAliases(baseClassName,si);
4554           baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4555                                      baseClassName,
4556                                      mode==Undocumented,
4557                                      true
4558                                     );
4559           baseClassTypeDef = resolver.getTypedef();
4560           found=baseClass!=0 && baseClass!=cd;
4561           if (found) templSpec = resolver.getTemplateSpec();
4562         }
4563         //printf("2. found=%d\n",found);
4564
4565         if (!found)
4566         {
4567           baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4568           //printf("findClassWithinClassContext(%s,%s)=%p\n",
4569           //    qPrint(cd->name()),qPrint(baseClassName),baseClass);
4570           found = baseClass!=0 && baseClass!=cd;
4571
4572         }
4573         //printf("3. found=%d\n",found);
4574         if (!found)
4575         {
4576           // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4577           // the class name also in the alias mapping.
4578           auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4579           if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4580           {
4581             baseClass=getClassMutable(it->second.c_str());
4582             found = baseClass!=0 && baseClass!=cd;
4583           }
4584         }
4585         bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
4586
4587         //printf("4. found=%d\n",found);
4588         if (found)
4589         {
4590           AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
4591           // add base class to this class
4592
4593           // if templSpec is not empty then we should "instantiate"
4594           // the template baseClass. A new ClassDef should be created
4595           // to represent the instance. To be able to add the (instantiated)
4596           // members and documentation of a template class
4597           // (inserted in that template class at a later stage),
4598           // the template should know about its instances.
4599           // the instantiation process, should be done in a recursive way,
4600           // since instantiating a template may introduce new inheritance
4601           // relations.
4602           if (!templSpec.isEmpty() && mode==TemplateInstances)
4603           {
4604             // if baseClass is actually a typedef then we should not
4605             // instantiate it, since typedefs are in a different namespace
4606             // see bug531637 for an example where this would otherwise hang
4607             // doxygen
4608             if (baseClassTypeDef==0)
4609             {
4610               //printf("       => findTemplateInstanceRelation: %p\n",baseClassTypeDef);
4611               findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
4612             }
4613           }
4614           else if (mode==DocumentedOnly || mode==Undocumented)
4615           {
4616             //printf("       => insert base class\n");
4617             QCString usedName;
4618             if (baseClassTypeDef || cd->isCSharp())
4619             {
4620               usedName=biName;
4621               //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
4622             }
4623             Protection prot = bi->prot;
4624             if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
4625             if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
4626             {
4627               cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
4628               // add this class as super class to the base class
4629               baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
4630             }
4631             else
4632             {
4633               warn(root->fileName,root->startLine,
4634                   "Detected potential recursive class relation "
4635                   "between class %s and base class %s!",
4636                   qPrint(cd->name()),qPrint(baseClass->name())
4637                   );
4638             }
4639           }
4640           return TRUE;
4641         }
4642         else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
4643         {
4644           AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
4645                        biName,baseClassName,templSpec,isArtificial);
4646           baseClass=0;
4647           if (isATemplateArgument)
4648           {
4649             baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
4650             if (baseClass==0) // not found (or alias)
4651             {
4652               baseClass= toClassDefMutable(
4653                 Doxygen::hiddenClassLinkedMap->add(baseClassName,
4654                   createClassDef(root->fileName,root->startLine,root->startColumn,
4655                                baseClassName,
4656                                ClassDef::Class)));
4657               if (baseClass) // really added (not alias)
4658               {
4659                 if (isArtificial) baseClass->setArtificial(TRUE);
4660                 baseClass->setLanguage(root->lang);
4661               }
4662             }
4663           }
4664           else
4665           {
4666             baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
4667             //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
4668             //    qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
4669             if (baseClass==0) // not found (or alias)
4670             {
4671               baseClass = toClassDefMutable(
4672                   Doxygen::classLinkedMap->add(baseClassName,
4673                     createClassDef(root->fileName,root->startLine,root->startColumn,
4674                       baseClassName,
4675                       ClassDef::Class)));
4676               if (baseClass) // really added (not alias)
4677               {
4678                 if (isArtificial) baseClass->setArtificial(TRUE);
4679                 baseClass->setLanguage(root->lang);
4680                 si = baseClassName.findRev("::");
4681                 if (si!=-1) // class is nested
4682                 {
4683                   Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),0,root->tagInfo());
4684                   if (sd==0 || sd==Doxygen::globalScope) // outer scope not found
4685                   {
4686                     baseClass->setArtificial(TRUE); // see bug678139
4687                   }
4688                 }
4689               }
4690             }
4691           }
4692           if (baseClass)
4693           {
4694             if (biName.endsWith("-p"))
4695             {
4696               biName="<"+biName.left(biName.length()-2)+">";
4697             }
4698             if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
4699             {
4700               // add base class to this class
4701               cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
4702               // add this class as super class to the base class
4703               baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
4704             }
4705             // the undocumented base was found in this file
4706             baseClass->insertUsedFile(root->fileDef());
4707
4708             Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,0);
4709             if (scope!=baseClass)
4710             {
4711               baseClass->setOuterScope(scope);
4712             }
4713
4714             if (baseClassName.endsWith("-p"))
4715             {
4716               baseClass->setCompoundType(ClassDef::Protocol);
4717             }
4718             return TRUE;
4719           }
4720           else
4721           {
4722             AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
4723           }
4724         }
4725         else
4726         {
4727           AUTO_TRACE_ADD("Base class '{}' not found",biName);
4728         }
4729       }
4730       else
4731       {
4732         if (mode!=TemplateInstances)
4733         {
4734           warn(root->fileName,root->startLine,
4735               "Detected potential recursive class relation "
4736               "between class %s and base class %s!",
4737               qPrint(root->name),qPrint(baseClassName)
4738               );
4739         }
4740         // for mode==TemplateInstance this case is quite common and
4741         // indicates a relation between a template class and a template
4742         // instance with the same name.
4743       }
4744       if (scopeOffset==0)
4745       {
4746         scopeOffset=-1;
4747       }
4748       else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4749       {
4750         scopeOffset=0;
4751       }
4752       //printf("new scopeOffset='%d'",scopeOffset);
4753     } while (scopeOffset>=0);
4754
4755     if (parentNode==0)
4756     {
4757       lastParent=TRUE;
4758     }
4759     else
4760     {
4761       parentNode=parentNode->parent();
4762     }
4763   } while (lastParent);
4764
4765   return FALSE;
4766 }
4767
4768 //----------------------------------------------------------------------
4769 // Computes the base and super classes for each class in the tree
4770
4771 static bool isClassSection(const Entry *root)
4772 {
4773   if ( !root->name.isEmpty() )
4774   {
4775     if (root->section & Entry::COMPOUND_MASK)
4776          // is it a compound (class, struct, union, interface ...)
4777     {
4778       return TRUE;
4779     }
4780     else if (root->section & Entry::COMPOUNDDOC_MASK)
4781          // is it a documentation block with inheritance info.
4782     {
4783       bool hasExtends = !root->extends.empty();
4784       if (hasExtends) return TRUE;
4785     }
4786   }
4787   return FALSE;
4788 }
4789
4790
4791 /*! Builds a dictionary of all entry nodes in the tree starting with \a root
4792  */
4793 static void findClassEntries(const Entry *root)
4794 {
4795   if (isClassSection(root))
4796   {
4797     g_classEntries.insert({root->name.str(),root});
4798   }
4799   for (const auto &e : root->children()) findClassEntries(e.get());
4800 }
4801
4802 static QCString extractClassName(const Entry *root)
4803 {
4804   // strip any anonymous scopes first
4805   QCString bName=stripAnonymousNamespaceScope(root->name);
4806   bName=stripTemplateSpecifiersFromScope(bName);
4807   int i;
4808   if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) &&
4809       (i=bName.find('<'))!=-1)
4810   {
4811     // a Java/C# generic class looks like a C++ specialization, so we need to strip the
4812     // template part before looking for matches
4813     bName=bName.left(i);
4814   }
4815   return bName;
4816 }
4817
4818 /*! Using the dictionary build by findClassEntries(), this
4819  *  function will look for additional template specialization that
4820  *  exists as inheritance relations only. These instances will be
4821  *  added to the template they are derived from.
4822  */
4823 static void findInheritedTemplateInstances()
4824 {
4825   AUTO_TRACE();
4826   ClassDefSet visitedClasses;
4827   for (const auto &[name,root] : g_classEntries)
4828   {
4829     ClassDef *cd;
4830     QCString bName = extractClassName(root);
4831     if ((cd=getClass(bName)))
4832     {
4833       ClassDefMutable *cdm = toClassDefMutable(cd);
4834       if (cdm)
4835       {
4836         findBaseClassesForClass(root,cd,cdm,cdm,TemplateInstances,FALSE);
4837       }
4838     }
4839   }
4840 }
4841
4842 static void findUsedTemplateInstances()
4843 {
4844   AUTO_TRACE();
4845   for (const auto &[name,root] : g_classEntries)
4846   {
4847     ClassDef *cd;
4848     QCString bName = extractClassName(root);
4849     if ((cd=getClass(bName)))
4850     {
4851       ClassDefMutable *cdm = toClassDefMutable(cd);
4852       if (cdm)
4853       {
4854         findUsedClassesForClass(root,cd,cdm,cdm,TRUE);
4855         cdm->addTypeConstraints();
4856       }
4857     }
4858   }
4859 }
4860
4861 static void computeClassRelations()
4862 {
4863   AUTO_TRACE();
4864   for (const auto &[name,root] : g_classEntries)
4865   {
4866     ClassDefMutable *cd;
4867
4868     QCString bName = extractClassName(root);
4869     if ((cd=getClassMutable(bName)))
4870     {
4871       findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,FALSE);
4872     }
4873     size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
4874     if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 &&
4875         !bName.endsWith("::"))
4876     {
4877       if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
4878           (guessSection(root->fileName)==Entry::HEADER_SEC ||
4879            Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
4880            protectionLevelVisible(root->protection) && // hidden by protection
4881            !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
4882          )
4883         warn_undoc(
4884                    root->fileName,root->startLine,
4885                    "Compound %s is not documented.",
4886                    qPrint(root->name)
4887              );
4888     }
4889   }
4890 }
4891
4892 static void computeTemplateClassRelations()
4893 {
4894   AUTO_TRACE();
4895   for (const auto &[name,root] : g_classEntries)
4896   {
4897     QCString bName=stripAnonymousNamespaceScope(root->name);
4898     bName=stripTemplateSpecifiersFromScope(bName);
4899     ClassDefMutable *cd=getClassMutable(bName);
4900     // strip any anonymous scopes first
4901     if (cd && !cd->getTemplateInstances().empty())
4902     {
4903       AUTO_TRACE_ADD("Template class '{}'",cd->name());
4904       for (const auto &ti : cd->getTemplateInstances()) // for each template instance
4905       {
4906         ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
4907         if (tcd)
4908         {
4909           AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
4910           QCString templSpec = ti.templSpec;
4911           std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
4912           for (const BaseInfo &bi : root->extends)
4913           {
4914             // check if the base class is a template argument
4915             BaseInfo tbi = bi;
4916             const ArgumentList &tl = cd->templateArguments();
4917             if (!tl.empty())
4918             {
4919               TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
4920               TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
4921               // for each template name that we inherit from we need to
4922               // substitute the formal with the actual arguments
4923               TemplateNameMap actualTemplateNames;
4924               for (const auto &tn_kv : templateNames)
4925               {
4926                 size_t templIndex = tn_kv.second;
4927                 Argument actArg;
4928                 bool hasActArg=FALSE;
4929                 if (templIndex<templArgs->size())
4930                 {
4931                   actArg=templArgs->at(templIndex);
4932                   hasActArg=TRUE;
4933                 }
4934                 if (hasActArg &&
4935                     baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
4936                     actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
4937                    )
4938                 {
4939                   actualTemplateNames.insert(std::make_pair(actArg.type.str(),static_cast<int>(templIndex)));
4940                 }
4941               }
4942
4943               tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs);
4944               // find a documented base class in the correct scope
4945               if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
4946               {
4947                 // no documented base class -> try to find an undocumented one
4948                 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
4949               }
4950             }
4951           }
4952         }
4953       }
4954     }
4955   }
4956 }
4957
4958 //-----------------------------------------------------------------------
4959 // compute the references (anchors in HTML) for each function in the file
4960
4961 static void computeMemberReferences()
4962 {
4963   AUTO_TRACE();
4964   for (const auto &cd : *Doxygen::classLinkedMap)
4965   {
4966     ClassDefMutable *cdm = toClassDefMutable(cd.get());
4967     if (cdm)
4968     {
4969       cdm->computeAnchors();
4970     }
4971   }
4972   for (const auto &fn : *Doxygen::inputNameLinkedMap)
4973   {
4974     for (const auto &fd : *fn)
4975     {
4976       fd->computeAnchors();
4977     }
4978   }
4979   for (const auto &nd : *Doxygen::namespaceLinkedMap)
4980   {
4981     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
4982     if (ndm)
4983     {
4984       ndm->computeAnchors();
4985     }
4986   }
4987   for (const auto &gd : *Doxygen::groupLinkedMap)
4988   {
4989     gd->computeAnchors();
4990   }
4991 }
4992
4993 //----------------------------------------------------------------------
4994
4995 static void addListReferences()
4996 {
4997   AUTO_TRACE();
4998   for (const auto &cd : *Doxygen::classLinkedMap)
4999   {
5000     ClassDefMutable *cdm = toClassDefMutable(cd.get());
5001     if (cdm)
5002     {
5003       cdm->addListReferences();
5004     }
5005   }
5006
5007   for (const auto &fn : *Doxygen::inputNameLinkedMap)
5008   {
5009     for (const auto &fd : *fn)
5010     {
5011       fd->addListReferences();
5012     }
5013   }
5014
5015   for (const auto &nd : *Doxygen::namespaceLinkedMap)
5016   {
5017     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
5018     if (ndm)
5019     {
5020       ndm->addListReferences();
5021     }
5022   }
5023
5024   for (const auto &gd : *Doxygen::groupLinkedMap)
5025   {
5026     gd->addListReferences();
5027   }
5028
5029   for (const auto &pd : *Doxygen::pageLinkedMap)
5030   {
5031     QCString name = pd->getOutputFileBase();
5032     if (pd->getGroupDef())
5033     {
5034       name = pd->getGroupDef()->getOutputFileBase();
5035     }
5036     {
5037       const RefItemVector &xrefItems = pd->xrefListItems();
5038       addRefItem(xrefItems,
5039           name,
5040           theTranslator->trPage(TRUE,TRUE),
5041           name,pd->title(),QCString(),0);
5042     }
5043   }
5044
5045   for (const auto &dd : *Doxygen::dirLinkedMap)
5046   {
5047     QCString name = dd->getOutputFileBase();
5048     //if (dd->getGroupDef())
5049     //{
5050     //  name = dd->getGroupDef()->getOutputFileBase();
5051     //}
5052     const RefItemVector &xrefItems = dd->xrefListItems();
5053     addRefItem(xrefItems,
5054         name,
5055         theTranslator->trDir(TRUE,TRUE),
5056         name,dd->displayName(),QCString(),0);
5057   }
5058
5059   ModuleManager::instance().addListReferences();
5060 }
5061
5062 //----------------------------------------------------------------------
5063
5064 static void generateXRefPages()
5065 {
5066   AUTO_TRACE();
5067   for (RefListManager::Ptr &rl : RefListManager::instance())
5068   {
5069     rl->generatePage();
5070   }
5071 }
5072
5073 //----------------------------------------------------------------------
5074 // Copy the documentation in entry 'root' to member definition 'md' and
5075 // set the function declaration of the member to 'funcDecl'. If the boolean
5076 // over_load is set the standard overload text is added.
5077
5078 static void addMemberDocs(const Entry *root,
5079                    MemberDefMutable *md, const QCString &funcDecl,
5080                    const ArgumentList *al,
5081                    bool over_load,
5082                    uint64_t spec
5083                   )
5084 {
5085   if (md==0) return;
5086   AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={:#x}",
5087        root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5088   QCString fDecl=funcDecl;
5089   // strip extern specifier
5090   fDecl.stripPrefix("extern ");
5091   md->setDefinition(fDecl);
5092   md->enableCallGraph(root->callGraph);
5093   md->enableCallerGraph(root->callerGraph);
5094   md->enableReferencedByRelation(root->referencedByRelation);
5095   md->enableReferencesRelation(root->referencesRelation);
5096   md->addQualifiers(root->qualifiers);
5097   ClassDefMutable *cd=md->getClassDefMutable();
5098   const NamespaceDef *nd=md->getNamespaceDef();
5099   QCString fullName;
5100   if (cd)
5101     fullName = cd->name();
5102   else if (nd)
5103     fullName = nd->name();
5104
5105   if (!fullName.isEmpty()) fullName+="::";
5106   fullName+=md->name();
5107   FileDef *rfd=root->fileDef();
5108
5109   // TODO determine scope based on root not md
5110   Definition *rscope = md->getOuterScope();
5111
5112   const ArgumentList &mdAl = md->argumentList();
5113   if (al)
5114   {
5115     ArgumentList mergedAl = *al;
5116     //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5117     mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5118   }
5119   else
5120   {
5121     if (
5122           matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5123                            rscope,rfd,&root->argList,
5124                            TRUE, root->lang
5125                          )
5126        )
5127     {
5128       //printf("merging arguments (2)\n");
5129       ArgumentList mergedArgList = root->argList;
5130       mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5131     }
5132   }
5133   if (over_load)  // the \overload keyword was used
5134   {
5135     QCString doc=getOverloadDocs();
5136     if (!root->doc.isEmpty())
5137     {
5138       doc+="<p>";
5139       doc+=root->doc;
5140     }
5141     md->setDocumentation(doc,root->docFile,root->docLine);
5142     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5143     md->setDocsForDefinition(!root->proto);
5144   }
5145   else
5146   {
5147     //printf("overwrite!\n");
5148     md->setDocumentation(root->doc,root->docFile,root->docLine);
5149     md->setDocsForDefinition(!root->proto);
5150
5151     //printf("overwrite!\n");
5152     md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5153
5154     if (
5155         (md->inbodyDocumentation().isEmpty() ||
5156          !root->parent()->name.isEmpty()
5157         ) && !root->inbodyDocs.isEmpty()
5158        )
5159     {
5160       md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5161     }
5162   }
5163
5164   //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5165   //    qPrint(md->initializer()),md->initializer().isEmpty(),
5166   //    qPrint(root->initializer),root->initializer.isEmpty()
5167   //   );
5168   std::string rootInit = root->initializer.str();
5169   if (md->initializer().isEmpty() && !rootInit.empty())
5170   {
5171     //printf("setInitializer\n");
5172     md->setInitializer(rootInit.c_str());
5173   }
5174   if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5175   {
5176     md->setRequiresClause(root->req);
5177   }
5178
5179   md->setMaxInitLines(root->initLines);
5180
5181   if (rfd)
5182   {
5183     if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5184        )
5185     {
5186       //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5187       md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5188       md->setBodyDef(rfd);
5189     }
5190
5191     md->setRefItems(root->sli);
5192   }
5193
5194   md->enableCallGraph(md->hasCallGraph() || root->callGraph);
5195   md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
5196   md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
5197   md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
5198   md->addQualifiers(root->qualifiers);
5199
5200   md->mergeMemberSpecifiers(spec);
5201   md->addSectionsToDefinition(root->anchors);
5202   addMemberToGroups(root,md);
5203   ModuleManager::instance().addMemberToModule(root,md);
5204   if (cd) cd->insertUsedFile(rfd);
5205   //printf("root->mGrpId=%d\n",root->mGrpId);
5206   if (root->mGrpId!=-1)
5207   {
5208     if (md->getMemberGroupId()!=-1)
5209     {
5210       if (md->getMemberGroupId()!=root->mGrpId)
5211       {
5212         warn(
5213              root->fileName,root->startLine,
5214              "member %s belongs to two different groups. The second "
5215              "one found here will be ignored.",
5216              qPrint(md->name())
5217             );
5218       }
5219     }
5220     else // set group id
5221     {
5222       //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5223       md->setMemberGroupId(root->mGrpId);
5224     }
5225   }
5226   md->addQualifiers(root->qualifiers);
5227 }
5228
5229 //----------------------------------------------------------------------
5230 // find a class definition given the scope name and (optionally) a
5231 // template list specifier
5232
5233 static const ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd,
5234                          const QCString &scopeName)
5235 {
5236   SymbolResolver resolver(fd);
5237   const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5238   return tcd;
5239 }
5240
5241 //----------------------------------------------------------------------------
5242 // Returns TRUE, if the entry belongs to the group of the member definition,
5243 // otherwise FALSE.
5244
5245 static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md)
5246 {
5247   const GroupDef *gd = md->getGroupDef();
5248   if (!gd)
5249   {
5250     return FALSE;
5251   }
5252
5253   for (const auto &g : root->groups)
5254   {
5255     if (g.groupname == gd->name())
5256     {
5257       return TRUE;
5258     }
5259   }
5260
5261   return FALSE;
5262 }
5263
5264 //----------------------------------------------------------------------
5265 // Adds the documentation contained in 'root' to a global function
5266 // with name 'name' and argument list 'args' (for overloading) and
5267 // function declaration 'decl' to the corresponding member definition.
5268
5269 static bool findGlobalMember(const Entry *root,
5270                            const QCString &namespaceName,
5271                            const QCString &type,
5272                            const QCString &name,
5273                            const QCString &tempArg,
5274                            const QCString &,
5275                            const QCString &decl,
5276                            uint64_t /* spec */)
5277 {
5278   AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5279   QCString n=name;
5280   if (n.isEmpty()) return FALSE;
5281   if (n.find("::")!=-1) return FALSE; // skip undefined class members
5282   MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5283   if (mn==0)
5284   {
5285     mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5286   }
5287   if (mn) // function name defined
5288   {
5289     AUTO_TRACE_ADD("Found symbol name");
5290     //int count=0;
5291     bool found=FALSE;
5292     for (const auto &md : *mn)
5293     {
5294       // If the entry has groups, then restrict the search to members which are
5295       // in one of the groups of the entry.
5296       if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get()))
5297       {
5298         continue;
5299       }
5300       const NamespaceDef *nd=0;
5301       if (md->isAlias() && md->getOuterScope() &&
5302           md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5303       {
5304         nd = toNamespaceDef(md->getOuterScope());
5305       }
5306       else
5307       {
5308         nd = md->getNamespaceDef();
5309       }
5310
5311       // special case for strong enums
5312       int enumNamePos=0;
5313       if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5314       { // md part of a strong enum in a namespace?
5315         QCString enumName = namespaceName.mid(enumNamePos+2);
5316         if (namespaceName.left(enumNamePos)==nd->name())
5317         {
5318           MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5319           if (enumMn)
5320           {
5321             for (const auto &emd : *enumMn)
5322             {
5323               found = emd->isStrong() && md->getEnumScope()==emd.get();
5324               if (found)
5325               {
5326                 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5327                 break;
5328               }
5329             }
5330           }
5331         }
5332         if (found)
5333         {
5334           break;
5335         }
5336       }
5337       else if (nd==0 && md->isEnumValue()) // md part of global strong enum?
5338       {
5339         MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5340         if (enumMn)
5341         {
5342           for (const auto &emd : *enumMn)
5343           {
5344             found = emd->isStrong() && md->getEnumScope()==emd.get();
5345             if (found)
5346             {
5347               addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5348               break;
5349             }
5350           }
5351         }
5352       }
5353
5354       const FileDef *fd=root->fileDef();
5355       //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5356       LinkedRefMap<NamespaceDef> nl;
5357       if (fd)
5358       {
5359         nl = fd->getUsedNamespaces();
5360       }
5361       //printf("NamespaceList %p\n",nl);
5362
5363       // search in the list of namespaces that are imported via a
5364       // using declaration
5365       bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=0;
5366
5367       if ((namespaceName.isEmpty() && nd==0) ||  // not in a namespace
5368           (nd && nd->name()==namespaceName) ||   // or in the same namespace
5369           viaUsingDirective                      // member in 'using' namespace
5370          )
5371       {
5372         AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5373
5374         NamespaceDef *rnd = 0;
5375         if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5376
5377         const ArgumentList &mdAl = const_cast<const MemberDef *>(md.get())->argumentList();
5378         bool matching=
5379           (mdAl.empty() && root->argList.empty()) ||
5380           md->isVariable() || md->isTypedef() || /* in case of function pointers */
5381           matchArguments2(md->getOuterScope(),const_cast<const MemberDef *>(md.get())->getFileDef(),&mdAl,
5382                           rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5383                           FALSE,root->lang);
5384
5385         // for template members we need to check if the number of
5386         // template arguments is the same, otherwise we are dealing with
5387         // different functions.
5388         if (matching && !root->tArgLists.empty())
5389         {
5390           const ArgumentList &mdTempl = md->templateArguments();
5391           if (root->tArgLists.back().size()!=mdTempl.size())
5392           {
5393             matching=FALSE;
5394           }
5395         }
5396
5397         //printf("%s<->%s\n",
5398         //    qPrint(argListToString(md->argumentList())),
5399         //    qPrint(argListToString(root->argList)));
5400
5401         // For static members we also check if the comment block was found in
5402         // the same file. This is needed because static members with the same
5403         // name can be in different files. Thus it would be wrong to just
5404         // put the comment block at the first syntactically matching member. If
5405         // the comment block belongs to a group of the static member, then add
5406         // the documentation even if it is in a different file.
5407         if (matching && md->isStatic() &&
5408             md->getDefFileName()!=root->fileName &&
5409             mn->size()>1 &&
5410             !isEntryInGroupOfMember(root,md.get()))
5411         {
5412           matching = FALSE;
5413         }
5414
5415         // for template member we also need to check the return type and requires
5416         if (!md->templateArguments().empty() && !root->tArgLists.empty())
5417         {
5418           //printf("Comparing return types '%s'<->'%s'\n",
5419           //    md->typeString(),type);
5420           if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5421               md->typeString()!=type ||
5422               md->requiresClause()!=root->req)
5423           {
5424             //printf(" ---> no matching\n");
5425             matching = FALSE;
5426           }
5427         }
5428
5429         if (matching) // add docs to the member
5430         {
5431           AUTO_TRACE_ADD("Match found");
5432           addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5433           found=TRUE;
5434           break;
5435         }
5436       }
5437     }
5438     if (!found && root->relatesType!=RelatesType::Duplicate && root->section==Entry::FUNCTION_SEC) // no match
5439     {
5440       QCString fullFuncDecl=decl;
5441       if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5442       QCString warnMsg =
5443          QCString("no matching file member found for \n")+substitute(fullFuncDecl,"%","%%");
5444       if (mn->size()>0)
5445       {
5446         warnMsg+="\nPossible candidates:";
5447         for (const auto &md : *mn)
5448         {
5449           warnMsg+="\n '";
5450           warnMsg+=substitute(md->declaration(),"%","%%");
5451           warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5452         }
5453       }
5454       warn(root->fileName,root->startLine, "%s", qPrint(warnMsg));
5455     }
5456   }
5457   else // got docs for an undefined member!
5458   {
5459     if (root->type!="friend class" &&
5460         root->type!="friend struct" &&
5461         root->type!="friend union" &&
5462         root->type!="friend" &&
5463         (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5464          root->type.find("typedef ")==-1)
5465        )
5466     {
5467       warn(root->fileName,root->startLine,
5468            "documented symbol '%s' was not declared or defined.",qPrint(decl)
5469           );
5470     }
5471   }
5472   return TRUE;
5473 }
5474
5475 static bool isSpecialization(
5476                   const ArgumentLists &srcTempArgLists,
5477                   const ArgumentLists &dstTempArgLists
5478     )
5479 {
5480     auto srcIt = srcTempArgLists.begin();
5481     auto dstIt = dstTempArgLists.begin();
5482     while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5483     {
5484       if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5485       ++srcIt;
5486       ++dstIt;
5487     }
5488     return FALSE;
5489 }
5490
5491 static bool scopeIsTemplate(const Definition *d)
5492 {
5493   bool result=FALSE;
5494   if (d && d->definitionType()==Definition::TypeClass)
5495   {
5496     result = !(toClassDef(d))->templateArguments().empty() ||
5497              scopeIsTemplate(d->getOuterScope());
5498   }
5499   return result;
5500 }
5501
5502 static QCString substituteTemplatesInString(
5503     const ArgumentLists &srcTempArgLists,
5504     const ArgumentLists &dstTempArgLists,
5505     const std::string &src
5506     )
5507 {
5508   std::string dst;
5509   static const reg::Ex re(R"(\a\w*)");
5510   reg::Iterator it(src,re);
5511   reg::Iterator end;
5512   //printf("type=%s\n",qPrint(sa->type));
5513   size_t p=0;
5514   for (; it!=end ; ++it) // for each word in srcType
5515   {
5516     const auto &match = *it;
5517     size_t i = match.position();
5518     size_t l = match.length();
5519     bool found=FALSE;
5520     dst+=src.substr(p,i-p);
5521     std::string name=match.str();
5522
5523     auto srcIt = srcTempArgLists.begin();
5524     auto dstIt = dstTempArgLists.begin();
5525     while (srcIt!=srcTempArgLists.end() && !found)
5526     {
5527       const ArgumentList *tdAli = 0;
5528       std::vector<Argument>::const_iterator tdaIt;
5529       if (dstIt!=dstTempArgLists.end())
5530       {
5531         tdAli = &(*dstIt);
5532         tdaIt = tdAli->begin();
5533         ++dstIt;
5534       }
5535
5536       const ArgumentList &tsaLi = *srcIt;
5537       for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5538       {
5539         Argument tsa = *tsaIt;
5540         const Argument *tda = 0;
5541         if (tdAli && tdaIt!=tdAli->end())
5542         {
5543           tda = &(*tdaIt);
5544           ++tdaIt;
5545         }
5546         //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5547         //    qPrint(tsa.type),qPrint(tsa.name),
5548         //    qPrint(tda->type),qPrint(tda->name));
5549         if (name==tsa.name.str())
5550         {
5551           if (tda && tda->name.isEmpty())
5552           {
5553             QCString tdaName = tda->name;
5554             QCString tdaType = tda->type;
5555             int vc=0;
5556             if      (tdaType.startsWith("class "))    vc=6;
5557             else if (tdaType.startsWith("typename ")) vc=9;
5558             if (vc>0) // convert type=="class T" to type=="class" name=="T"
5559             {
5560               tdaName = tdaType.mid(vc);
5561             }
5562             if (!tdaName.isEmpty())
5563             {
5564               name=tdaName.str(); // substitute
5565               found=TRUE;
5566             }
5567           }
5568         }
5569       }
5570
5571       //printf("   srcList='%s' dstList='%s faList='%s'\n",
5572       //  qPrint(argListToString(srclali.current())),
5573       //  qPrint(argListToString(dstlali.current())),
5574       //  funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
5575       ++srcIt;
5576     }
5577     dst+=name;
5578     p=i+l;
5579   }
5580   dst+=src.substr(p);
5581   //printf("  substituteTemplatesInString(%s)=%s\n",
5582   //    qPrint(src),qPrint(dst));
5583   return QCString(dst);
5584 }
5585
5586 static void substituteTemplatesInArgList(
5587                   const ArgumentLists &srcTempArgLists,
5588                   const ArgumentLists &dstTempArgLists,
5589                   const ArgumentList &src,
5590                   ArgumentList &dst
5591                  )
5592 {
5593   auto dstIt = dst.begin();
5594   for (const Argument &sa : src)
5595   {
5596     QCString dstType =  substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
5597     QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
5598     if (dstIt == dst.end())
5599     {
5600       Argument da = sa;
5601       da.type  = dstType;
5602       da.array = dstArray;
5603       dst.push_back(da);
5604       dstIt = dst.end();
5605     }
5606     else
5607     {
5608       Argument da = *dstIt;
5609       da.type  = dstType;
5610       da.array = dstArray;
5611       ++dstIt;
5612     }
5613   }
5614   dst.setConstSpecifier(src.constSpecifier());
5615   dst.setVolatileSpecifier(src.volatileSpecifier());
5616   dst.setPureSpecifier(src.pureSpecifier());
5617   dst.setTrailingReturnType(substituteTemplatesInString(
5618                              srcTempArgLists,dstTempArgLists,
5619                              src.trailingReturnType().str()));
5620   dst.setIsDeleted(src.isDeleted());
5621   dst.setRefQualifier(src.refQualifier());
5622   dst.setNoParameters(src.noParameters());
5623   //printf("substituteTemplatesInArgList: replacing %s with %s\n",
5624   //    qPrint(argListToString(src)),qPrint(argListToString(dst))
5625   //    );
5626 }
5627
5628 //-------------------------------------------------------------------------------------------
5629
5630 static void addLocalObjCMethod(const Entry *root,
5631                         const QCString &scopeName,
5632                         const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
5633                         const QCString &exceptions,const QCString &funcDecl,
5634                         uint64_t spec)
5635 {
5636   AUTO_TRACE();
5637   //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
5638   ClassDefMutable *cd=0;
5639   if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
5640   {
5641     AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
5642     auto md = createMemberDef(
5643         root->fileName,root->startLine,root->startColumn,
5644         funcType,funcName,funcArgs,exceptions,
5645         root->protection,root->virt,root->isStatic,Relationship::Member,
5646         MemberType_Function,ArgumentList(),root->argList,root->metaData);
5647     auto mmd = toMemberDefMutable(md.get());
5648     mmd->setTagInfo(root->tagInfo());
5649     mmd->setLanguage(root->lang);
5650     mmd->setId(root->id);
5651     mmd->makeImplementationDetail();
5652     mmd->setMemberClass(cd);
5653     mmd->setDefinition(funcDecl);
5654     mmd->enableCallGraph(root->callGraph);
5655     mmd->enableCallerGraph(root->callerGraph);
5656     mmd->enableReferencedByRelation(root->referencedByRelation);
5657     mmd->enableReferencesRelation(root->referencesRelation);
5658     mmd->addQualifiers(root->qualifiers);
5659     mmd->setDocumentation(root->doc,root->docFile,root->docLine);
5660     mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5661     mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5662     mmd->setDocsForDefinition(!root->proto);
5663     mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
5664     mmd->addSectionsToDefinition(root->anchors);
5665     mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5666     FileDef *fd=root->fileDef();
5667     mmd->setBodyDef(fd);
5668     mmd->setMemberSpecifiers(spec);
5669     mmd->setMemberGroupId(root->mGrpId);
5670     cd->insertMember(md.get());
5671     cd->insertUsedFile(fd);
5672     mmd->setRefItems(root->sli);
5673
5674     MemberName *mn = Doxygen::memberNameLinkedMap->add(root->name);
5675     mn->push_back(std::move(md));
5676   }
5677   else
5678   {
5679     // local objective C method found for class without interface
5680   }
5681 }
5682
5683 //-------------------------------------------------------------------------------------------
5684
5685 static void addMemberFunction(const Entry *root,
5686                        MemberName *mn,
5687                        const QCString &scopeName,
5688                        const QCString &namespaceName,
5689                        const QCString &className,
5690                        const QCString &funcTyp,
5691                        const QCString &funcName,
5692                        const QCString &funcArgs,
5693                        const QCString &funcTempList,
5694                        const QCString &exceptions,
5695                        const QCString &type,
5696                        const QCString &args,
5697                        bool isFriend,
5698                        uint64_t spec,
5699                        const QCString &relates,
5700                        const QCString &funcDecl,
5701                        bool overloaded,
5702                        bool isFunc)
5703 {
5704   AUTO_TRACE();
5705   QCString funcType = funcTyp;
5706   int count=0;
5707   int noMatchCount=0;
5708   bool memFound=FALSE;
5709   for (const auto &imd : *mn)
5710   {
5711     MemberDefMutable *md = toMemberDefMutable(imd.get());
5712     if (md==0) continue;
5713     ClassDefMutable *cd=md->getClassDefMutable();
5714     if (cd==0) continue;
5715     //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
5716     //    scopeName, cd->name(), md->argsString(), root->fileName);
5717     FileDef *fd=root->fileDef();
5718     NamespaceDef *nd=0;
5719     if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
5720
5721     //printf("scopeName %s->%s\n",qPrint(scopeName),
5722     //       qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
5723
5724     // if the member we are searching for is an enum value that is part of
5725     // a "strong" enum, we need to look into the fields of the enum for a match
5726     int enumNamePos=0;
5727     if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
5728     {
5729       QCString enumName = className.mid(enumNamePos+2);
5730       QCString fullScope = className.left(enumNamePos);
5731       if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
5732       if (fullScope==cd->name())
5733       {
5734         MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
5735         //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
5736         if (enumMn)
5737         {
5738           for (const auto &emd : *enumMn)
5739           {
5740             memFound = emd->isStrong() && md->getEnumScope()==emd.get();
5741             if (memFound)
5742             {
5743               addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5744               count++;
5745             }
5746             if (memFound) break;
5747           }
5748         }
5749       }
5750     }
5751     if (memFound) break;
5752
5753     const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
5754     if (tcd==0 && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
5755     {
5756       // don't be fooled by anonymous scopes
5757       tcd=cd;
5758     }
5759     //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
5760     //    qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd,tcd?qPrint(tcd->name()):"",cd);
5761
5762     if (cd && tcd==cd) // member's classes match
5763     {
5764       AUTO_TRACE_ADD("class definition '{}' found",cd->name());
5765
5766       // get the template parameter lists found at the member declaration
5767       ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
5768       const ArgumentList &templAl = md->templateArguments();
5769       if (!templAl.empty())
5770       {
5771         declTemplArgs.push_back(templAl);
5772       }
5773
5774       // get the template parameter lists found at the member definition
5775       const ArgumentLists &defTemplArgs = root->tArgLists;
5776       //printf("defTemplArgs=%p\n",defTemplArgs);
5777
5778       // do we replace the decl argument lists with the def argument lists?
5779       bool substDone=FALSE;
5780       ArgumentList argList;
5781
5782       /* substitute the occurrences of class template names in the
5783        * argument list before matching
5784        */
5785       const ArgumentList &mdAl = md->argumentList();
5786       if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
5787       {
5788         /* the function definition has template arguments
5789          * and the class definition also has template arguments, so
5790          * we must substitute the template names of the class by that
5791          * of the function definition before matching.
5792          */
5793         substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
5794
5795         substDone=TRUE;
5796       }
5797       else /* no template arguments, compare argument lists directly */
5798       {
5799         argList = mdAl;
5800       }
5801
5802       AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}'",
5803           argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName);
5804
5805       bool matching=
5806         md->isVariable() || md->isTypedef() || // needed for function pointers
5807         matchArguments2(
5808             md->getClassDef(),md->getFileDef(),&argList,
5809             cd,fd,&root->argList,
5810             TRUE,root->lang);
5811
5812       if (md->getLanguage()==SrcLangExt_ObjC && md->isVariable() && (root->section&Entry::FUNCTION_SEC))
5813       {
5814         matching = FALSE; // don't match methods and attributes with the same name
5815       }
5816
5817       // for template member we also need to check the return type
5818       if (!md->templateArguments().empty() && !root->tArgLists.empty())
5819       {
5820         QCString memType = md->typeString();
5821         memType.stripPrefix("static "); // see bug700696
5822         funcType=substitute(stripTemplateSpecifiersFromScope(funcType,TRUE),
5823             className+"::",""); // see bug700693 & bug732594
5824         memType=substitute(stripTemplateSpecifiersFromScope(memType,TRUE),
5825             className+"::",""); // see bug758900
5826         AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
5827             md->typeString(),funcType,md->templateArguments().size(),root->tArgLists.back().size());
5828         if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
5829         {
5830           //printf(" ---> no matching\n");
5831           matching = FALSE;
5832         }
5833       }
5834       else if (defTemplArgs.size()>declTemplArgs.size())
5835       {
5836         // avoid matching a non-template function in a template class against a
5837         // template function with the same name and parameters, see issue #10184
5838         substDone = false;
5839         matching  = false;
5840       }
5841       bool rootIsUserDoc = (root->section&Entry::MEMBERDOC_SEC)!=0;
5842       bool classIsTemplate = scopeIsTemplate(md->getClassDef());
5843       bool mdIsTemplate    = md->templateArguments().hasParameters();
5844       bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
5845       bool rootIsTemplate  = !root->tArgLists.empty();
5846       //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
5847       if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
5848           (mdIsTemplate || rootIsTemplate) && // either md or root is a template
5849           ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
5850          )
5851       {
5852         // Method with template return type does not match method without return type
5853         // even if the parameters are the same. See also bug709052
5854         AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
5855         matching = FALSE;
5856       }
5857
5858       AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
5859
5860       if (substDone) // found a new argument list
5861       {
5862         if (matching) // replace member's argument list
5863         {
5864           md->setDefinitionTemplateParameterLists(root->tArgLists);
5865           md->moveArgumentList(std::make_unique<ArgumentList>(argList));
5866         }
5867         else // no match
5868         {
5869           if (!funcTempList.isEmpty() &&
5870               isSpecialization(declTemplArgs,defTemplArgs))
5871           {
5872             // check if we are dealing with a partial template
5873             // specialization. In this case we add it to the class
5874             // even though the member arguments do not match.
5875
5876             addMethodToClass(root,cd,type,md->name(),args,isFriend,
5877                 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
5878             return;
5879           }
5880         }
5881       }
5882       if (matching)
5883       {
5884         addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5885         count++;
5886         memFound=TRUE;
5887       }
5888     }
5889     else if (cd && cd!=tcd) // we did find a class with the same name as cd
5890       // but in a different namespace
5891     {
5892       noMatchCount++;
5893     }
5894
5895     if (memFound) break;
5896   }
5897   if (count==0 && root->parent() &&
5898       root->parent()->section==Entry::OBJCIMPL_SEC)
5899   {
5900     addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
5901     return;
5902   }
5903   if (count==0 && !(isFriend && funcType=="class"))
5904   {
5905     int candidates=0;
5906     const ClassDef *ecd = 0, *ucd = 0;
5907     MemberDef *emd = 0, *umd = 0;
5908     //printf("Assume template class\n");
5909     for (const auto &md : *mn)
5910     {
5911       MemberDef *cmd=md.get();
5912       MemberDefMutable *cdmdm = toMemberDefMutable(cmd);
5913       ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : 0;
5914       //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
5915       if (ccd!=0 && rightScopeMatch(ccd->name(),className))
5916       {
5917         const ArgumentList &templAl = md->templateArguments();
5918         if (!root->tArgLists.empty() && !templAl.empty() &&
5919             root->tArgLists.back().size()<=templAl.size())
5920         {
5921           AUTO_TRACE_ADD("add template specialization");
5922           addMethodToClass(root,ccd,type,md->name(),args,isFriend,
5923               root->protection,root->isStatic,root->virt,spec,relates);
5924           return;
5925         }
5926         if (argListToString(md->argumentList(),FALSE,FALSE) ==
5927             argListToString(root->argList,FALSE,FALSE))
5928         { // exact argument list match -> remember
5929           ucd = ecd = ccd;
5930           umd = emd = cmd;
5931           AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
5932               className,ccd->name(),md->argsString());
5933         }
5934         else // arguments do not match, but member name and scope do -> remember
5935         {
5936           ucd = ccd;
5937           umd = cmd;
5938           AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
5939               className,ccd->name(),md->argsString());
5940         }
5941         candidates++;
5942       }
5943     }
5944     bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
5945     if (!strictProtoMatching)
5946     {
5947       if (candidates==1 && ucd && umd)
5948       {
5949         // we didn't find an actual match on argument lists, but there is only 1 member with this
5950         // name in the same scope, so that has to be the one.
5951         addMemberDocs(root,toMemberDefMutable(umd),funcDecl,0,overloaded,spec);
5952         return;
5953       }
5954       else if (candidates>1 && ecd && emd)
5955       {
5956         // we didn't find a unique match using type resolution,
5957         // but one of the matches has the exact same signature so
5958         // we take that one.
5959         addMemberDocs(root,toMemberDefMutable(emd),funcDecl,0,overloaded,spec);
5960         return;
5961       }
5962     }
5963
5964     QCString warnMsg = "no ";
5965     if (noMatchCount>1) warnMsg+="uniquely ";
5966     warnMsg+="matching class member found for \n";
5967
5968     for (const ArgumentList &al : root->tArgLists)
5969     {
5970       warnMsg+="  template ";
5971       warnMsg+=tempArgListToString(al,root->lang);
5972       warnMsg+='\n';
5973     }
5974
5975     QCString fullFuncDecl=funcDecl;
5976     if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
5977
5978     warnMsg+="  ";
5979     warnMsg+=fullFuncDecl;
5980
5981     if (candidates>0 || noMatchCount>=1)
5982     {
5983       warnMsg+="\nPossible candidates:";
5984
5985       NamespaceDef *nd=0;
5986       if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
5987       FileDef *fd=root->fileDef();
5988
5989       for (const auto &md : *mn)
5990       {
5991         const ClassDef *cd=md->getClassDef();
5992         const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
5993         if (tcd==0 && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
5994         {
5995           // don't be fooled by anonymous scopes
5996           tcd=cd;
5997         }
5998         if (cd!=0 && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
5999         {
6000           warnMsg+='\n';
6001           const ArgumentList &templAl = md->templateArguments();
6002           warnMsg+="  '";
6003           if (templAl.hasParameters())
6004           {
6005             warnMsg+="template ";
6006             warnMsg+=tempArgListToString(templAl,root->lang);
6007             warnMsg+='\n';
6008             warnMsg+="  ";
6009           }
6010           if (!md->typeString().isEmpty())
6011           {
6012             warnMsg+=md->typeString();
6013             warnMsg+=' ';
6014           }
6015           QCString qScope = cd->qualifiedNameWithTemplateParameters();
6016           if (!qScope.isEmpty())
6017             warnMsg+=qScope+"::"+md->name();
6018           warnMsg+=md->argsString();
6019           warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6020         }
6021       }
6022     }
6023     warn(root->fileName,root->startLine,"%s",qPrint(warnMsg));
6024   }
6025 }
6026
6027 //-------------------------------------------------------------------------------------------
6028
6029 static void addMemberSpecialization(const Entry *root,
6030                              MemberName *mn,
6031                              ClassDefMutable *cd,
6032                              const QCString &funcType,
6033                              const QCString &funcName,
6034                              const QCString &funcArgs,
6035                              const QCString &funcDecl,
6036                              const QCString &exceptions,
6037                              uint64_t spec
6038                             )
6039 {
6040   MemberDef *declMd=0;
6041   for (const auto &md : *mn)
6042   {
6043     if (md->getClassDef()==cd)
6044     {
6045       // TODO: we should probably also check for matching arguments
6046       declMd = md.get();
6047       break;
6048     }
6049   }
6050   MemberType mtype=MemberType_Function;
6051   ArgumentList tArgList;
6052   //  getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6053   auto md = createMemberDef(
6054       root->fileName,root->startLine,root->startColumn,
6055       funcType,funcName,funcArgs,exceptions,
6056       declMd ? declMd->protection() : root->protection,
6057       root->virt,root->isStatic,Relationship::Member,
6058       mtype,tArgList,root->argList,root->metaData);
6059   auto mmd = toMemberDefMutable(md.get());
6060   //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6061   mmd->setTagInfo(root->tagInfo());
6062   mmd->setLanguage(root->lang);
6063   mmd->setId(root->id);
6064   mmd->setMemberClass(cd);
6065   mmd->setTemplateSpecialization(TRUE);
6066   mmd->setTypeConstraints(root->typeConstr);
6067   mmd->setDefinition(funcDecl);
6068   mmd->enableCallGraph(root->callGraph);
6069   mmd->enableCallerGraph(root->callerGraph);
6070   mmd->enableReferencedByRelation(root->referencedByRelation);
6071   mmd->enableReferencesRelation(root->referencesRelation);
6072   mmd->addQualifiers(root->qualifiers);
6073   mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6074   mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6075   mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6076   mmd->setDocsForDefinition(!root->proto);
6077   mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6078   mmd->addSectionsToDefinition(root->anchors);
6079   mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6080   FileDef *fd=root->fileDef();
6081   mmd->setBodyDef(fd);
6082   mmd->setMemberSpecifiers(spec);
6083   mmd->setMemberGroupId(root->mGrpId);
6084   cd->insertMember(md.get());
6085   mmd->setRefItems(root->sli);
6086
6087   mn->push_back(std::move(md));
6088 }
6089
6090 //-------------------------------------------------------------------------------------------
6091
6092 static void addOverloaded(const Entry *root,MemberName *mn,
6093                           const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6094                           const QCString &funcDecl,const QCString &exceptions,uint64_t spec)
6095 {
6096   // for unique overloaded member we allow the class to be
6097   // omitted, this is to be Qt compatible. Using this should
6098   // however be avoided, because it is error prone
6099   bool sameClass=false;
6100   if (mn->size()>0)
6101   {
6102     // check if all members with the same name are also in the same class
6103     sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6104                   [](const auto &md1,const auto &md2)
6105                   { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6106   }
6107   if (sameClass)
6108   {
6109     MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6110     ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : 0;
6111     if (cd==0) return;
6112
6113     MemberType mtype;
6114     if      (root->mtype==MethodTypes::Signal)  mtype=MemberType_Signal;
6115     else if (root->mtype==MethodTypes::Slot)    mtype=MemberType_Slot;
6116     else if (root->mtype==MethodTypes::DCOP)    mtype=MemberType_DCOP;
6117     else                                        mtype=MemberType_Function;
6118
6119     // new overloaded member function
6120     std::unique_ptr<ArgumentList> tArgList =
6121       getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6122     //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6123     auto md = createMemberDef(
6124         root->fileName,root->startLine,root->startColumn,
6125         funcType,funcName,funcArgs,exceptions,
6126         root->protection,root->virt,root->isStatic,Relationship::Related,
6127         mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6128     auto mmd = toMemberDefMutable(md.get());
6129     mmd->setTagInfo(root->tagInfo());
6130     mmd->setLanguage(root->lang);
6131     mmd->setId(root->id);
6132     mmd->setTypeConstraints(root->typeConstr);
6133     mmd->setMemberClass(cd);
6134     mmd->setDefinition(funcDecl);
6135     mmd->enableCallGraph(root->callGraph);
6136     mmd->enableCallerGraph(root->callerGraph);
6137     mmd->enableReferencedByRelation(root->referencedByRelation);
6138     mmd->enableReferencesRelation(root->referencesRelation);
6139     mmd->addQualifiers(root->qualifiers);
6140     QCString doc=getOverloadDocs();
6141     doc+="<p>";
6142     doc+=root->doc;
6143     mmd->setDocumentation(doc,root->docFile,root->docLine);
6144     mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6145     mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6146     mmd->setDocsForDefinition(!root->proto);
6147     mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6148     mmd->addSectionsToDefinition(root->anchors);
6149     mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6150     FileDef *fd=root->fileDef();
6151     mmd->setBodyDef(fd);
6152     mmd->setMemberSpecifiers(spec);
6153     mmd->setMemberGroupId(root->mGrpId);
6154     cd->insertMember(md.get());
6155     cd->insertUsedFile(fd);
6156     mmd->setRefItems(root->sli);
6157
6158     mn->push_back(std::move(md));
6159   }
6160 }
6161
6162 //-------------------------------------------------------------------------------------------
6163
6164 /*! This function tries to find a member (in a documented class/file/namespace)
6165  * that corresponds to the function/variable declaration given in \a funcDecl.
6166  *
6167  * The boolean \a overloaded is used to specify whether or not a standard
6168  * overload documentation line should be generated.
6169  *
6170  * The boolean \a isFunc is a hint that indicates that this is a function
6171  * instead of a variable or typedef.
6172  */
6173 static void findMember(const Entry *root,
6174                        const QCString &relates,
6175                        const QCString &type,
6176                        const QCString &args,
6177                        QCString funcDecl,
6178                        bool overloaded,
6179                        bool isFunc
6180                       )
6181 {
6182   AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={:#x} lang={}",
6183                root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6184                root->spec, root->lang);
6185
6186   QCString scopeName;
6187   QCString className;
6188   QCString namespaceName;
6189   QCString funcType;
6190   QCString funcName;
6191   QCString funcArgs;
6192   QCString funcTempList;
6193   QCString exceptions;
6194   QCString funcSpec;
6195   bool isRelated=FALSE;
6196   bool isMemberOf=FALSE;
6197   bool isFriend=FALSE;
6198   bool done;
6199   uint64_t spec = root->spec;
6200   do
6201   {
6202     done=TRUE;
6203     if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6204     {
6205       isFriend=TRUE;
6206       done=FALSE;
6207     }
6208     if (funcDecl.stripPrefix("inline "))
6209     {
6210       spec|=Entry::Inline;
6211       done=FALSE;
6212     }
6213     if (funcDecl.stripPrefix("explicit "))
6214     {
6215       spec|=Entry::Explicit;
6216       done=FALSE;
6217     }
6218     if (funcDecl.stripPrefix("mutable "))
6219     {
6220       spec|=Entry::Mutable;
6221       done=FALSE;
6222     }
6223     if (funcDecl.stripPrefix("virtual "))
6224     {
6225       done=FALSE;
6226     }
6227   } while (!done);
6228
6229   // delete any ; from the function declaration
6230   int sep;
6231   while ((sep=funcDecl.find(';'))!=-1)
6232   {
6233     funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6234   }
6235
6236   // make sure the first character is a space to simplify searching.
6237   if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6238
6239   // remove some superfluous spaces
6240   funcDecl= substitute(
6241               substitute(
6242                 substitute(funcDecl,"~ ","~"),
6243                 ":: ","::"
6244               ),
6245               " ::","::"
6246             ).stripWhiteSpace();
6247
6248   //printf("funcDecl='%s'\n",qPrint(funcDecl));
6249   if (isFriend && funcDecl.startsWith("class "))
6250   {
6251     //printf("friend class\n");
6252     funcDecl=funcDecl.right(funcDecl.length()-6);
6253     funcName = funcDecl;
6254   }
6255   else if (isFriend && funcDecl.startsWith("struct "))
6256   {
6257     funcDecl=funcDecl.right(funcDecl.length()-7);
6258     funcName = funcDecl;
6259   }
6260   else
6261   {
6262     // extract information from the declarations
6263     parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6264                 funcArgs,funcTempList,exceptions
6265                );
6266   }
6267
6268   // the class name can also be a namespace name, we decide this later.
6269   // if a related class name is specified and the class name could
6270   // not be derived from the function declaration, then use the
6271   // related field.
6272   AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6273                  scopeName,className,namespaceName,funcType,funcName,funcArgs);
6274   if (!relates.isEmpty())
6275   {                             // related member, prefix user specified scope
6276     isRelated=TRUE;
6277     isMemberOf=(root->relatesType == RelatesType::MemberOf);
6278     if (getClass(relates)==0 && !scopeName.isEmpty())
6279     {
6280       scopeName= mergeScopes(scopeName,relates);
6281     }
6282     else
6283     {
6284       scopeName = relates;
6285     }
6286   }
6287
6288   if (relates.isEmpty() && root->parent() &&
6289       ((root->parent()->section&Entry::SCOPE_MASK) ||
6290        (root->parent()->section==Entry::OBJCIMPL_SEC)
6291       ) &&
6292       !root->parent()->name.isEmpty()) // see if we can combine scopeName
6293                                      // with the scope in which it was found
6294   {
6295     QCString joinedName = root->parent()->name+"::"+scopeName;
6296     if (!scopeName.isEmpty() &&
6297         (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6298     {
6299       scopeName = joinedName;
6300     }
6301     else
6302     {
6303       scopeName = mergeScopes(root->parent()->name,scopeName);
6304     }
6305   }
6306   else // see if we can prefix a namespace or class that is used from the file
6307   {
6308      FileDef *fd=root->fileDef();
6309      if (fd)
6310      {
6311        for (const auto &fnd : fd->getUsedNamespaces())
6312        {
6313          QCString joinedName = fnd->name()+"::"+scopeName;
6314          if (Doxygen::namespaceLinkedMap->find(joinedName))
6315          {
6316            scopeName=joinedName;
6317            break;
6318          }
6319        }
6320      }
6321   }
6322   scopeName=stripTemplateSpecifiersFromScope(
6323       removeRedundantWhiteSpace(scopeName),FALSE,&funcSpec);
6324
6325   // funcSpec contains the last template specifiers of the given scope.
6326   // If this method does not have any template arguments or they are
6327   // empty while funcSpec is not empty we assume this is a
6328   // specialization of a method. If not, we clear the funcSpec and treat
6329   // this as a normal method of a template class.
6330   if (!(root->tArgLists.size()>0 &&
6331         root->tArgLists.front().size()==0
6332        )
6333      )
6334   {
6335     funcSpec.resize(0);
6336   }
6337
6338   // split scope into a namespace and a class part
6339   extractNamespaceName(scopeName,className,namespaceName,TRUE);
6340   AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6341
6342   //namespaceName=removeAnonymousScopes(namespaceName);
6343   if (namespaceName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6344
6345   //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6346   // merge class and namespace scopes again
6347   scopeName.resize(0);
6348   if (!namespaceName.isEmpty())
6349   {
6350     if (className.isEmpty())
6351     {
6352       scopeName=namespaceName;
6353     }
6354     else if (!relates.isEmpty() || // relates command with explicit scope
6355              !getClass(className)) // class name only exists in a namespace
6356     {
6357       scopeName=namespaceName+"::"+className;
6358     }
6359     else
6360     {
6361       scopeName=className;
6362     }
6363   }
6364   else if (!className.isEmpty())
6365   {
6366     scopeName=className;
6367   }
6368   //printf("new scope='%s'\n",qPrint(scopeName));
6369
6370   QCString tempScopeName=scopeName;
6371   ClassDefMutable *cd=getClassMutable(scopeName);
6372   if (cd)
6373   {
6374     if (funcSpec.isEmpty())
6375     {
6376       uint32_t argListIndex=0;
6377       tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6378     }
6379     else
6380     {
6381       tempScopeName=scopeName+funcSpec;
6382     }
6383   }
6384   //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6385   //    qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6386
6387   //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6388   // rebuild the function declaration (needed to get the scope right).
6389   if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6390   {
6391     if (!funcType.isEmpty())
6392     {
6393       if (isFunc) // a function -> we use argList for the arguments
6394       {
6395         funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6396       }
6397       else
6398       {
6399         funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6400       }
6401     }
6402     else
6403     {
6404       if (isFunc) // a function => we use argList for the arguments
6405       {
6406         funcDecl=tempScopeName+"::"+funcName+funcTempList;
6407       }
6408       else // variable => add 'argument' list
6409       {
6410         funcDecl=tempScopeName+"::"+funcName+funcArgs;
6411       }
6412     }
6413   }
6414   else // build declaration without scope
6415   {
6416     if (!funcType.isEmpty()) // but with a type
6417     {
6418       if (isFunc) // function => omit argument list
6419       {
6420         funcDecl=funcType+" "+funcName+funcTempList;
6421       }
6422       else // variable => add 'argument' list
6423       {
6424         funcDecl=funcType+" "+funcName+funcArgs;
6425       }
6426     }
6427     else // no type
6428     {
6429       if (isFunc)
6430       {
6431         funcDecl=funcName+funcTempList;
6432       }
6433       else
6434       {
6435         funcDecl=funcName+funcArgs;
6436       }
6437     }
6438   }
6439
6440   if (funcType=="template class" && !funcTempList.isEmpty())
6441     return;   // ignore explicit template instantiations
6442
6443   AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6444            " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6445            " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6446            namespaceName, className, funcType, funcSpec,
6447            funcName, funcArgs, funcTempList, funcDecl, relates,
6448            exceptions, isRelated, isMemberOf, isFriend, isFunc);
6449
6450   if (!funcName.isEmpty()) // function name is valid
6451   {
6452     // check if 'className' is actually a scoped enum, in which case we need to
6453     // process it as a global, see issue #6471
6454     bool strongEnum = false;
6455     MemberName *mn=0;
6456     if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6457     {
6458       for (const auto &imd : *mn)
6459       {
6460         MemberDefMutable *md = toMemberDefMutable(imd.get());
6461         Definition *mdScope = nullptr;
6462         if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
6463             // need filter for the correct scope, see issue #9668
6464             ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
6465         {
6466           AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
6467           strongEnum = true;
6468           // pass the scope name name as a 'namespace' to the findGlobalMember function
6469           if (!namespaceName.isEmpty())
6470           {
6471             namespaceName+="::"+className;
6472           }
6473           else
6474           {
6475             namespaceName=className;
6476           }
6477         }
6478       }
6479     }
6480
6481     if (funcName.startsWith("operator ")) // strip class scope from cast operator
6482     {
6483       funcName = substitute(funcName,className+"::","");
6484     }
6485     mn = 0;
6486     if (!funcTempList.isEmpty()) // try with member specialization
6487     {
6488       mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6489     }
6490     if (mn==0) // try without specialization
6491     {
6492       mn=Doxygen::memberNameLinkedMap->find(funcName);
6493     }
6494     if (!isRelated && !strongEnum && mn) // function name already found
6495     {
6496       AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
6497       if (!className.isEmpty()) // class name is valid
6498       {
6499         if (funcSpec.isEmpty()) // not a member specialization
6500         {
6501           addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6502                             funcArgs,funcTempList,exceptions,
6503                             type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6504         }
6505         else if (cd) // member specialization
6506         {
6507           addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6508         }
6509         else
6510         {
6511           //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6512           //        qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6513         }
6514       }
6515       else if (overloaded) // check if the function belongs to only one class
6516       {
6517         addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6518       }
6519       else // unrelated function with the same name as a member
6520       {
6521         if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6522         {
6523           QCString fullFuncDecl=funcDecl;
6524           if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6525           warn(root->fileName,root->startLine,
6526                "Cannot determine class for function\n%s",
6527                qPrint(fullFuncDecl)
6528               );
6529         }
6530       }
6531     }
6532     else if (isRelated && !relates.isEmpty())
6533     {
6534       AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
6535       if (className.isEmpty()) className=relates;
6536       //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6537       if ((cd=getClassMutable(scopeName)))
6538       {
6539         bool newMember=TRUE; // assume we have a new member
6540         MemberDefMutable *mdDefine=0;
6541         {
6542           mn = Doxygen::functionNameLinkedMap->find(funcName);
6543           if (mn)
6544           {
6545             for (const auto &imd : *mn)
6546             {
6547               MemberDefMutable *md = toMemberDefMutable(imd.get());
6548               if (md && md->isDefine())
6549               {
6550                 mdDefine = md;
6551                 break;
6552               }
6553             }
6554           }
6555         }
6556
6557         FileDef *fd=root->fileDef();
6558
6559         if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==0)
6560         {
6561           mn=Doxygen::memberNameLinkedMap->add(funcName);
6562         }
6563         else
6564         {
6565           // see if we got another member with matching arguments
6566           MemberDefMutable *rmd_found = 0;
6567           for (const auto &irmd : *mn)
6568           {
6569             MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6570             if (rmd)
6571             {
6572               const ArgumentList &rmdAl = rmd->argumentList();
6573
6574               newMember=
6575                 className!=rmd->getOuterScope()->name() ||
6576                 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6577                     cd,fd,&root->argList,
6578                     TRUE,root->lang);
6579               if (!newMember)
6580               {
6581                 rmd_found = rmd;
6582               }
6583             }
6584           }
6585           if (rmd_found) // member already exists as rmd -> add docs
6586           {
6587             //printf("addMemberDocs for related member %s\n",qPrint(root->name));
6588             //rmd->setMemberDefTemplateArguments(root->mtArgList);
6589             addMemberDocs(root,rmd_found,funcDecl,0,overloaded,spec);
6590           }
6591         }
6592
6593         if (newMember) // need to create a new member
6594         {
6595           MemberType mtype;
6596           if (mdDefine)
6597             mtype=MemberType_Define;
6598           else if (root->mtype==MethodTypes::Signal)
6599             mtype=MemberType_Signal;
6600           else if (root->mtype==MethodTypes::Slot)
6601             mtype=MemberType_Slot;
6602           else if (root->mtype==MethodTypes::DCOP)
6603             mtype=MemberType_DCOP;
6604           else
6605             mtype=MemberType_Function;
6606
6607           if (mdDefine)
6608           {
6609             mdDefine->setHidden(TRUE);
6610             funcType="#define";
6611             funcArgs=mdDefine->argsString();
6612             funcDecl=funcType + " " + funcName;
6613           }
6614
6615           //printf("New related name '%s' '%d'\n",qPrint(funcName),
6616           //    root->argList ? (int)root->argList->count() : -1);
6617
6618           // first note that we pass:
6619           //   (root->tArgLists ? root->tArgLists->last() : 0)
6620           // for the template arguments for the new "member."
6621           // this accurately reflects the template arguments of
6622           // the related function, which don't have to do with
6623           // those of the related class.
6624           auto md = createMemberDef(
6625               root->fileName,root->startLine,root->startColumn,
6626               funcType,funcName,funcArgs,exceptions,
6627               root->protection,root->virt,
6628               root->isStatic,
6629               isMemberOf ? Relationship::Foreign : Relationship::Related,
6630               mtype,
6631               (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
6632               funcArgs.isEmpty() ? ArgumentList() : root->argList,
6633               root->metaData);
6634           auto mmd = toMemberDefMutable(md.get());
6635
6636           if (mdDefine)
6637           {
6638             mmd->setInitializer(mdDefine->initializer());
6639           }
6640
6641           //
6642           // we still have the problem that
6643           // MemberDef::writeDocumentation() in memberdef.cpp
6644           // writes the template argument list for the class,
6645           // as if this member is a member of the class.
6646           // fortunately, MemberDef::writeDocumentation() has
6647           // a special mechanism that allows us to totally
6648           // override the set of template argument lists that
6649           // are printed.  We use that and set it to the
6650           // template argument lists of the related function.
6651           //
6652           mmd->setDefinitionTemplateParameterLists(root->tArgLists);
6653
6654           mmd->setTagInfo(root->tagInfo());
6655
6656           //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
6657           //       qPrint(funcName),qPrint(funcDecl),root->bodyLine);
6658
6659           // try to find the matching line number of the body from the
6660           // global function list
6661           bool found=FALSE;
6662           if (root->bodyLine==-1)
6663           {
6664             MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
6665             if (rmn)
6666             {
6667               const MemberDefMutable *rmd_found=0;
6668               for (const auto &irmd : *rmn)
6669               {
6670                 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6671                 if (rmd)
6672                 {
6673                   const ArgumentList &rmdAl = rmd->argumentList();
6674                   // check for matching argument lists
6675                   if (
6676                       matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6677                         cd,fd,&root->argList,
6678                         TRUE,root->lang)
6679                      )
6680                   {
6681                     found=TRUE;
6682                     rmd_found = rmd;
6683                     break;
6684                   }
6685                 }
6686               }
6687               if (rmd_found) // member found -> copy line number info
6688               {
6689                 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
6690                 mmd->setBodyDef(rmd_found->getBodyDef());
6691                 //md->setBodyMember(rmd);
6692               }
6693             }
6694           }
6695           if (!found) // line number could not be found or is available in this
6696                       // entry
6697           {
6698             mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6699             mmd->setBodyDef(fd);
6700           }
6701
6702           //if (root->mGrpId!=-1)
6703           //{
6704           //  md->setMemberGroup(memberGroupDict[root->mGrpId]);
6705           //}
6706           mmd->setMemberClass(cd);
6707           mmd->setMemberSpecifiers(spec);
6708           mmd->setDefinition(funcDecl);
6709           mmd->enableCallGraph(root->callGraph);
6710           mmd->enableCallerGraph(root->callerGraph);
6711           mmd->enableReferencedByRelation(root->referencedByRelation);
6712           mmd->enableReferencesRelation(root->referencesRelation);
6713           mmd->addQualifiers(root->qualifiers);
6714           mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6715           mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6716           mmd->setDocsForDefinition(!root->proto);
6717           mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6718           mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6719           mmd->addSectionsToDefinition(root->anchors);
6720           mmd->setMemberGroupId(root->mGrpId);
6721           mmd->setLanguage(root->lang);
6722           mmd->setId(root->id);
6723           //md->setMemberDefTemplateArguments(root->mtArgList);
6724           cd->insertMember(md.get());
6725           cd->insertUsedFile(fd);
6726           mmd->setRefItems(root->sli);
6727           if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
6728           if (!mdDefine)
6729           {
6730             addMemberToGroups(root,md.get());
6731             ModuleManager::instance().addMemberToModule(root,md.get());
6732           }
6733           //printf("Adding member=%s\n",qPrint(md->name()));
6734           mn->push_back(std::move(md));
6735         }
6736         if (root->relatesType==RelatesType::Duplicate)
6737         {
6738           if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6739           {
6740             QCString fullFuncDecl=funcDecl;
6741             if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6742             warn(root->fileName,root->startLine,
6743                "Cannot determine file/namespace for relatedalso function\n%s",
6744                qPrint(fullFuncDecl)
6745               );
6746           }
6747         }
6748       }
6749       else
6750       {
6751         warn_undoc(root->fileName,root->startLine,
6752                    "class '%s' for related function '%s' is not "
6753                    "documented.",
6754                    qPrint(className),qPrint(funcName)
6755                   );
6756       }
6757     }
6758     else if (root->parent() && root->parent()->section==Entry::OBJCIMPL_SEC)
6759     {
6760       addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6761     }
6762     else // unrelated not overloaded member found
6763     {
6764       bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
6765       if (className.isEmpty() && !globMem)
6766       {
6767         warn(root->fileName,root->startLine,
6768              "class for member '%s' cannot "
6769              "be found.", qPrint(funcName)
6770             );
6771       }
6772       else if (!className.isEmpty() && !globMem)
6773       {
6774         warn(root->fileName,root->startLine,
6775              "member '%s' of class '%s' cannot be found",
6776              qPrint(funcName),qPrint(className));
6777       }
6778     }
6779   }
6780   else
6781   {
6782     // this should not be called
6783     warn(root->fileName,root->startLine,
6784          "member with no name found.");
6785   }
6786   return;
6787 }
6788
6789 //----------------------------------------------------------------------
6790 // find the members corresponding to the different documentation blocks
6791 // that are extracted from the sources.
6792
6793 static void filterMemberDocumentation(const Entry *root,const QCString &relates)
6794 {
6795   int i=-1,l;
6796   AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={:#x} root->spec={:#x} root->mGrpId={}",
6797       root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
6798   //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
6799   bool isFunc=TRUE;
6800
6801   QCString type = root->type;
6802   QCString args = root->args;
6803   if ( // detect func variable/typedef to func ptr
6804       (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
6805      )
6806   {
6807     //printf("Fixing function pointer!\n");
6808     // fix type and argument
6809     args.prepend(type.right(type.length()-i-l));
6810     type=type.left(i+l);
6811     //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
6812     isFunc=FALSE;
6813   }
6814   else if ((type.startsWith("typedef ") && args.find('(')!=-1))
6815     // detect function types marked as functions
6816   {
6817     isFunc=FALSE;
6818   }
6819
6820   //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
6821   if (root->section==Entry::MEMBERDOC_SEC)
6822   {
6823     //printf("Documentation for inline member '%s' found args='%s'\n",
6824     //    qPrint(root->name),qPrint(args));
6825     //if (relates.length()) printf("  Relates %s\n",qPrint(relates));
6826     if (type.isEmpty())
6827     {
6828       findMember(root,
6829                  relates,
6830                  type,
6831                  args,
6832                  root->name + args + root->exception,
6833                  FALSE,
6834                  isFunc);
6835     }
6836     else
6837     {
6838       findMember(root,
6839                  relates,
6840                  type,
6841                  args,
6842                  type + " " + root->name + args + root->exception,
6843                  FALSE,
6844                  isFunc);
6845     }
6846   }
6847   else if (root->section==Entry::OVERLOADDOC_SEC)
6848   {
6849     //printf("Overloaded member %s found\n",qPrint(root->name));
6850     findMember(root,
6851                relates,
6852                type,
6853                args,
6854                root->name,
6855                TRUE,
6856                isFunc);
6857   }
6858   else if
6859     ((root->section==Entry::FUNCTION_SEC      // function
6860       ||
6861       (root->section==Entry::VARIABLE_SEC &&  // variable
6862        !type.isEmpty() &&                // with a type
6863        g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
6864        // (to skip forward declaration of class etc.)
6865       )
6866      )
6867     )
6868     {
6869       //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
6870       //    qPrint(root->name),qPrint(args),qPrint(root->exception));
6871       //if (relates.length()) printf("  Relates %s\n",qPrint(relates));
6872       //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
6873       if (type=="friend class" || type=="friend struct" ||
6874           type=="friend union")
6875       {
6876         findMember(root,
6877             relates,
6878             type,
6879             args,
6880             type+" "+root->name,
6881             FALSE,FALSE);
6882
6883       }
6884       else if (!type.isEmpty())
6885       {
6886         findMember(root,
6887             relates,
6888             type,
6889             args,
6890             type+" "+ root->inside + root->name + args + root->exception,
6891             FALSE,isFunc);
6892       }
6893       else
6894       {
6895         findMember(root,
6896             relates,
6897             type,
6898             args,
6899             root->inside + root->name + args + root->exception,
6900             FALSE,isFunc);
6901       }
6902     }
6903   else if (root->section==Entry::DEFINE_SEC && !relates.isEmpty())
6904   {
6905     findMember(root,
6906                relates,
6907                type,
6908                args,
6909                root->name + args,
6910                FALSE,
6911                !args.isEmpty());
6912   }
6913   else if (root->section==Entry::VARIABLEDOC_SEC)
6914   {
6915     //printf("Documentation for variable %s found\n",qPrint(root->name));
6916     //if (!relates.isEmpty()) printf("  Relates %s\n",qPrint(relates));
6917     findMember(root,
6918                relates,
6919                type,
6920                args,
6921                root->name,
6922                FALSE,
6923                FALSE);
6924   }
6925   else if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
6926            root->section==Entry::INCLUDED_SERVICE_SEC)
6927   {
6928     findMember(root,
6929                relates,
6930                type,
6931                args,
6932                type + " " + root->name,
6933                FALSE,
6934                FALSE);
6935   }
6936   else
6937   {
6938     // skip section
6939     //printf("skip section\n");
6940   }
6941 }
6942
6943 static void findMemberDocumentation(const Entry *root)
6944 {
6945   if (root->section==Entry::MEMBERDOC_SEC ||
6946       root->section==Entry::OVERLOADDOC_SEC ||
6947       root->section==Entry::FUNCTION_SEC ||
6948       root->section==Entry::VARIABLE_SEC ||
6949       root->section==Entry::VARIABLEDOC_SEC ||
6950       root->section==Entry::DEFINE_SEC ||
6951       root->section==Entry::INCLUDED_SERVICE_SEC ||
6952       root->section==Entry::EXPORTED_INTERFACE_SEC
6953      )
6954   {
6955     AUTO_TRACE();
6956     if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
6957     {
6958       filterMemberDocumentation(root,"");
6959     }
6960     filterMemberDocumentation(root,root->relates);
6961   }
6962   for (const auto &e : root->children())
6963   {
6964     if (e->section!=Entry::ENUM_SEC)
6965     {
6966       findMemberDocumentation(e.get());
6967     }
6968   }
6969 }
6970
6971 //----------------------------------------------------------------------
6972
6973 static void findObjCMethodDefinitions(const Entry *root)
6974 {
6975   AUTO_TRACE();
6976   for (const auto &objCImpl : root->children())
6977   {
6978     if (objCImpl->section==Entry::OBJCIMPL_SEC)
6979     {
6980       for (const auto &objCMethod : objCImpl->children())
6981       {
6982         if (objCMethod->section==Entry::FUNCTION_SEC)
6983         {
6984           //Printf("  Found ObjC method definition %s\n",qPrint(objCMethod->name));
6985           findMember(objCMethod.get(),
6986                      objCMethod->relates,
6987                      objCMethod->type,
6988                      objCMethod->args,
6989                      objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
6990                      FALSE,TRUE);
6991           objCMethod->section=Entry::EMPTY_SEC;
6992         }
6993       }
6994     }
6995   }
6996 }
6997
6998 //----------------------------------------------------------------------
6999 // find and add the enumeration to their classes, namespaces or files
7000
7001 static void findEnums(const Entry *root)
7002 {
7003   if (root->section==Entry::ENUM_SEC)
7004   {
7005     ClassDefMutable *cd=0;
7006     FileDef         *fd=0;
7007     NamespaceDefMutable *nd=0;
7008     MemberNameLinkedMap *mnsd=0;
7009     bool isGlobal;
7010     bool isRelated=FALSE;
7011     bool isMemberOf=FALSE;
7012     //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7013     int i;
7014
7015     QCString name;
7016     QCString scope;
7017
7018     if ((i=root->name.findRev("::"))!=-1) // scope is specified
7019     {
7020       scope=root->name.left(i); // extract scope
7021       name=root->name.right(root->name.length()-i-2); // extract name
7022       if ((cd=getClassMutable(scope))==0)
7023       {
7024         nd=toNamespaceDefMutable(buildScopeFromQualifiedName(root->name.left(i+2),root->lang,root->tagInfo()));
7025       }
7026     }
7027     else // no scope, check the scope in which the docs where found
7028     {
7029       if (( root->parent()->section & Entry::SCOPE_MASK )
7030           && !root->parent()->name.isEmpty()
7031          ) // found enum docs inside a compound
7032       {
7033         scope=root->parent()->name;
7034         if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7035       }
7036       name=root->name;
7037     }
7038
7039     if (!root->relates.isEmpty())
7040     {   // related member, prefix user specified scope
7041       isRelated=TRUE;
7042       isMemberOf=(root->relatesType==RelatesType::MemberOf);
7043       if (getClass(root->relates)==0 && !scope.isEmpty())
7044         scope=mergeScopes(scope,root->relates);
7045       else
7046         scope=root->relates;
7047       if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7048     }
7049
7050     if (cd && !name.isEmpty()) // found a enum inside a compound
7051     {
7052       //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7053       fd=0;
7054       mnsd=Doxygen::memberNameLinkedMap;
7055       isGlobal=FALSE;
7056     }
7057     else if (nd) // found enum inside namespace
7058     {
7059       mnsd=Doxygen::functionNameLinkedMap;
7060       isGlobal=TRUE;
7061     }
7062     else // found a global enum
7063     {
7064       fd=root->fileDef();
7065       mnsd=Doxygen::functionNameLinkedMap;
7066       isGlobal=TRUE;
7067     }
7068
7069     if (!name.isEmpty())
7070     {
7071       // new enum type
7072       auto md = createMemberDef(
7073           root->fileName,root->startLine,root->startColumn,
7074           QCString(),name,QCString(),QCString(),
7075           root->protection,Specifier::Normal,FALSE,
7076           isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7077           MemberType_Enumeration,
7078           ArgumentList(),ArgumentList(),root->metaData);
7079       auto mmd = toMemberDefMutable(md.get());
7080       mmd->setTagInfo(root->tagInfo());
7081       mmd->setLanguage(root->lang);
7082       mmd->setId(root->id);
7083       if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7084       mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7085       mmd->setBodyDef(root->fileDef());
7086       mmd->setMemberSpecifiers(root->spec);
7087       mmd->setEnumBaseType(root->args);
7088       //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7089       //    qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7090       mmd->addSectionsToDefinition(root->anchors);
7091       mmd->setMemberGroupId(root->mGrpId);
7092       mmd->enableCallGraph(root->callGraph);
7093       mmd->enableCallerGraph(root->callerGraph);
7094       mmd->enableReferencedByRelation(root->referencedByRelation);
7095       mmd->enableReferencesRelation(root->referencesRelation);
7096       mmd->addQualifiers(root->qualifiers);
7097       //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7098       mmd->setRefItems(root->sli);
7099       //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7100       bool defSet=FALSE;
7101
7102       QCString baseType = root->args;
7103       if (!baseType.isEmpty())
7104       {
7105         baseType.prepend(" : ");
7106       }
7107
7108       if (nd)
7109       {
7110         if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7111         {
7112           mmd->setDefinition(name+baseType);
7113         }
7114         else
7115         {
7116           mmd->setDefinition(nd->name()+"::"+name+baseType);
7117         }
7118         //printf("definition=%s\n",md->definition());
7119         defSet=TRUE;
7120         mmd->setNamespace(nd);
7121         nd->insertMember(md.get());
7122       }
7123
7124       // even if we have already added the enum to a namespace, we still
7125       // also want to add it to other appropriate places such as file
7126       // or class.
7127       if (isGlobal && (nd==0 || !nd->isAnonymous()))
7128       {
7129         if (!defSet) mmd->setDefinition(name+baseType);
7130         if (fd==0 && root->parent())
7131         {
7132           fd=root->parent()->fileDef();
7133         }
7134         if (fd)
7135         {
7136           mmd->setFileDef(fd);
7137           fd->insertMember(md.get());
7138         }
7139       }
7140       else if (cd)
7141       {
7142         if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7143         {
7144           mmd->setDefinition(name+baseType);
7145         }
7146         else
7147         {
7148           mmd->setDefinition(cd->name()+"::"+name+baseType);
7149         }
7150         cd->insertMember(md.get());
7151         cd->insertUsedFile(fd);
7152       }
7153       mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7154       mmd->setDocsForDefinition(!root->proto);
7155       mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7156       mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7157
7158       //printf("Adding member=%s\n",qPrint(md->name()));
7159       addMemberToGroups(root,md.get());
7160       ModuleManager::instance().addMemberToModule(root,md.get());
7161
7162       MemberName *mn = mnsd->add(name);
7163       mn->push_back(std::move(md));
7164     }
7165   }
7166   else
7167   {
7168     for (const auto &e : root->children()) findEnums(e.get());
7169   }
7170 }
7171
7172 //----------------------------------------------------------------------
7173
7174 static void addEnumValuesToEnums(const Entry *root)
7175 {
7176   if (root->section==Entry::ENUM_SEC)
7177     // non anonymous enumeration
7178   {
7179     ClassDefMutable     *cd=0;
7180     FileDef             *fd=0;
7181     NamespaceDefMutable *nd=0;
7182     MemberNameLinkedMap *mnsd=0;
7183     bool isGlobal;
7184     bool isRelated=FALSE;
7185     //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7186     int i;
7187
7188     QCString name;
7189     QCString scope;
7190
7191     if ((i=root->name.findRev("::"))!=-1) // scope is specified
7192     {
7193       scope=root->name.left(i); // extract scope
7194       name=root->name.right(root->name.length()-i-2); // extract name
7195       if ((cd=getClassMutable(scope))==0)
7196       {
7197         nd=toNamespaceDefMutable(buildScopeFromQualifiedName(root->name.left(i+2),root->lang,root->tagInfo()));
7198       }
7199     }
7200     else // no scope, check the scope in which the docs where found
7201     {
7202       if (( root->parent()->section & Entry::SCOPE_MASK )
7203           && !root->parent()->name.isEmpty()
7204          ) // found enum docs inside a compound
7205       {
7206         scope=root->parent()->name;
7207         if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7208       }
7209       name=root->name;
7210     }
7211
7212     if (!root->relates.isEmpty())
7213     {   // related member, prefix user specified scope
7214       isRelated=TRUE;
7215       if (getClassMutable(root->relates)==0 && !scope.isEmpty())
7216         scope=mergeScopes(scope,root->relates);
7217       else
7218         scope=root->relates;
7219       if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7220     }
7221
7222     if (cd && !name.isEmpty()) // found a enum inside a compound
7223     {
7224       //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7225       fd=0;
7226       mnsd=Doxygen::memberNameLinkedMap;
7227       isGlobal=FALSE;
7228     }
7229     else if (nd && !nd->isAnonymous()) // found enum inside namespace
7230     {
7231       //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7232       mnsd=Doxygen::functionNameLinkedMap;
7233       isGlobal=TRUE;
7234     }
7235     else // found a global enum
7236     {
7237       fd=root->fileDef();
7238       //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7239       mnsd=Doxygen::functionNameLinkedMap;
7240       isGlobal=TRUE;
7241     }
7242
7243     if (!name.isEmpty())
7244     {
7245       //printf("** name=%s\n",qPrint(name));
7246       MemberName *mn = mnsd->find(name); // for all members with this name
7247       if (mn)
7248       {
7249         struct EnumValueInfo
7250         {
7251           EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7252             name(n), member(std::move(md)) {}
7253           QCString name;
7254           std::unique_ptr<MemberDef> member;
7255         };
7256         std::vector< EnumValueInfo > extraMembers;
7257         // for each enum in this list
7258         for (const auto &imd : *mn)
7259         {
7260           MemberDefMutable *md = toMemberDefMutable(imd.get());
7261           // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7262           if (md && md->isEnumerate() && !root->children().empty())
7263           {
7264             //printf("   enum with %zu children\n",root->children().size());
7265             for (const auto &e : root->children())
7266             {
7267               SrcLangExt sle = root->lang;
7268               bool isJavaLike = sle==SrcLangExt_CSharp || sle==SrcLangExt_Java || sle==SrcLangExt_XML;
7269               if ( isJavaLike ||
7270                    (root->spec&Entry::Strong)
7271                  )
7272               {
7273                 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7274                 // values are only visible inside the enum scope, so we must create
7275                 // them here and only add them to the enum
7276                 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7277                 //    qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7278                 QCString qualifiedName = root->name;
7279                 if (isJavaLike)
7280                 {
7281                   qualifiedName=substitute(qualifiedName,"::",".");
7282                 }
7283                 if (md->qualifiedName()==qualifiedName)       // enum value scope matches that of the enum
7284                 {
7285                   QCString fileName = e->fileName;
7286                   if (fileName.isEmpty() && e->tagInfo())
7287                   {
7288                     fileName = e->tagInfo()->tagName;
7289                   }
7290                   auto fmd = createMemberDef(
7291                       fileName,e->startLine,e->startColumn,
7292                       e->type,e->name,e->args,QCString(),
7293                       e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7294                       MemberType_EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7295                   auto fmmd = toMemberDefMutable(fmd.get());
7296                   NamespaceDef *mnd = md->getNamespaceDef();
7297                   if      (md->getClassDef())
7298                     fmmd->setMemberClass(md->getClassDef());
7299                   else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7300                     fmmd->setNamespace(mnd);
7301                   else if (md->getFileDef())
7302                     fmmd->setFileDef(md->getFileDef());
7303                   fmmd->setOuterScope(md->getOuterScope());
7304                   fmmd->setTagInfo(e->tagInfo());
7305                   fmmd->setLanguage(e->lang);
7306                   fmmd->setId(e->id);
7307                   fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7308                   fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7309                   fmmd->addSectionsToDefinition(e->anchors);
7310                   std::string init = e->initializer.str();
7311                   fmmd->setInitializer(init.c_str());
7312                   fmmd->setMaxInitLines(e->initLines);
7313                   fmmd->setMemberGroupId(e->mGrpId);
7314                   fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7315                   fmmd->setRefItems(e->sli);
7316                   fmmd->setAnchor();
7317                   md->insertEnumField(fmd.get());
7318                   fmmd->setEnumScope(md,TRUE);
7319                   extraMembers.push_back(EnumValueInfo(e->name,std::move(fmd)));
7320                 }
7321               }
7322               else
7323               {
7324                 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7325                 MemberName *fmn=0;
7326                 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7327                 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7328                   // get list of members with the same name as the field
7329                 {
7330                   for (const auto &ifmd : *fmn)
7331                   {
7332                     MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7333                     if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7334                     {
7335                       //printf("found enum value with same name %s in scope %s\n",
7336                       //    qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7337                       if (nd && !nd->isAnonymous())
7338                       {
7339                         if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7340                         {
7341                           const NamespaceDef *fnd=fmd->getNamespaceDef();
7342                           if (fnd==nd) // enum value is inside a namespace
7343                           {
7344                             md->insertEnumField(fmd);
7345                             fmd->setEnumScope(md);
7346                           }
7347                         }
7348                       }
7349                       else if (isGlobal)
7350                       {
7351                         if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7352                         {
7353                           const FileDef *ffd=fmd->getFileDef();
7354                           if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7355                           {
7356                             md->insertEnumField(fmd);
7357                             fmd->setEnumScope(md);
7358                           }
7359                         }
7360                       }
7361                       else if (isRelated && cd) // reparent enum value to
7362                                                 // match the enum's scope
7363                       {
7364                         md->insertEnumField(fmd);   // add field def to list
7365                         fmd->setEnumScope(md);      // cross ref with enum name
7366                         fmd->setEnumClassScope(cd); // cross ref with enum name
7367                         fmd->setOuterScope(cd);
7368                         fmd->makeRelated();
7369                         cd->insertMember(fmd);
7370                       }
7371                       else
7372                       {
7373                         if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7374                         {
7375                           const ClassDef *fcd=fmd->getClassDef();
7376                           if (fcd==cd) // enum value is inside a class
7377                           {
7378                             //printf("Inserting enum field %s in enum scope %s\n",
7379                             //    qPrint(fmd->name()),qPrint(md->name()));
7380                             md->insertEnumField(fmd); // add field def to list
7381                             fmd->setEnumScope(md);    // cross ref with enum name
7382                           }
7383                         }
7384                       }
7385                     }
7386                   }
7387                 }
7388               }
7389             }
7390           }
7391         }
7392         // move the newly added members into mn
7393         for (auto &e : extraMembers)
7394         {
7395           MemberName *emn=mnsd->add(e.name);
7396           emn->push_back(std::move(e.member));
7397         }
7398       }
7399     }
7400   }
7401   else
7402   {
7403     for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7404   }
7405 }
7406
7407 //----------------------------------------------------------------------
7408
7409 static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7410 {
7411   AUTO_TRACE();
7412   // documentation outside a compound overrides the documentation inside it
7413   {
7414     md->setDocumentation(root->doc,root->docFile,root->docLine);
7415     md->setDocsForDefinition(!root->proto);
7416   }
7417
7418   // brief descriptions inside a compound override the documentation
7419   // outside it
7420   {
7421     md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7422   }
7423
7424   if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7425   {
7426     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7427   }
7428
7429   if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7430   {
7431     md->setMemberGroupId(root->mGrpId);
7432   }
7433
7434   md->addSectionsToDefinition(root->anchors);
7435   md->setRefItems(root->sli);
7436
7437   const GroupDef *gd=md->getGroupDef();
7438   if (gd==0 && !root->groups.empty()) // member not grouped but out-of-line documentation is
7439   {
7440     addMemberToGroups(root,md);
7441   }
7442   ModuleManager::instance().addMemberToModule(root,md);
7443 }
7444
7445 //----------------------------------------------------------------------
7446 // Search for the name in the associated groups.  If a matching member
7447 // definition exists, then add the documentation to it and return TRUE,
7448 // otherwise FALSE.
7449
7450 static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
7451 {
7452   for (const auto &g : root->groups)
7453   {
7454     const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
7455     if (gd)
7456     {
7457       MemberList *ml = gd->getMemberList(MemberListType_decEnumMembers);
7458       if (ml)
7459       {
7460         MemberDefMutable *md = toMemberDefMutable(ml->find(name));
7461         if (md)
7462         {
7463           addEnumDocs(root,md);
7464           return TRUE;
7465         }
7466       }
7467     }
7468   }
7469
7470   return FALSE;
7471 }
7472
7473 //----------------------------------------------------------------------
7474 // find the documentation blocks for the enumerations
7475
7476 static void findEnumDocumentation(const Entry *root)
7477 {
7478   if (root->section==Entry::ENUMDOC_SEC
7479       && !root->name.isEmpty()
7480       && root->name.at(0)!='@'        // skip anonymous enums
7481      )
7482   {
7483     int i;
7484     QCString name;
7485     QCString scope;
7486     if ((i=root->name.findRev("::"))!=-1) // scope is specified as part of the name
7487     {
7488       name=root->name.right(root->name.length()-i-2); // extract name
7489       scope=root->name.left(i); // extract scope
7490       //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7491     }
7492     else // just the name
7493     {
7494       name=root->name;
7495     }
7496     if (( root->parent()->section & Entry::SCOPE_MASK )
7497         && !root->parent()->name.isEmpty()
7498        ) // found enum docs inside a compound
7499     {
7500       if (!scope.isEmpty()) scope.prepend("::");
7501       scope.prepend(root->parent()->name);
7502     }
7503     const ClassDef *cd = getClass(scope);
7504     const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
7505     const FileDef *fd = root->fileDef();
7506     AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
7507                  name,scope,root->parent()->name,
7508                  cd ? cd->name() : QCString("<none>"),
7509                  nd ? nd->name() : QCString("<none>"),
7510                  fd ? fd->name() : QCString("<none>"));
7511
7512     if (!name.isEmpty())
7513     {
7514       bool found = FALSE;
7515       if (root->groups.empty())
7516       {
7517         MemberName *mn;
7518         if (cd)
7519         {
7520           mn = Doxygen::memberNameLinkedMap->find(name);
7521         }
7522         else
7523         {
7524           mn = Doxygen::functionNameLinkedMap->find(name);
7525         }
7526         if (mn)
7527         {
7528           for (const auto &imd : *mn)
7529           {
7530             MemberDefMutable *md = toMemberDefMutable(imd.get());
7531             if (md && md->isEnumerate())
7532             {
7533               const ClassDef *mcd = md->getClassDef();
7534               const NamespaceDef *mnd = md->getNamespaceDef();
7535               const FileDef *mfd = md->getFileDef();
7536               if (cd && mcd==cd)
7537               {
7538                 AUTO_TRACE_ADD("Match found for class scope");
7539                 addEnumDocs(root,md);
7540                 found = TRUE;
7541                 break;
7542               }
7543               else if (cd==0 && mcd==0 && nd!=0 && mnd==nd)
7544               {
7545                 AUTO_TRACE_ADD("Match found for namespace scope");
7546                 addEnumDocs(root,md);
7547                 found = TRUE;
7548                 break;
7549               }
7550               else if (cd==0 && nd==0 && mcd==0 && mnd==0 && fd==mfd)
7551               {
7552                 AUTO_TRACE_ADD("Match found for global scope");
7553                 addEnumDocs(root,md);
7554                 found = TRUE;
7555                 break;
7556               }
7557             }
7558           }
7559         }
7560       }
7561       else
7562       {
7563         found = tryAddEnumDocsToGroupMember(root, name);
7564       }
7565       if (!found)
7566       {
7567         warn(root->fileName,root->startLine,
7568              "Documentation for undefined enum '%s' found.",
7569              qPrint(name)
7570             );
7571       }
7572     }
7573   }
7574   for (const auto &e : root->children()) findEnumDocumentation(e.get());
7575 }
7576
7577 // search for each enum (member or function) in mnl if it has documented
7578 // enum values.
7579 static void findDEV(const MemberNameLinkedMap &mnsd)
7580 {
7581   // for each member name
7582   for (const auto &mn : mnsd)
7583   {
7584     // for each member definition
7585     for (const auto &imd : *mn)
7586     {
7587       MemberDefMutable *md = toMemberDefMutable(imd.get());
7588       if (md && md->isEnumerate()) // member is an enum
7589       {
7590         int documentedEnumValues=0;
7591         // for each enum value
7592         for (const auto &fmd : md->enumFieldList())
7593         {
7594           if (fmd->isLinkableInProject()) documentedEnumValues++;
7595         }
7596         // at least one enum value is documented
7597         if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
7598       }
7599     }
7600   }
7601 }
7602
7603 // search for each enum (member or function) if it has documented enum
7604 // values.
7605 static void findDocumentedEnumValues()
7606 {
7607   findDEV(*Doxygen::memberNameLinkedMap);
7608   findDEV(*Doxygen::functionNameLinkedMap);
7609 }
7610
7611 //----------------------------------------------------------------------
7612
7613 static void addMembersToIndex()
7614 {
7615   auto &index = Index::instance();
7616   // for each class member name
7617   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7618   {
7619     // for each member definition
7620     for (const auto &md : *mn)
7621     {
7622       index.addClassMemberNameToIndex(md.get());
7623       if (md->getModuleDef())
7624       {
7625         index.addModuleMemberNameToIndex(md.get());
7626       }
7627     }
7628   }
7629   // for each file/namespace function name
7630   for (const auto &mn : *Doxygen::functionNameLinkedMap)
7631   {
7632     // for each member definition
7633     for (const auto &md : *mn)
7634     {
7635       if (md->getNamespaceDef())
7636       {
7637         index.addNamespaceMemberNameToIndex(md.get());
7638       }
7639       else
7640       {
7641         index.addFileMemberNameToIndex(md.get());
7642       }
7643       if (md->getModuleDef())
7644       {
7645         index.addModuleMemberNameToIndex(md.get());
7646       }
7647     }
7648   }
7649
7650   index.sortMemberIndexLists();
7651 }
7652
7653 //----------------------------------------------------------------------
7654
7655 static void addToIndices()
7656 {
7657   for (const auto &cd : *Doxygen::classLinkedMap)
7658   {
7659     if (cd->isLinkableInProject())
7660     {
7661       Doxygen::indexList->addIndexItem(cd.get(),0);
7662       if (Doxygen::searchIndex)
7663       {
7664         Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7665         Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7666       }
7667     }
7668   }
7669
7670   for (const auto &cd : *Doxygen::conceptLinkedMap)
7671   {
7672     if (cd->isLinkableInProject())
7673     {
7674       Doxygen::indexList->addIndexItem(cd.get(),0);
7675       if (Doxygen::searchIndex)
7676       {
7677         Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7678         Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7679       }
7680     }
7681   }
7682
7683   for (const auto &nd : *Doxygen::namespaceLinkedMap)
7684   {
7685     if (nd->isLinkableInProject())
7686     {
7687       Doxygen::indexList->addIndexItem(nd.get(),0);
7688       if (Doxygen::searchIndex)
7689       {
7690         Doxygen::searchIndex->setCurrentDoc(nd.get(),nd->anchor(),FALSE);
7691         Doxygen::searchIndex->addWord(nd->localName(),TRUE);
7692       }
7693     }
7694   }
7695
7696   for (const auto &fn : *Doxygen::inputNameLinkedMap)
7697   {
7698     for (const auto &fd : *fn)
7699     {
7700       if (Doxygen::searchIndex && fd->isLinkableInProject())
7701       {
7702         Doxygen::searchIndex->setCurrentDoc(fd.get(),fd->anchor(),FALSE);
7703         Doxygen::searchIndex->addWord(fd->localName(),TRUE);
7704       }
7705     }
7706   }
7707
7708   for (const auto &gd : *Doxygen::groupLinkedMap)
7709   {
7710     if (gd->isLinkableInProject())
7711     {
7712       Doxygen::indexList->addIndexItem(gd.get(),0,QCString(),gd->groupTitle());
7713       if (Doxygen::searchIndex)
7714       {
7715         Doxygen::searchIndex->setCurrentDoc(gd.get(),gd->anchor(),FALSE);
7716         std::string title = gd->groupTitle().str();
7717         static const reg::Ex re(R"(\a[\w-]*)");
7718         reg::Iterator it(title,re);
7719         reg::Iterator end;
7720         for (; it!=end ; ++it)
7721         {
7722           const auto &match = *it;
7723           std::string matchStr = match.str();
7724           Doxygen::searchIndex->addWord(matchStr.c_str(),TRUE);
7725         }
7726       }
7727     }
7728   }
7729
7730   for (const auto &pd : *Doxygen::pageLinkedMap)
7731   {
7732     if (pd->isLinkableInProject())
7733     {
7734       Doxygen::indexList->addIndexItem(pd.get(),0,QCString(),filterTitle(pd->title()));
7735     }
7736   }
7737
7738   auto addMemberToSearchIndex = [](const MemberDef *md)
7739   {
7740     if (Doxygen::searchIndex)
7741     {
7742       Doxygen::searchIndex->setCurrentDoc(md,md->anchor(),FALSE);
7743       QCString ln=md->localName();
7744       QCString qn=md->qualifiedName();
7745       Doxygen::searchIndex->addWord(ln,TRUE);
7746       if (ln!=qn)
7747       {
7748         Doxygen::searchIndex->addWord(qn,TRUE);
7749         if (md->getClassDef())
7750         {
7751           Doxygen::searchIndex->addWord(md->getClassDef()->displayName(),TRUE);
7752         }
7753         if (md->getNamespaceDef())
7754         {
7755           Doxygen::searchIndex->addWord(md->getNamespaceDef()->displayName(),TRUE);
7756         }
7757       }
7758     }
7759   };
7760
7761   auto getScope = [](const MemberDef *md)
7762   {
7763     const Definition *scope = 0;
7764     if (md->getGroupDef())          scope = md->getGroupDef();
7765     else if (md->getClassDef())     scope = md->getClassDef();
7766     else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
7767     else if (md->getFileDef())      scope = md->getFileDef();
7768     return scope;
7769   };
7770
7771   auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
7772   {
7773     if (md->isLinkableInProject())
7774     {
7775       if (!(md->isEnumerate() && md->isAnonymous()))
7776       {
7777         Doxygen::indexList->addIndexItem(getScope(md),md);
7778         addMemberToSearchIndex(md);
7779       }
7780       if (md->isEnumerate())
7781       {
7782         for (const auto &fmd : md->enumFieldList())
7783         {
7784           Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
7785           addMemberToSearchIndex(fmd);
7786         }
7787       }
7788     }
7789   };
7790
7791   // for each class member name
7792   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7793   {
7794     // for each member definition
7795     for (const auto &md : *mn)
7796     {
7797       addMemberToIndices(md.get());
7798     }
7799   }
7800   // for each file/namespace function name
7801   for (const auto &mn : *Doxygen::functionNameLinkedMap)
7802   {
7803     // for each member definition
7804     for (const auto &md : *mn)
7805     {
7806       addMemberToIndices(md.get());
7807     }
7808   }
7809 }
7810
7811 //----------------------------------------------------------------------
7812
7813 static void vhdlCorrectMemberProperties()
7814 {
7815   // for each member name
7816   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7817   {
7818     // for each member definition
7819     for (const auto &imd : *mn)
7820     {
7821       MemberDefMutable *md = toMemberDefMutable(imd.get());
7822       if (md)
7823       {
7824         VhdlDocGen::correctMemberProperties(md);
7825       }
7826     }
7827   }
7828   // for each member name
7829   for (const auto &mn : *Doxygen::functionNameLinkedMap)
7830   {
7831     // for each member definition
7832     for (const auto &imd : *mn)
7833     {
7834       MemberDefMutable *md = toMemberDefMutable(imd.get());
7835       if (md)
7836       {
7837         VhdlDocGen::correctMemberProperties(md);
7838       }
7839     }
7840   }
7841 }
7842
7843 // recursive helper function looking for reimplements/implemented
7844 // by relations between class cd and direct or indirect base class bcd
7845 static void computeMemberRelationsForBaseClass(const ClassDef *cd,const BaseClassDef *bcd)
7846 {
7847   for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
7848   {
7849     for (const auto &imd : *mn) // for each member with a given name
7850     {
7851       MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
7852       if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
7853       {
7854         ClassDef *mbcd = bcd->classDef;
7855         if (mbcd && mbcd->isLinkable()) // filter on linkable classes
7856         {
7857           const auto &bmn = mbcd->memberNameInfoLinkedMap();
7858           const auto &bmni = bmn.find(mn->memberName());
7859           if (bmni) // there are base class members with the same name
7860           {
7861             for (const auto &ibmd : *bmni) // for base class member with that name
7862             {
7863               MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
7864               if (bmd) // not part of an inline namespace
7865               {
7866                 if (bmd->virtualness()!=Specifier::Normal     ||
7867                     bmd->getLanguage()==SrcLangExt_Python     ||
7868                     bmd->getLanguage()==SrcLangExt_Java       ||
7869                     bmd->getLanguage()==SrcLangExt_PHP        ||
7870                     mbcd->compoundType()==ClassDef::Interface ||
7871                     mbcd->compoundType()==ClassDef::Protocol)
7872                 {
7873                   const ArgumentList &bmdAl = bmd->argumentList();
7874                   const ArgumentList &mdAl =  md->argumentList();
7875                   //printf(" Base argList='%s'\n Super argList='%s'\n",
7876                   //        qPrint(argListToString(bmdAl)),
7877                   //        qPrint(argListToString(mdAl))
7878                   //      );
7879                   if (
7880                       bmd->getLanguage()==SrcLangExt_Python ||
7881                       matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
7882                         md->getOuterScope(), md->getFileDef(), &mdAl,
7883                         TRUE,bmd->getLanguage()
7884                         )
7885                      )
7886                   {
7887                     //printf("match!\n");
7888                     const MemberDef *rmd = md->reimplements();
7889                     if (rmd==0) // not already assigned
7890                     {
7891                       //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
7892                       md->setReimplements(bmd);
7893                     }
7894                     //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
7895                     bmd->insertReimplementedBy(md);
7896                   }
7897                   else
7898                   {
7899                     //printf("no match!\n");
7900                   }
7901                 }
7902               }
7903             }
7904           }
7905         }
7906       }
7907     }
7908   }
7909
7910   // do also for indirect base classes
7911   for (const auto &bbcd : bcd->classDef->baseClasses())
7912   {
7913     computeMemberRelationsForBaseClass(cd,&bbcd);
7914   }
7915 }
7916
7917 //----------------------------------------------------------------------
7918 // computes the relation between all members. For each member 'm'
7919 // the members that override the implementation of 'm' are searched and
7920 // the member that 'm' overrides is searched.
7921
7922 static void computeMemberRelations()
7923 {
7924   for (const auto &cd : *Doxygen::classLinkedMap)
7925   {
7926     if (cd->isLinkable())
7927     {
7928       for (const auto &bcd : cd->baseClasses())
7929       {
7930         computeMemberRelationsForBaseClass(cd.get(),&bcd);
7931       }
7932     }
7933   }
7934 }
7935
7936 //----------------------------------------------------------------------------
7937
7938 static void createTemplateInstanceMembers()
7939 {
7940   // for each class
7941   for (const auto &cd : *Doxygen::classLinkedMap)
7942   {
7943     // that is a template
7944     for (const auto &ti : cd->getTemplateInstances())
7945     {
7946       ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
7947       if (tcdm)
7948       {
7949         tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
7950       }
7951     }
7952   }
7953 }
7954
7955 //----------------------------------------------------------------------------
7956
7957 static void mergeCategories()
7958 {
7959   // merge members of categories into the class they extend
7960   for (const auto &cd : *Doxygen::classLinkedMap)
7961   {
7962     int i=cd->name().find('(');
7963     if (i!=-1) // it is an Objective-C category
7964     {
7965       QCString baseName=cd->name().left(i);
7966       ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
7967       if (baseClass)
7968       {
7969         //printf("*** merging members of category %s into %s\n",
7970         //    qPrint(cd->name()),qPrint(baseClass->name()));
7971         baseClass->mergeCategory(cd.get());
7972       }
7973     }
7974   }
7975 }
7976
7977 // builds the list of all members for each class
7978
7979 static void buildCompleteMemberLists()
7980 {
7981   // merge the member list of base classes into the inherited classes.
7982   for (const auto &cd : *Doxygen::classLinkedMap)
7983   {
7984     if (// !cd->isReference() && // not an external class
7985          cd->subClasses().empty() && // is a root of the hierarchy
7986          !cd->baseClasses().empty()) // and has at least one base class
7987     {
7988       ClassDefMutable *cdm = toClassDefMutable(cd.get());
7989       if (cdm)
7990       {
7991         //printf("*** merging members for %s\n",qPrint(cd->name()));
7992         cdm->mergeMembers();
7993       }
7994     }
7995   }
7996   // now sort the member list of all members for all classes.
7997   for (const auto &cd : *Doxygen::classLinkedMap)
7998   {
7999     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8000     if (cdm)
8001     {
8002       cdm->sortAllMembersList();
8003     }
8004   }
8005 }
8006
8007 //----------------------------------------------------------------------------
8008
8009 static void generateFileSources()
8010 {
8011   auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8012   {
8013     bool showSources  = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8014     bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8015     if (showSources)
8016     {
8017       msg("Generating code for file %s...\n",qPrint(fd->docName()));
8018       fd->writeSourceHeader(ol);
8019       fd->writeSourceBody(ol,parser);
8020       fd->writeSourceFooter(ol);
8021     }
8022     else if (parseSources)
8023     {
8024       msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8025       fd->parseSource(parser);
8026     }
8027   };
8028   if (!Doxygen::inputNameLinkedMap->empty())
8029   {
8030 #if USE_LIBCLANG
8031     if (Doxygen::clangAssistedParsing)
8032     {
8033       StringUnorderedSet processedFiles;
8034
8035       // create a dictionary with files to process
8036       StringUnorderedSet filesToProcess;
8037
8038       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8039       {
8040         for (const auto &fd : *fn)
8041         {
8042           filesToProcess.insert(fd->absFilePath().str());
8043         }
8044       }
8045       // process source files (and their include dependencies)
8046       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8047       {
8048         for (const auto &fd : *fn)
8049         {
8050           if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt_Cpp &&
8051               (fd->generateSourceFile() ||
8052                (!fd->isReference() && Doxygen::parseSourcesNeeded)
8053               )
8054              )
8055           {
8056             auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8057             clangParser->parse();
8058             processSourceFile(fd.get(),*g_outputList,clangParser.get());
8059
8060             for (auto incFile : clangParser->filesInSameTU())
8061             {
8062               if (filesToProcess.find(incFile)!=filesToProcess.end() &&  // part of input
8063                   fd->absFilePath()!=QCString(incFile) &&                // not same file
8064                   processedFiles.find(incFile)==processedFiles.end())    // not yet marked as processed
8065               {
8066                 StringVector moreFiles;
8067                 bool ambig;
8068                 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
8069                 if (ifd && !ifd->isReference())
8070                 {
8071                   processSourceFile(ifd,*g_outputList,clangParser.get());
8072                   processedFiles.insert(incFile);
8073                 }
8074               }
8075             }
8076             processedFiles.insert(fd->absFilePath().str());
8077           }
8078         }
8079       }
8080       // process remaining files
8081       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8082       {
8083         for (const auto &fd : *fn)
8084         {
8085           if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8086           {
8087             if (fd->getLanguage()==SrcLangExt_Cpp) // C/C++ file, use clang parser
8088             {
8089               auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8090               clangParser->parse();
8091               processSourceFile(fd.get(),*g_outputList,clangParser.get());
8092             }
8093             else // non C/C++ file, use built-in parser
8094             {
8095               processSourceFile(fd.get(),*g_outputList,nullptr);
8096             }
8097           }
8098         }
8099       }
8100     }
8101     else
8102 #endif
8103     {
8104       std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8105       if (numThreads>1)
8106       {
8107         msg("Generating code files using %zu threads.\n",numThreads);
8108         struct SourceContext
8109         {
8110           SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8111             : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8112           FileDef *fd;
8113           bool generateSourceFile;
8114           OutputList ol;
8115         };
8116         ThreadPool threadPool(numThreads);
8117         std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8118         for (const auto &fn : *Doxygen::inputNameLinkedMap)
8119         {
8120           for (const auto &fd : *fn)
8121           {
8122             bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8123             auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8124             auto processFile = [ctx]()
8125             {
8126               if (ctx->generateSourceFile)
8127               {
8128                 msg("Generating code for file %s...\n",qPrint(ctx->fd->docName()));
8129               }
8130               else
8131               {
8132                 msg("Parsing code for file %s...\n",qPrint(ctx->fd->docName()));
8133               }
8134               StringVector filesInSameTu;
8135               ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8136               if (ctx->generateSourceFile) // sources need to be shown in the output
8137               {
8138                 ctx->fd->writeSourceHeader(ctx->ol);
8139                 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8140                 ctx->fd->writeSourceFooter(ctx->ol);
8141               }
8142               else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8143                 // we needed to parse the sources even if we do not show them
8144               {
8145                 ctx->fd->parseSource(nullptr);
8146               }
8147               return ctx;
8148             };
8149             results.emplace_back(threadPool.queue(processFile));
8150           }
8151         }
8152         for (auto &f : results)
8153         {
8154           auto ctx = f.get();
8155         }
8156       }
8157       else // single threaded version
8158       {
8159         for (const auto &fn : *Doxygen::inputNameLinkedMap)
8160         {
8161           for (const auto &fd : *fn)
8162           {
8163             StringVector filesInSameTu;
8164             fd->getAllIncludeFilesRecursively(filesInSameTu);
8165             processSourceFile(fd.get(),*g_outputList,nullptr);
8166           }
8167         }
8168       }
8169     }
8170   }
8171 }
8172
8173 //----------------------------------------------------------------------------
8174
8175 static void generateFileDocs()
8176 {
8177   if (Index::instance().numDocumentedFiles()==0) return;
8178
8179   if (!Doxygen::inputNameLinkedMap->empty())
8180   {
8181     std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8182     if (numThreads>1) // multi threaded processing
8183     {
8184       struct DocContext
8185       {
8186         DocContext(FileDef *fd_,const OutputList &ol_)
8187           : fd(fd_), ol(ol_) {}
8188         FileDef *fd;
8189         OutputList ol;
8190       };
8191       ThreadPool threadPool(numThreads);
8192       std::vector< std::future< std::shared_ptr<DocContext> > > results;
8193       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8194       {
8195         for (const auto &fd : *fn)
8196         {
8197           bool doc = fd->isLinkableInProject();
8198           if (doc)
8199           {
8200             auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8201             auto processFile = [ctx]() {
8202               msg("Generating docs for file %s...\n",qPrint(ctx->fd->docName()));
8203               ctx->fd->writeDocumentation(ctx->ol);
8204               return ctx;
8205             };
8206             results.emplace_back(threadPool.queue(processFile));
8207           }
8208         }
8209       }
8210       for (auto &f : results)
8211       {
8212         auto ctx = f.get();
8213       }
8214     }
8215     else // single threaded processing
8216     {
8217       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8218       {
8219         for (const auto &fd : *fn)
8220         {
8221           bool doc = fd->isLinkableInProject();
8222           if (doc)
8223           {
8224             msg("Generating docs for file %s...\n",qPrint(fd->docName()));
8225             fd->writeDocumentation(*g_outputList);
8226           }
8227         }
8228       }
8229     }
8230   }
8231 }
8232
8233 //----------------------------------------------------------------------------
8234
8235 static void addSourceReferences()
8236 {
8237   // add source references for class definitions
8238   for (const auto &cd : *Doxygen::classLinkedMap)
8239   {
8240     const FileDef *fd=cd->getBodyDef();
8241     if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8242     {
8243       const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8244     }
8245   }
8246   // add source references for concept definitions
8247   for (const auto &cd : *Doxygen::conceptLinkedMap)
8248   {
8249     const FileDef *fd=cd->getBodyDef();
8250     if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8251     {
8252       const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8253     }
8254   }
8255   // add source references for namespace definitions
8256   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8257   {
8258     const FileDef *fd=nd->getBodyDef();
8259     if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8260     {
8261       const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),0);
8262     }
8263   }
8264
8265   // add source references for member names
8266   for (const auto &mn : *Doxygen::memberNameLinkedMap)
8267   {
8268     for (const auto &md : *mn)
8269     {
8270       //printf("class member %s: def=%s body=%d link?=%d\n",
8271       //    qPrint(md->name()),
8272       //    md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8273       //    md->getStartBodyLine(),md->isLinkableInProject());
8274       const FileDef *fd=md->getBodyDef();
8275       if (fd &&
8276           md->getStartDefLine()!=-1 &&
8277           md->isLinkableInProject() &&
8278           (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8279          )
8280       {
8281         //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8282         //    qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8283         const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8284       }
8285     }
8286   }
8287   for (const auto &mn : *Doxygen::functionNameLinkedMap)
8288   {
8289     for (const auto &md : *mn)
8290     {
8291       const FileDef *fd=md->getBodyDef();
8292       //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8293       //    qPrint(md->name()),
8294       //    md->getStartBodyLine(),md->getEndBodyLine(),fd,
8295       //    md->isLinkableInProject(),
8296       //    Doxygen::parseSourcesNeeded);
8297       if (fd &&
8298           md->getStartDefLine()!=-1 &&
8299           md->isLinkableInProject() &&
8300           (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8301          )
8302       {
8303         //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8304         //    qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8305         const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8306       }
8307     }
8308   }
8309 }
8310
8311 //----------------------------------------------------------------------------
8312
8313 // add the macro definitions found during preprocessing as file members
8314 static void buildDefineList()
8315 {
8316   AUTO_TRACE();
8317   for (const auto &s : g_inputFiles)
8318   {
8319     auto it = Doxygen::macroDefinitions.find(s);
8320     if (it!=Doxygen::macroDefinitions.end())
8321     {
8322       for (const auto &def : it->second)
8323       {
8324         auto md = createMemberDef(
8325             def.fileName,def.lineNr,def.columnNr,
8326             "#define",def.name,def.args,QCString(),
8327             Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType_Define,
8328             ArgumentList(),ArgumentList(),"");
8329         auto mmd = toMemberDefMutable(md.get());
8330
8331         if (!def.args.isEmpty())
8332         {
8333           mmd->moveArgumentList(stringToArgumentList(SrcLangExt_Cpp, def.args));
8334         }
8335         mmd->setInitializer(def.definition);
8336         mmd->setFileDef(def.fileDef);
8337         mmd->setDefinition("#define "+def.name);
8338
8339         MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8340         if (def.fileDef)
8341         {
8342           def.fileDef->insertMember(md.get());
8343         }
8344         AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8345         mn->push_back(std::move(md));
8346       }
8347     }
8348   }
8349 }
8350
8351 //----------------------------------------------------------------------------
8352
8353 static void sortMemberLists()
8354 {
8355   // sort class member lists
8356   for (const auto &cd : *Doxygen::classLinkedMap)
8357   {
8358     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8359     if (cdm)
8360     {
8361       cdm->sortMemberLists();
8362     }
8363   }
8364
8365   // sort namespace member lists
8366   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8367   {
8368     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8369     if (ndm)
8370     {
8371       ndm->sortMemberLists();
8372     }
8373   }
8374
8375   // sort file member lists
8376   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8377   {
8378     for (const auto &fd : *fn)
8379     {
8380       fd->sortMemberLists();
8381     }
8382   }
8383
8384   // sort group member lists
8385   for (const auto &gd : *Doxygen::groupLinkedMap)
8386   {
8387     gd->sortMemberLists();
8388   }
8389
8390   ModuleManager::instance().sortMemberLists();
8391 }
8392
8393 //----------------------------------------------------------------------------
8394
8395 static bool isSymbolHidden(const Definition *d)
8396 {
8397   bool hidden = d->isHidden();
8398   const Definition *parent = d->getOuterScope();
8399   return parent ? hidden || isSymbolHidden(parent) : hidden;
8400 }
8401
8402 static void computeTooltipTexts()
8403 {
8404   std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8405   if (numThreads>1)
8406   {
8407     ThreadPool threadPool(numThreads);
8408     std::vector < std::future< void > > results;
8409     // queue the work
8410     for (const auto &[name,symList] : *Doxygen::symbolMap)
8411     {
8412       for (const auto &def : symList)
8413       {
8414         DefinitionMutable *dm = toDefinitionMutable(def);
8415         if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
8416         {
8417           auto processTooltip = [dm]() {
8418             dm->computeTooltip();
8419           };
8420           results.emplace_back(threadPool.queue(processTooltip));
8421         }
8422       }
8423     }
8424     // wait for the results
8425     for (auto &f : results)
8426     {
8427       f.get();
8428     }
8429   }
8430   else
8431   {
8432     for (const auto &[name,symList] : *Doxygen::symbolMap)
8433     {
8434       for (const auto &def : symList)
8435       {
8436         DefinitionMutable *dm = toDefinitionMutable(def);
8437         if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
8438         {
8439           dm->computeTooltip();
8440         }
8441       }
8442     }
8443   }
8444 }
8445
8446 //----------------------------------------------------------------------------
8447
8448 static void setAnonymousEnumType()
8449 {
8450   for (const auto &cd : *Doxygen::classLinkedMap)
8451   {
8452     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8453     if (cdm)
8454     {
8455       cdm->setAnonymousEnumType();
8456     }
8457   }
8458 }
8459
8460 //----------------------------------------------------------------------------
8461
8462 static void countMembers()
8463 {
8464   for (const auto &cd : *Doxygen::classLinkedMap)
8465   {
8466     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8467     if (cdm)
8468     {
8469       cdm->countMembers();
8470     }
8471   }
8472
8473   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8474   {
8475     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8476     if (ndm)
8477     {
8478       ndm->countMembers();
8479     }
8480   }
8481
8482   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8483   {
8484     for (const auto &fd : *fn)
8485     {
8486       fd->countMembers();
8487     }
8488   }
8489
8490   for (const auto &gd : *Doxygen::groupLinkedMap)
8491   {
8492     gd->countMembers();
8493   }
8494
8495   auto &mm = ModuleManager::instance();
8496   mm.countMembers();
8497 }
8498
8499
8500 //----------------------------------------------------------------------------
8501 // generate the documentation for all classes
8502
8503 static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
8504 {
8505   std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8506   if (numThreads>1) // multi threaded processing
8507   {
8508     struct DocContext
8509     {
8510       DocContext(ClassDefMutable *cd_,const OutputList &ol_)
8511         : cd(cd_), ol(ol_) {}
8512       ClassDefMutable *cd;
8513       OutputList ol;
8514     };
8515     ThreadPool threadPool(numThreads);
8516     std::vector< std::future< std::shared_ptr<DocContext> > > results;
8517     for (const auto &cd : classList)
8518     {
8519       //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8520       if ((cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8521            cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8522           ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8523          )
8524       {
8525         auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
8526         auto processFile = [ctx]()
8527         {
8528           msg("Generating docs for compound %s...\n",qPrint(ctx->cd->name()));
8529
8530           // skip external references, anonymous compounds and
8531           // template instances
8532           if ( ctx->cd->isLinkableInProject() && ctx->cd->templateMaster()==0)
8533           {
8534             ctx->cd->writeDocumentation(ctx->ol);
8535             ctx->cd->writeMemberList(ctx->ol);
8536           }
8537
8538           // even for undocumented classes, the inner classes can be documented.
8539           ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
8540           return ctx;
8541         };
8542         results.emplace_back(threadPool.queue(processFile));
8543       }
8544     }
8545     for (auto &f : results)
8546     {
8547       auto ctx = f.get();
8548     }
8549   }
8550   else // single threaded processing
8551   {
8552     for (const auto &cd : classList)
8553     {
8554       //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8555       if ((cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8556            cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8557           ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8558          )
8559       {
8560         // skip external references, anonymous compounds and
8561         // template instances
8562         if ( cd->isLinkableInProject() && cd->templateMaster()==0)
8563         {
8564           msg("Generating docs for compound %s...\n",qPrint(cd->name()));
8565
8566           cd->writeDocumentation(*g_outputList);
8567           cd->writeMemberList(*g_outputList);
8568         }
8569         // even for undocumented classes, the inner classes can be documented.
8570         cd->writeDocumentationForInnerClasses(*g_outputList);
8571       }
8572     }
8573   }
8574 }
8575
8576 static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
8577 {
8578   list.push_back(cd);
8579   for (const auto &innerCdi : cd->getClasses())
8580   {
8581     ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
8582     if (innerCd && innerCd->isLinkableInProject() && innerCd->templateMaster()==0 &&
8583         protectionLevelVisible(innerCd->protection()) &&
8584         !innerCd->isEmbeddedInOuterScope()
8585        )
8586     {
8587       list.push_back(innerCd);
8588       addClassAndNestedClasses(list,innerCd);
8589     }
8590   }
8591 }
8592
8593 static void generateClassDocs()
8594 {
8595   std::vector<ClassDefMutable*> classList;
8596   for (const auto &cdi : *Doxygen::classLinkedMap)
8597   {
8598     ClassDefMutable *cd = toClassDefMutable(cdi.get());
8599     if (cd && (cd->getOuterScope()==0 ||
8600                cd->getOuterScope()->definitionType()!=Definition::TypeClass))
8601     {
8602       addClassAndNestedClasses(classList,cd);
8603     }
8604   }
8605   for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
8606   {
8607     ClassDefMutable *cd = toClassDefMutable(cdi.get());
8608     if (cd && (cd->getOuterScope()==0 ||
8609                cd->getOuterScope()->definitionType()!=Definition::TypeClass))
8610     {
8611       addClassAndNestedClasses(classList,cd);
8612     }
8613   }
8614   generateDocsForClassList(classList);
8615 }
8616
8617 //----------------------------------------------------------------------------
8618
8619 static void generateConceptDocs()
8620 {
8621   for (const auto &cdi : *Doxygen::conceptLinkedMap)
8622   {
8623     ConceptDefMutable *cd=toConceptDefMutable(cdi.get());
8624
8625     //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8626     if (cd &&
8627         (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8628          cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
8629         ) && !cd->isHidden() && cd->isLinkableInProject()
8630        )
8631     {
8632       msg("Generating docs for concept %s...\n",qPrint(cd->name()));
8633       cd->writeDocumentation(*g_outputList);
8634     }
8635   }
8636 }
8637
8638 //----------------------------------------------------------------------------
8639
8640 static void inheritDocumentation()
8641 {
8642   for (const auto &mn : *Doxygen::memberNameLinkedMap)
8643   {
8644     for (const auto &imd : *mn)
8645     {
8646       MemberDefMutable *md = toMemberDefMutable(imd.get());
8647       //static int count=0;
8648       //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
8649       if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
8650       { // no documentation yet
8651         const MemberDef *bmd = md->reimplements();
8652         while (bmd && bmd->documentation().isEmpty() &&
8653                       bmd->briefDescription().isEmpty()
8654               )
8655         { // search up the inheritance tree for a documentation member
8656           //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
8657           bmd = bmd->reimplements();
8658         }
8659         if (bmd) // copy the documentation from the reimplemented member
8660         {
8661           md->setInheritsDocsFrom(bmd);
8662           md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
8663           md->setDocsForDefinition(bmd->isDocsForDefinition());
8664           md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
8665           md->copyArgumentNames(bmd);
8666           md->setInbodyDocumentation(bmd->inbodyDocumentation(),bmd->inbodyFile(),bmd->inbodyLine());
8667         }
8668       }
8669     }
8670   }
8671 }
8672
8673 //----------------------------------------------------------------------------
8674
8675 static void combineUsingRelations()
8676 {
8677   // for each file
8678   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8679   {
8680     for (const auto &fd : *fn)
8681     {
8682       fd->combineUsingRelations();
8683     }
8684   }
8685
8686   // for each namespace
8687   NamespaceDefSet visitedNamespaces;
8688   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8689   {
8690     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8691     if (ndm)
8692     {
8693       ndm->combineUsingRelations(visitedNamespaces);
8694     }
8695   }
8696 }
8697
8698 //----------------------------------------------------------------------------
8699
8700 static void addMembersToMemberGroup()
8701 {
8702   // for each class
8703   for (const auto &cd : *Doxygen::classLinkedMap)
8704   {
8705     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8706     if (cdm)
8707     {
8708       cdm->addMembersToMemberGroup();
8709     }
8710   }
8711   // for each file
8712   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8713   {
8714     for (const auto &fd : *fn)
8715     {
8716       fd->addMembersToMemberGroup();
8717     }
8718   }
8719   // for each namespace
8720   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8721   {
8722     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8723     if (ndm)
8724     {
8725       ndm->addMembersToMemberGroup();
8726     }
8727   }
8728   // for each group
8729   for (const auto &gd : *Doxygen::groupLinkedMap)
8730   {
8731     gd->addMembersToMemberGroup();
8732   }
8733   ModuleManager::instance().addMembersToMemberGroup();
8734 }
8735
8736 //----------------------------------------------------------------------------
8737
8738 static void distributeMemberGroupDocumentation()
8739 {
8740   // for each class
8741   for (const auto &cd : *Doxygen::classLinkedMap)
8742   {
8743     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8744     if (cdm)
8745     {
8746       cdm->distributeMemberGroupDocumentation();
8747     }
8748   }
8749   // for each file
8750   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8751   {
8752     for (const auto &fd : *fn)
8753     {
8754       fd->distributeMemberGroupDocumentation();
8755     }
8756   }
8757   // for each namespace
8758   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8759   {
8760     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8761     if (ndm)
8762     {
8763       ndm->distributeMemberGroupDocumentation();
8764     }
8765   }
8766   // for each group
8767   for (const auto &gd : *Doxygen::groupLinkedMap)
8768   {
8769     gd->distributeMemberGroupDocumentation();
8770   }
8771   ModuleManager::instance().distributeMemberGroupDocumentation();
8772 }
8773
8774 //----------------------------------------------------------------------------
8775
8776 static void findSectionsInDocumentation()
8777 {
8778   // for each class
8779   for (const auto &cd : *Doxygen::classLinkedMap)
8780   {
8781     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8782     if (cdm)
8783     {
8784       cdm->findSectionsInDocumentation();
8785     }
8786   }
8787   // for each concept
8788   for (const auto &cd : *Doxygen::conceptLinkedMap)
8789   {
8790     ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
8791     if (cdm)
8792     {
8793       cdm->findSectionsInDocumentation();
8794     }
8795   }
8796   // for each file
8797   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8798   {
8799     for (const auto &fd : *fn)
8800     {
8801       fd->findSectionsInDocumentation();
8802     }
8803   }
8804   // for each namespace
8805   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8806   {
8807     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8808     if (ndm)
8809     {
8810       ndm->findSectionsInDocumentation();
8811     }
8812   }
8813   // for each group
8814   for (const auto &gd : *Doxygen::groupLinkedMap)
8815   {
8816     gd->findSectionsInDocumentation();
8817   }
8818   // for each page
8819   for (const auto &pd : *Doxygen::pageLinkedMap)
8820   {
8821     pd->findSectionsInDocumentation();
8822   }
8823   ModuleManager::instance().findSectionsInDocumentation();
8824   if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
8825 }
8826
8827 //----------------------------------------------------------------------
8828
8829
8830 static void flushCachedTemplateRelations()
8831 {
8832   // remove all references to classes from the cache
8833   // as there can be new template instances in the inheritance path
8834   // to this class. Optimization: only remove those classes that
8835   // have inheritance instances as direct or indirect sub classes.
8836   StringVector elementsToRemove;
8837   for (const auto &ci : *Doxygen::typeLookupCache)
8838   {
8839     const LookupInfo &li = ci.second;
8840     if (li.definition)
8841     {
8842       elementsToRemove.push_back(ci.first);
8843     }
8844   }
8845   for (const auto &k : elementsToRemove)
8846   {
8847     Doxygen::typeLookupCache->remove(k);
8848   }
8849
8850   // remove all cached typedef resolutions whose target is a
8851   // template class as this may now be a template instance
8852   // for each global function name
8853   for (const auto &fn : *Doxygen::functionNameLinkedMap)
8854   {
8855     // for each function with that name
8856     for (const auto &ifmd : *fn)
8857     {
8858       MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8859       if (fmd && fmd->isTypedefValCached())
8860       {
8861         const ClassDef *cd = fmd->getCachedTypedefVal();
8862         if (cd->isTemplate()) fmd->invalidateTypedefValCache();
8863       }
8864     }
8865   }
8866   // for each class method name
8867   for (const auto &nm : *Doxygen::memberNameLinkedMap)
8868   {
8869     // for each function with that name
8870     for (const auto &imd : *nm)
8871     {
8872       MemberDefMutable *md = toMemberDefMutable(imd.get());
8873       if (md && md->isTypedefValCached())
8874       {
8875         const ClassDef *cd = md->getCachedTypedefVal();
8876         if (cd->isTemplate()) md->invalidateTypedefValCache();
8877       }
8878     }
8879   }
8880 }
8881
8882 //----------------------------------------------------------------------------
8883
8884 static void flushUnresolvedRelations()
8885 {
8886   // Remove all unresolved references to classes from the cache.
8887   // This is needed before resolving the inheritance relations, since
8888   // it would otherwise not find the inheritance relation
8889   // for C in the example below, as B::I was already found to be unresolvable
8890   // (which is correct if you ignore the inheritance relation between A and B).
8891   //
8892   // class A { class I {} };
8893   // class B : public A {};
8894   // class C : public B::I {};
8895
8896   StringVector elementsToRemove;
8897   for (const auto &ci : *Doxygen::typeLookupCache)
8898   {
8899     const LookupInfo &li = ci.second;
8900     if (li.definition==0 && li.typeDef==0)
8901     {
8902       elementsToRemove.push_back(ci.first);
8903     }
8904   }
8905   for (const auto &k : elementsToRemove)
8906   {
8907     Doxygen::typeLookupCache->remove(k);
8908   }
8909
8910   // for each global function name
8911   for (const auto &fn : *Doxygen::functionNameLinkedMap)
8912   {
8913     // for each function with that name
8914     for (const auto &ifmd : *fn)
8915     {
8916       MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8917       if (fmd)
8918       {
8919         fmd->invalidateCachedArgumentTypes();
8920       }
8921     }
8922   }
8923   // for each class method name
8924   for (const auto &nm : *Doxygen::memberNameLinkedMap)
8925   {
8926     // for each function with that name
8927     for (const auto &imd : *nm)
8928     {
8929       MemberDefMutable *md = toMemberDefMutable(imd.get());
8930       if (md)
8931       {
8932         md->invalidateCachedArgumentTypes();
8933       }
8934     }
8935   }
8936
8937 }
8938
8939 //----------------------------------------------------------------------------
8940 // Returns TRUE if the entry and member definition have equal file names,
8941 // otherwise FALSE.
8942
8943 static bool haveEqualFileNames(const Entry *root,const MemberDef *md)
8944 {
8945   const FileDef *fd = md->getFileDef();
8946   if (!fd)
8947   {
8948     return FALSE;
8949   }
8950
8951   return fd->absFilePath() == root->fileName;
8952 }
8953
8954 //----------------------------------------------------------------------------
8955
8956 static void addDefineDoc(const Entry *root, MemberDefMutable *md)
8957 {
8958   md->setDocumentation(root->doc,root->docFile,root->docLine);
8959   md->setDocsForDefinition(!root->proto);
8960   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8961   if (md->inbodyDocumentation().isEmpty())
8962   {
8963     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
8964   }
8965   if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
8966   {
8967     md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
8968     md->setBodyDef(root->fileDef());
8969   }
8970   md->addSectionsToDefinition(root->anchors);
8971   md->setMaxInitLines(root->initLines);
8972   md->setRefItems(root->sli);
8973   if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
8974   addMemberToGroups(root,md);
8975   ModuleManager::instance().addMemberToModule(root,md);
8976 }
8977
8978 //----------------------------------------------------------------------------
8979
8980 static void findDefineDocumentation(Entry *root)
8981 {
8982   if ((root->section==Entry::DEFINEDOC_SEC ||
8983        root->section==Entry::DEFINE_SEC) && !root->name.isEmpty()
8984      )
8985   {
8986     //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
8987     //       qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
8988
8989     if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
8990     {
8991       auto md = createMemberDef(root->tagInfo()->tagName,1,1,
8992                     "#define",root->name,root->args,QCString(),
8993                     Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType_Define,
8994                     ArgumentList(),ArgumentList(),"");
8995       auto mmd = toMemberDefMutable(md.get());
8996       mmd->setTagInfo(root->tagInfo());
8997       mmd->setLanguage(root->lang);
8998       //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
8999       mmd->setFileDef(root->parent()->fileDef());
9000       //printf("Adding member=%s\n",qPrint(md->name()));
9001       MemberName *mn = Doxygen::functionNameLinkedMap->add(root->name);
9002       mn->push_back(std::move(md));
9003     }
9004     MemberName *mn=Doxygen::functionNameLinkedMap->find(root->name);
9005     if (mn)
9006     {
9007       int count=0;
9008       for (const auto &md : *mn)
9009       {
9010         if (md->memberType()==MemberType_Define) count++;
9011       }
9012       if (count==1)
9013       {
9014         for (const auto &imd : *mn)
9015         {
9016           MemberDefMutable *md = toMemberDefMutable(imd.get());
9017           if (md && md->memberType()==MemberType_Define)
9018           {
9019             addDefineDoc(root,md);
9020           }
9021         }
9022       }
9023       else if (count>1 &&
9024                (!root->doc.isEmpty() ||
9025                 !root->brief.isEmpty() ||
9026                 root->bodyLine!=-1
9027                )
9028               )
9029         // multiple defines don't know where to add docs
9030         // but maybe they are in different files together with their documentation
9031       {
9032         for (const auto &imd : *mn)
9033         {
9034           MemberDefMutable *md = toMemberDefMutable(imd.get());
9035           if (md && md->memberType()==MemberType_Define)
9036           {
9037             if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9038               // doc and define in the same file or group assume they belong together.
9039             {
9040               addDefineDoc(root,md);
9041             }
9042           }
9043         }
9044         //warn("define %s found in the following files:\n",qPrint(root->name));
9045         //warn("Cannot determine where to add the documentation found "
9046         //     "at line %d of file %s. \n",
9047         //     root->startLine,qPrint(root->fileName));
9048       }
9049     }
9050     else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9051     {
9052       bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9053       if (preEnabled)
9054       {
9055         warn(root->fileName,root->startLine,
9056              "documentation for unknown define %s found.",
9057              qPrint(root->name)
9058             );
9059       }
9060       else
9061       {
9062         warn(root->fileName,root->startLine,
9063              "found documented #define %s but ignoring it because "
9064              "ENABLE_PREPROCESSING is NO.",
9065              qPrint(root->name)
9066             );
9067       }
9068     }
9069   }
9070   for (const auto &e : root->children()) findDefineDocumentation(e.get());
9071 }
9072
9073 //----------------------------------------------------------------------------
9074
9075 static void findDirDocumentation(const Entry *root)
9076 {
9077   if (root->section == Entry::DIRDOC_SEC)
9078   {
9079     QCString normalizedName = root->name;
9080     normalizedName = substitute(normalizedName,"\\","/");
9081     //printf("root->docFile=%s normalizedName=%s\n",
9082     //    qPrint(root->docFile),qPrint(normalizedName));
9083     if (root->docFile==normalizedName) // current dir?
9084     {
9085       int lastSlashPos=normalizedName.findRev('/');
9086       if (lastSlashPos!=-1) // strip file name
9087       {
9088         normalizedName=normalizedName.left(lastSlashPos);
9089       }
9090     }
9091     if (normalizedName.at(normalizedName.length()-1)!='/')
9092     {
9093       normalizedName+='/';
9094     }
9095     DirDef *matchingDir=0;
9096     for (const auto &dir : *Doxygen::dirLinkedMap)
9097     {
9098       //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9099       if (dir->name().right(normalizedName.length())==normalizedName)
9100       {
9101         if (matchingDir)
9102         {
9103            warn(root->fileName,root->startLine,
9104              "\\dir command matches multiple directories.\n"
9105              "  Applying the command for directory %s\n"
9106              "  Ignoring the command for directory %s",
9107              qPrint(matchingDir->name()),qPrint(dir->name())
9108            );
9109         }
9110         else
9111         {
9112           matchingDir=dir.get();
9113         }
9114       }
9115     }
9116     if (matchingDir)
9117     {
9118       //printf("Match for with dir %s\n",qPrint(matchingDir->name()));
9119       matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9120       matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9121       matchingDir->setRefItems(root->sli);
9122       matchingDir->enableDirectoryGraph(root->directoryGraph);
9123       addDirToGroups(root,matchingDir);
9124     }
9125     else
9126     {
9127       warn(root->fileName,root->startLine,"No matching "
9128           "directory found for command \\dir %s",qPrint(normalizedName));
9129     }
9130   }
9131   for (const auto &e : root->children()) findDirDocumentation(e.get());
9132 }
9133
9134
9135 //----------------------------------------------------------------------------
9136 // create a (sorted) list of separate documentation pages
9137
9138 static void buildPageList(Entry *root)
9139 {
9140   if (root->section == Entry::PAGEDOC_SEC)
9141   {
9142     if (!root->name.isEmpty())
9143     {
9144       addRelatedPage(root);
9145     }
9146   }
9147   else if (root->section == Entry::MAINPAGEDOC_SEC)
9148   {
9149     QCString title=root->args.stripWhiteSpace();
9150     if (title.isEmpty()) title=theTranslator->trMainPage();
9151     //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9152     QCString name = "index";
9153     addRefItem(root->sli,
9154                name,
9155                "page",
9156                name,
9157                title,
9158                QCString(),0
9159                );
9160   }
9161   for (const auto &e : root->children()) buildPageList(e.get());
9162 }
9163
9164 // search for the main page defined in this project
9165 static void findMainPage(Entry *root)
9166 {
9167   if (root->section == Entry::MAINPAGEDOC_SEC)
9168   {
9169     if (Doxygen::mainPage==0 && root->tagInfo()==0)
9170     {
9171       //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9172       //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9173       QCString title=root->args.stripWhiteSpace();
9174       if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9175       //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9176       QCString indexName="index";
9177       Doxygen::mainPage = createPageDef(root->docFile,root->docLine,
9178                               indexName, root->brief+root->doc+root->inbodyDocs,title);
9179       //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9180       Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9181       Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9182       Doxygen::mainPage->setFileName(indexName);
9183       Doxygen::mainPage->setLocalToc(root->localToc);
9184       addPageToContext(Doxygen::mainPage.get(),root);
9185
9186       const SectionInfo *si = SectionManager::instance().find(Doxygen::mainPage->name());
9187       if (si)
9188       {
9189         if (!si->ref().isEmpty()) // we are from a tag file
9190         {
9191           // a page name is a label as well! but should no be double either
9192           SectionManager::instance().replace(
9193             Doxygen::mainPage->name(),
9194             indexName,
9195             root->startLine,
9196             Doxygen::mainPage->title(),
9197             SectionType::Page,
9198             0); // level 0
9199         }
9200         else if (si->lineNr() != -1)
9201         {
9202           warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s, line %d)",
9203                qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()),si->lineNr());
9204         }
9205         else
9206         {
9207           warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s)",
9208                qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()));
9209         }
9210       }
9211       else
9212       {
9213         // a page name is a label as well! but should no be double either
9214         SectionManager::instance().add(
9215           Doxygen::mainPage->name(),
9216           indexName,
9217           root->startLine,
9218           Doxygen::mainPage->title(),
9219           SectionType::Page,
9220           0); // level 0
9221       }
9222       Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9223     }
9224     else if (root->tagInfo()==0)
9225     {
9226       warn(root->fileName,root->startLine,
9227            "found more than one \\mainpage comment block! (first occurrence: %s, line %d), Skipping current block!",
9228            qPrint(Doxygen::mainPage->docFile()),Doxygen::mainPage->getStartBodyLine());
9229     }
9230   }
9231   for (const auto &e : root->children()) findMainPage(e.get());
9232 }
9233
9234 // search for the main page imported via tag files and add only the section labels
9235 static void findMainPageTagFiles(Entry *root)
9236 {
9237   if (root->section == Entry::MAINPAGEDOC_SEC)
9238   {
9239     if (Doxygen::mainPage && root->tagInfo())
9240     {
9241       Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9242     }
9243   }
9244   for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9245 }
9246
9247 static void computePageRelations(Entry *root)
9248 {
9249   if ((root->section==Entry::PAGEDOC_SEC ||
9250        root->section==Entry::MAINPAGEDOC_SEC
9251       )
9252       && !root->name.isEmpty()
9253      )
9254   {
9255     PageDef *pd = root->section==Entry::PAGEDOC_SEC ?
9256                     Doxygen::pageLinkedMap->find(root->name) :
9257                     Doxygen::mainPage.get();
9258     if (pd)
9259     {
9260       for (const BaseInfo &bi : root->extends)
9261       {
9262         PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9263         if (pd==subPd)
9264         {
9265          term("page defined %s with label %s is a direct "
9266              "subpage of itself! Please remove this cyclic dependency.\n",
9267               qPrint(warn_line(pd->docFile(),pd->docLine())),qPrint(pd->name()));
9268         }
9269         else if (subPd)
9270         {
9271           pd->addInnerCompound(subPd);
9272           //printf("*** Added subpage relation: %s->%s\n",
9273           //    qPrint(pd->name()),qPrint(subPd->name()));
9274         }
9275       }
9276     }
9277   }
9278   for (const auto &e : root->children()) computePageRelations(e.get());
9279 }
9280
9281 static void checkPageRelations()
9282 {
9283   for (const auto &pd : *Doxygen::pageLinkedMap)
9284   {
9285     Definition *ppd = pd->getOuterScope();
9286     while (ppd)
9287     {
9288       if (ppd==pd.get())
9289       {
9290         term("page defined %s with label %s is a subpage "
9291              "of itself! Please remove this cyclic dependency.\n",
9292               qPrint(warn_line(pd->docFile(),pd->docLine())),qPrint(pd->name()));
9293       }
9294       ppd=ppd->getOuterScope();
9295     }
9296   }
9297 }
9298
9299 //----------------------------------------------------------------------------
9300
9301 static void resolveUserReferences()
9302 {
9303   for (const auto &si : SectionManager::instance())
9304   {
9305     //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9306     //        qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9307     //        qPrint(si->fileName));
9308     PageDef *pd=0;
9309
9310     // hack: the items of a todo/test/bug/deprecated list are all fragments from
9311     // different files, so the resulting section's all have the wrong file
9312     // name (not from the todo/test/bug/deprecated list, but from the file in
9313     // which they are defined). We correct this here by looking at the
9314     // generated section labels!
9315     for (const RefListManager::Ptr &rl : RefListManager::instance())
9316     {
9317       QCString label="_"+rl->listName(); // "_todo", "_test", ...
9318       if (si->label().left(label.length())==label)
9319       {
9320         si->setFileName(rl->listName());
9321         si->setGenerated(TRUE);
9322         break;
9323       }
9324     }
9325
9326     //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9327     if (!si->generated())
9328     {
9329       // if this section is in a page and the page is in a group, then we
9330       // have to adjust the link file name to point to the group.
9331       if (!si->fileName().isEmpty() &&
9332           (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9333           pd->getGroupDef())
9334       {
9335         si->setFileName(pd->getGroupDef()->getOutputFileBase());
9336       }
9337
9338       if (si->definition())
9339       {
9340         // TODO: there should be one function in Definition that returns
9341         // the file to link to, so we can avoid the following tests.
9342         const GroupDef *gd=0;
9343         if (si->definition()->definitionType()==Definition::TypeMember)
9344         {
9345           gd = (toMemberDef(si->definition()))->getGroupDef();
9346         }
9347
9348         if (gd)
9349         {
9350           si->setFileName(gd->getOutputFileBase());
9351         }
9352         else
9353         {
9354           //si->fileName=si->definition->getOutputFileBase();
9355           //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9356         }
9357       }
9358     }
9359     //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9360   }
9361 }
9362
9363
9364
9365 //----------------------------------------------------------------------------
9366 // generate all separate documentation pages
9367
9368
9369 static void generatePageDocs()
9370 {
9371   //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9372   if (Index::instance().numDocumentedPages()==0) return;
9373   for (const auto &pd : *Doxygen::pageLinkedMap)
9374   {
9375     if (!pd->getGroupDef() && !pd->isReference())
9376     {
9377       msg("Generating docs for page %s...\n",qPrint(pd->name()));
9378       pd->writeDocumentation(*g_outputList);
9379     }
9380   }
9381 }
9382
9383 //----------------------------------------------------------------------------
9384 // create a (sorted) list & dictionary of example pages
9385
9386 static void buildExampleList(Entry *root)
9387 {
9388   if ((root->section==Entry::EXAMPLE_SEC || root->section==Entry::EXAMPLE_LINENO_SEC) && !root->name.isEmpty())
9389   {
9390     if (Doxygen::exampleLinkedMap->find(root->name))
9391     {
9392       warn(root->fileName,root->startLine,
9393           "Example %s was already documented. Ignoring "
9394           "documentation found here.",
9395           qPrint(root->name)
9396           );
9397     }
9398     else
9399     {
9400       PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9401                createPageDef(root->fileName,root->startLine,
9402                  root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9403       pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9404       pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9405       pd->addSectionsToDefinition(root->anchors);
9406       pd->setLanguage(root->lang);
9407       pd->setShowLineNo(root->section==Entry::EXAMPLE_LINENO_SEC);
9408
9409       //we don't add example to groups
9410       //addExampleToGroups(root,pd);
9411     }
9412   }
9413   for (const auto &e : root->children()) buildExampleList(e.get());
9414 }
9415
9416 //----------------------------------------------------------------------------
9417 // prints the Entry tree (for debugging)
9418
9419 void printNavTree(Entry *root,int indent)
9420 {
9421   if (Debug::isFlagSet(Debug::Entries))
9422   {
9423     QCString indentStr;
9424     indentStr.fill(' ',indent);
9425     Debug::print(Debug::Entries,0,"%s%s at %s:%d (sec=0x%x, spec=%" PRIx64 ")\n",
9426         indentStr.isEmpty()?"":qPrint(indentStr),
9427         root->name.isEmpty()?"<empty>":qPrint(root->name),
9428         qPrint(root->fileName),root->startLine,
9429         root->section,
9430         root->spec);
9431     for (const auto &e : root->children())
9432     {
9433       printNavTree(e.get(),indent+2);
9434     }
9435   }
9436 }
9437
9438
9439 //----------------------------------------------------------------------------
9440 // generate the example documentation
9441
9442 static void generateExampleDocs()
9443 {
9444   g_outputList->disable(OutputType::Man);
9445   for (const auto &pd : *Doxygen::exampleLinkedMap)
9446   {
9447     msg("Generating docs for example %s...\n",qPrint(pd->name()));
9448     auto intf = Doxygen::parserManager->getCodeParser(".c"); // TODO: do this on code type
9449     intf->resetCodeParserState();
9450     QCString n=pd->getOutputFileBase();
9451     startFile(*g_outputList,n,n,pd->name());
9452     startTitle(*g_outputList,n);
9453     g_outputList->docify(pd->name());
9454     endTitle(*g_outputList,n,QCString());
9455     g_outputList->startContents();
9456     QCString lineNoOptStr;
9457     if (pd->showLineNo())
9458     {
9459       lineNoOptStr="{lineno}";
9460     }
9461     g_outputList->generateDoc(pd->docFile(),                       // file
9462                          pd->docLine(),                            // startLine
9463                          pd.get(),                                 // context
9464                          0,                                        // memberDef
9465                          (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9466                          pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9467                          TRUE,                                     // index words
9468                          TRUE,                                     // is example
9469                          pd->name(),
9470                          FALSE,
9471                          FALSE,
9472                          Config_getBool(MARKDOWN_SUPPORT)
9473                         );
9474     endFile(*g_outputList); // contains g_outputList->endContents()
9475   }
9476   g_outputList->enable(OutputType::Man);
9477 }
9478
9479 //----------------------------------------------------------------------------
9480 // generate module pages
9481
9482 static void generateGroupDocs()
9483 {
9484   for (const auto &gd : *Doxygen::groupLinkedMap)
9485   {
9486     if (!gd->isReference())
9487     {
9488       gd->writeDocumentation(*g_outputList);
9489     }
9490   }
9491 }
9492
9493 //----------------------------------------------------------------------------
9494 // generate module pages
9495
9496 static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
9497 {
9498   std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9499   if (numThreads>1) // multi threaded processing
9500   {
9501     struct DocContext
9502     {
9503       DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
9504         : cdm(cdm_), ol(ol_) {}
9505       ClassDefMutable *cdm;
9506       OutputList ol;
9507     };
9508     ThreadPool threadPool(numThreads);
9509     std::vector< std::future< std::shared_ptr<DocContext> > > results;
9510     // for each class in the namespace...
9511     for (const auto &cd : classList)
9512     {
9513       ClassDefMutable *cdm = toClassDefMutable(cd);
9514       if (cdm)
9515       {
9516         auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
9517         auto processFile = [ctx]()
9518         {
9519           if ( ( ctx->cdm->isLinkableInProject() &&
9520                 ctx->cdm->templateMaster()==0
9521                ) // skip external references, anonymous compounds and
9522               // template instances and nested classes
9523               && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
9524              )
9525           {
9526             msg("Generating docs for compound %s...\n",qPrint(ctx->cdm->name()));
9527             ctx->cdm->writeDocumentation(ctx->ol);
9528             ctx->cdm->writeMemberList(ctx->ol);
9529           }
9530           ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
9531           return ctx;
9532         };
9533         results.emplace_back(threadPool.queue(processFile));
9534       }
9535     }
9536     // wait for the results
9537     for (auto &f : results)
9538     {
9539       auto ctx = f.get();
9540     }
9541   }
9542   else // single threaded processing
9543   {
9544     // for each class in the namespace...
9545     for (const auto &cd : classList)
9546     {
9547       ClassDefMutable *cdm = toClassDefMutable(cd);
9548       if (cdm)
9549       {
9550         if ( ( cd->isLinkableInProject() &&
9551               cd->templateMaster()==0
9552              ) // skip external references, anonymous compounds and
9553             // template instances and nested classes
9554             && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
9555            )
9556         {
9557           msg("Generating docs for compound %s...\n",qPrint(cd->name()));
9558
9559           cdm->writeDocumentation(*g_outputList);
9560           cdm->writeMemberList(*g_outputList);
9561         }
9562         cdm->writeDocumentationForInnerClasses(*g_outputList);
9563       }
9564     }
9565   }
9566 }
9567
9568 static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
9569 {
9570   // for each concept in the namespace...
9571   for (const auto &cd : conceptList)
9572   {
9573     ConceptDefMutable *cdm = toConceptDefMutable(cd);
9574     if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
9575     {
9576       msg("Generating docs for concept %s...\n",qPrint(cd->name()));
9577       cdm->writeDocumentation(*g_outputList);
9578     }
9579   }
9580 }
9581
9582 static void generateNamespaceDocs()
9583 {
9584   bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
9585
9586   //writeNamespaceIndex(*g_outputList);
9587
9588   // for each namespace...
9589   for (const auto &nd : *Doxygen::namespaceLinkedMap)
9590   {
9591     if (nd->isLinkableInProject())
9592     {
9593       NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
9594       if (ndm)
9595       {
9596         msg("Generating docs for namespace %s\n",qPrint(nd->name()));
9597         ndm->writeDocumentation(*g_outputList);
9598       }
9599     }
9600
9601     generateNamespaceClassDocs(nd->getClasses());
9602     if (sliceOpt)
9603     {
9604       generateNamespaceClassDocs(nd->getInterfaces());
9605       generateNamespaceClassDocs(nd->getStructs());
9606       generateNamespaceClassDocs(nd->getExceptions());
9607     }
9608     generateNamespaceConceptDocs(nd->getConcepts());
9609   }
9610 }
9611
9612 static void runHtmlHelpCompiler()
9613 {
9614   std::string oldDir = Dir::currentDirPath();
9615   Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
9616   Portable::setShortDir();
9617   if (Portable::system(Config_getString(HHC_LOCATION).data(), qPrint(HtmlHelp::hhpFileName), Debug::isFlagSet(Debug::ExtCmd))!=1)
9618   {
9619     err("failed to run html help compiler on %s\n", qPrint(HtmlHelp::hhpFileName));
9620   }
9621   Dir::setCurrent(oldDir);
9622 }
9623
9624 static void runQHelpGenerator()
9625 {
9626   QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
9627   std::string oldDir = Dir::currentDirPath();
9628   Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
9629
9630   QCString qhgLocation=Config_getString(QHG_LOCATION);
9631   if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
9632   {
9633     // run qhelpgenerator -v and extract the Qt version used
9634     QCString cmd=qhgLocation+ " -v 2>&1";
9635     Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",qPrint(cmd));
9636     FILE *f=Portable::popen(cmd,"r");
9637     if (!f)
9638     {
9639       err("could not execute %s\n",qPrint(qhgLocation));
9640     }
9641     else
9642     {
9643       const size_t bufSize = 1024;
9644       char inBuf[bufSize+1];
9645       size_t numRead=fread(inBuf,1,bufSize,f);
9646       inBuf[numRead] = '\0';
9647       Debug::print(Debug::Qhp,0,inBuf);
9648       Portable::pclose(f);
9649
9650       int qtVersion=0;
9651       static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
9652       reg::Match match;
9653       std::string s = inBuf;
9654       if (reg::search(inBuf,match,versionReg))
9655       {
9656         qtVersion = 10000*QCString(match[1].str()).toInt() +
9657                       100*QCString(match[2].str()).toInt() +
9658                           QCString(match[3].str()).toInt();
9659       }
9660       if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
9661       {
9662         // dump the output of qhelpgenerator -c file.qhp
9663         // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
9664         cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
9665         Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",qPrint(cmd));
9666         f=Portable::popen(cmd,"r");
9667         if (!f)
9668         {
9669           err("could not execute %s\n",qPrint(qhgLocation));
9670         }
9671         else
9672         {
9673           std::string output;
9674           while ((numRead=fread(inBuf,1,bufSize,f))>0)
9675           {
9676             inBuf[numRead] = '\0';
9677             output += inBuf;
9678           }
9679           Portable::pclose(f);
9680           Debug::print(Debug::Qhp,0,output.c_str());
9681         }
9682       }
9683     }
9684   }
9685
9686   if (Portable::system(qhgLocation, args, FALSE))
9687   {
9688     err("failed to run qhelpgenerator on %s\n",qPrint(Qhp::qhpFileName));
9689   }
9690   Dir::setCurrent(oldDir);
9691 }
9692
9693 //----------------------------------------------------------------------------
9694
9695 static void computeVerifiedDotPath()
9696 {
9697   // check dot path
9698   QCString dotPath = Config_getString(DOT_PATH);
9699   if (!dotPath.isEmpty())
9700   {
9701     FileInfo fi(dotPath.str());
9702     if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
9703     {
9704       dotPath = dotPath+"/dot"+Portable::commandExtension();
9705       FileInfo dp(dotPath.str());
9706       if (!dp.exists() || !dp.isFile())
9707       {
9708         warn_uncond("the dot tool could not be found as '%s'\n",qPrint(dotPath));
9709         dotPath = "dot";
9710         dotPath += Portable::commandExtension();
9711       }
9712     }
9713 #if defined(_WIN32) // convert slashes
9714     uint32_t i=0,l=dotPath.length();
9715     for (i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
9716 #endif
9717   }
9718   else
9719   {
9720     dotPath = "dot";
9721     dotPath += Portable::commandExtension();
9722   }
9723   Doxygen::verifiedDotPath = dotPath;
9724   TRACE("{}",Doxygen::verifiedDotPath);
9725 }
9726
9727 //----------------------------------------------------------------------------
9728
9729 /*! Generate a template version of the configuration file.
9730  *  If the \a shortList parameter is TRUE a configuration file without
9731  *  comments will be generated.
9732  */
9733 static void generateConfigFile(const QCString &configFile,bool shortList,
9734                                bool updateOnly=FALSE)
9735 {
9736   std::ofstream f;
9737   bool fileOpened=openOutputFile(configFile,f);
9738   bool writeToStdout=configFile=="-";
9739   if (fileOpened)
9740   {
9741     TextStream t(&f);
9742     Config::writeTemplate(t,shortList,updateOnly);
9743     if (!writeToStdout)
9744     {
9745       if (!updateOnly)
9746       {
9747         msg("\n\nConfiguration file '%s' created.\n\n",qPrint(configFile));
9748         msg("Now edit the configuration file and enter\n\n");
9749         if (configFile!="Doxyfile" && configFile!="doxyfile")
9750           msg("  doxygen %s\n\n",qPrint(configFile));
9751         else
9752           msg("  doxygen\n\n");
9753         msg("to generate the documentation for your project\n\n");
9754       }
9755       else
9756       {
9757         msg("\n\nConfiguration file '%s' updated.\n\n",qPrint(configFile));
9758       }
9759     }
9760   }
9761   else
9762   {
9763     term("Cannot open file %s for writing\n",qPrint(configFile));
9764   }
9765 }
9766
9767 static void compareDoxyfile(Config::CompareMode diffList)
9768 {
9769   std::ofstream f;
9770   bool fileOpened=openOutputFile("-",f);
9771   if (fileOpened)
9772   {
9773     TextStream t(&f);
9774     Config::compareDoxyfile(t,diffList);
9775   }
9776   else
9777   {
9778     term("Cannot open stdout for writing\n");
9779   }
9780 }
9781
9782 //----------------------------------------------------------------------------
9783 // read and parse a tag file
9784
9785 static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
9786 {
9787   QCString fileName;
9788   QCString destName;
9789   int eqPos = tagLine.find('=');
9790   if (eqPos!=-1) // tag command contains a destination
9791   {
9792     fileName = tagLine.left(eqPos).stripWhiteSpace();
9793     destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
9794     if (fileName.isEmpty() || destName.isEmpty()) return;
9795     FileInfo fi(fileName.str());
9796     Doxygen::tagDestinationMap.insert(
9797         std::make_pair(fi.absFilePath(), destName.str()));
9798     //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
9799   }
9800   else
9801   {
9802     fileName = tagLine;
9803   }
9804
9805   FileInfo fi(fileName.str());
9806   if (!fi.exists() || !fi.isFile())
9807   {
9808     err("Tag file '%s' does not exist or is not a file. Skipping it...\n",
9809         qPrint(fileName));
9810     return;
9811   }
9812
9813   if (!destName.isEmpty())
9814     msg("Reading tag file '%s', location '%s'...\n",qPrint(fileName),qPrint(destName));
9815   else
9816     msg("Reading tag file '%s'...\n",qPrint(fileName));
9817
9818   parseTagFile(root,fi.absFilePath().c_str());
9819 }
9820
9821 //----------------------------------------------------------------------------
9822 static void copyLatexStyleSheet()
9823 {
9824   const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
9825   for (const auto &sheet : latexExtraStyleSheet)
9826   {
9827     std::string fileName = sheet;
9828     if (!fileName.empty())
9829     {
9830       FileInfo fi(fileName);
9831       if (!fi.exists())
9832       {
9833         err("Style sheet '%s' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName));
9834       }
9835       else
9836       {
9837         QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
9838         if (!checkExtension(fi.fileName().c_str(), LATEX_STYLE_EXTENSION))
9839         {
9840           destFileName += LATEX_STYLE_EXTENSION;
9841         }
9842         copyFile(QCString(fileName), destFileName);
9843       }
9844     }
9845   }
9846 }
9847
9848 //----------------------------------------------------------------------------
9849 static void copyStyleSheet()
9850 {
9851   QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
9852   if (!htmlStyleSheet.isEmpty())
9853   {
9854     if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
9855     {
9856       FileInfo fi(htmlStyleSheet.str());
9857       if (!fi.exists())
9858       {
9859         err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",qPrint(htmlStyleSheet));
9860         htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
9861       }
9862       else
9863       {
9864         QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9865         copyFile(htmlStyleSheet,destFileName);
9866       }
9867     }
9868   }
9869   const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
9870   for (const auto &sheet : htmlExtraStyleSheet)
9871   {
9872     QCString fileName(sheet);
9873     if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
9874     {
9875       FileInfo fi(fileName.str());
9876       if (!fi.exists())
9877       {
9878         err("Style sheet '%s' specified by HTML_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName));
9879       }
9880       else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
9881       {
9882         err("Style sheet %s specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",qPrint(fi.fileName()));
9883       }
9884       else
9885       {
9886         QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9887         copyFile(QCString(fileName), destFileName);
9888       }
9889     }
9890   }
9891 }
9892
9893 static void copyLogo(const QCString &outputOption)
9894 {
9895   QCString projectLogo = Config_getString(PROJECT_LOGO);
9896   if (!projectLogo.isEmpty())
9897   {
9898     FileInfo fi(projectLogo.str());
9899     if (!fi.exists())
9900     {
9901       err("Project logo '%s' specified by PROJECT_LOGO does not exist!\n",qPrint(projectLogo));
9902       projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
9903     }
9904     else
9905     {
9906       QCString destFileName = outputOption+"/"+fi.fileName();
9907       copyFile(projectLogo,destFileName);
9908       Doxygen::indexList->addImageFile(fi.fileName().c_str());
9909     }
9910   }
9911 }
9912
9913 static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
9914 {
9915   for (const auto &fileName : files)
9916   {
9917     if (!fileName.empty())
9918     {
9919       FileInfo fi(fileName);
9920       if (!fi.exists())
9921       {
9922         err("Extra file '%s' specified in %s does not exist!\n", fileName.c_str(),qPrint(filesOption));
9923       }
9924       else
9925       {
9926         QCString destFileName = outputOption+"/"+fi.fileName();
9927         Doxygen::indexList->addImageFile(fi.fileName().c_str());
9928         copyFile(QCString(fileName), destFileName);
9929       }
9930     }
9931   }
9932 }
9933
9934 //----------------------------------------------------------------------------
9935
9936 static void generateDiskNames()
9937 {
9938   for (const auto &fn : *Doxygen::inputNameLinkedMap)
9939   {
9940     struct FileEntry
9941     {
9942       FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
9943       QCString path;
9944       FileDef *fileDef;
9945     };
9946
9947     // collect the entry for which to compute the longest common prefix (LCP) of the path
9948     std::vector<FileEntry> fileEntries;
9949     for (const auto &fd : *fn)
9950     {
9951       if (!fd->isReference()) // skip external references
9952       {
9953         fileEntries.emplace_back(fd->getPath(),fd.get());
9954       }
9955     }
9956
9957     size_t size = fileEntries.size();
9958
9959     if (size==1) // name if unique, so diskname is simply the name
9960     {
9961       FileDef *fd = fileEntries[0].fileDef;
9962       fd->setDiskName(fn->fileName());
9963     }
9964     else if (size>1) // multiple occurrences of the same file name
9965     {
9966       // sort the array
9967       std::sort(fileEntries.begin(),
9968                 fileEntries.end(),
9969                 [](const FileEntry &fe1,const FileEntry &fe2)
9970                 { return fe1.path < fe2.path; }
9971                );
9972
9973       // since the entries are sorted, the common prefix of the whole array is same
9974       // as the common prefix between the first and last entry
9975       const FileEntry &first = fileEntries[0];
9976       const FileEntry &last =  fileEntries[size-1];
9977       int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
9978       int last_path_size  = static_cast<int>(last.path.size())-1;  // -1 to skip trailing slash
9979       int j=0;
9980       int i=0;
9981       for (i=0;i<first_path_size && i<last_path_size;i++)
9982       {
9983         if (first.path[i]=='/') j=i;
9984         if (first.path[i]!=last.path[i]) break;
9985       }
9986       if (i==first_path_size && i<last_path_size && last.path[i]=='/')
9987       {
9988         // case first='some/path' and last='some/path/more' => match is 'some/path'
9989         j=first_path_size;
9990       }
9991       else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
9992       {
9993         // case first='some/path/more' and last='some/path' => match is 'some/path'
9994         j=last_path_size;
9995       }
9996
9997       // add non-common part of the path to the name
9998       for (auto &fileEntry : fileEntries)
9999       {
10000          QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10001          fileEntry.fileDef->setName(prefix+fn->fileName());
10002          //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10003          fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10004       }
10005     }
10006   }
10007 }
10008
10009
10010
10011 //----------------------------------------------------------------------------
10012
10013 static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10014 {
10015   QCString fileName=fn;
10016   QCString extension;
10017   int sep = fileName.findRev('/');
10018   int ei = fileName.findRev('.');
10019   if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10020   {
10021     extension=fileName.right(fileName.length()-ei);
10022   }
10023   else
10024   {
10025     extension = ".no_extension";
10026   }
10027
10028   return Doxygen::parserManager->getOutlineParser(extension);
10029 }
10030
10031 static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10032                       FileDef *fd,const QCString &fn,
10033                       ClangTUParser *clangParser,bool newTU)
10034 {
10035   QCString fileName=fn;
10036   AUTO_TRACE("fileName={}",fileName);
10037   QCString extension;
10038   int ei = fileName.findRev('.');
10039   if (ei!=-1)
10040   {
10041     extension=fileName.right(fileName.length()-ei);
10042   }
10043   else
10044   {
10045     extension = ".no_extension";
10046   }
10047
10048   FileInfo fi(fileName.str());
10049   BufStr preBuf(fi.size()+4096);
10050
10051   if (Config_getBool(ENABLE_PREPROCESSING) &&
10052       parser.needsPreprocessing(extension))
10053   {
10054     Preprocessor preprocessor;
10055     const StringVector &includePath = Config_getList(INCLUDE_PATH);
10056     for (const auto &s : includePath)
10057     {
10058       std::string absPath = FileInfo(s).absFilePath();
10059       preprocessor.addSearchDir(absPath.c_str());
10060     }
10061     BufStr inBuf(fi.size()+4096);
10062     msg("Preprocessing %s...\n",qPrint(fn));
10063     readInputFile(fileName,inBuf);
10064     inBuf.addTerminalCharIfMissing('\n');
10065     preprocessor.processFile(fileName,inBuf,preBuf);
10066   }
10067   else // no preprocessing
10068   {
10069     msg("Reading %s...\n",qPrint(fn));
10070     readInputFile(fileName,preBuf);
10071     preBuf.addTerminalCharIfMissing('\n');
10072   }
10073
10074   BufStr convBuf(preBuf.curPos()+1024);
10075
10076   // convert multi-line C++ comments to C style comments
10077   convertCppComments(preBuf,convBuf,fileName);
10078
10079   convBuf.addChar('\0');
10080
10081   std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10082   // use language parse to parse the file
10083   if (clangParser)
10084   {
10085     if (newTU) clangParser->parse();
10086     clangParser->switchToFile(fd);
10087   }
10088   parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10089   fileRoot->setFileDef(fd);
10090   return fileRoot;
10091 }
10092
10093 //! parse the list of input files
10094 static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10095 {
10096   AUTO_TRACE();
10097 #if USE_LIBCLANG
10098   if (Doxygen::clangAssistedParsing)
10099   {
10100     StringUnorderedSet processedFiles;
10101
10102     // create a dictionary with files to process
10103     StringUnorderedSet filesToProcess;
10104     for (const auto &s : g_inputFiles)
10105     {
10106       filesToProcess.insert(s);
10107     }
10108
10109     std::mutex processedFilesLock;
10110     // process source files (and their include dependencies)
10111     std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10112     msg("Processing input using %zu threads.\n",numThreads);
10113     ThreadPool threadPool(numThreads);
10114     using FutureType = std::vector< std::shared_ptr<Entry> >;
10115     std::vector< std::future< FutureType > > results;
10116     for (const auto &s : g_inputFiles)
10117     {
10118       bool ambig;
10119       FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10120       ASSERT(fd!=0);
10121       if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt_Cpp) // this is a source file
10122       {
10123         // lambda representing the work to executed by a thread
10124         auto processFile = [s,&filesToProcess,&processedFilesLock,&processedFiles]() {
10125           bool ambig_l;
10126           std::vector< std::shared_ptr<Entry> > roots;
10127           FileDef *fd_l = findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig_l);
10128           auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10129           auto parser = getParserForFile(s.c_str());
10130           auto fileRoot { parseFile(*parser.get(),fd_l,s.c_str(),clangParser.get(),true) };
10131           roots.push_back(fileRoot);
10132
10133           // Now process any include files in the same translation unit
10134           // first. When libclang is used this is much more efficient.
10135           for (auto incFile : clangParser->filesInSameTU())
10136           {
10137             if (filesToProcess.find(incFile)!=filesToProcess.end())
10138             {
10139               bool needsToBeProcessed;
10140               {
10141                 std::lock_guard<std::mutex> lock(processedFilesLock);
10142                 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10143                 if (needsToBeProcessed) processedFiles.insert(incFile);
10144               }
10145               if (incFile!=s && needsToBeProcessed)
10146               {
10147                 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig_l);
10148                 if (ifd && !ifd->isReference())
10149                 {
10150                   //printf("  Processing %s in same translation unit as %s\n",incFile,s->c_str());
10151                   fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10152                   roots.push_back(fileRoot);
10153                 }
10154               }
10155             }
10156           }
10157           return roots;
10158         };
10159         // dispatch the work and collect the future results
10160         results.emplace_back(threadPool.queue(processFile));
10161       }
10162     }
10163     // synchronise with the Entry result lists produced and add them to the root
10164     for (auto &f : results)
10165     {
10166       auto l = f.get();
10167       for (auto &e : l)
10168       {
10169         root->moveToSubEntryAndKeep(e);
10170       }
10171     }
10172     // process remaining files
10173     results.clear();
10174     for (const auto &s : g_inputFiles)
10175     {
10176       if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10177       {
10178         // lambda representing the work to executed by a thread
10179         auto processFile = [s]() {
10180           bool ambig;
10181           std::vector< std::shared_ptr<Entry> > roots;
10182           FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10183           auto parser { getParserForFile(s.c_str()) };
10184           bool useClang = getLanguageFromFileName(s.c_str())==SrcLangExt_Cpp;
10185           if (useClang)
10186           {
10187             auto clangParser = ClangParser::instance()->createTUParser(fd);
10188             auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10189             roots.push_back(fileRoot);
10190           }
10191           else
10192           {
10193             auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10194             roots.push_back(fileRoot);
10195           }
10196           return roots;
10197         };
10198         results.emplace_back(threadPool.queue(processFile));
10199       }
10200     }
10201     // synchronise with the Entry result lists produced and add them to the root
10202     for (auto &f : results)
10203     {
10204       auto l = f.get();
10205       for (auto &e : l)
10206       {
10207         root->moveToSubEntryAndKeep(e);
10208       }
10209     }
10210   }
10211   else // normal processing
10212 #endif
10213   {
10214     std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10215     msg("Processing input using %zu threads.\n",numThreads);
10216     ThreadPool threadPool(numThreads);
10217     using FutureType = std::shared_ptr<Entry>;
10218     std::vector< std::future< FutureType > > results;
10219     for (const auto &s : g_inputFiles)
10220     {
10221       // lambda representing the work to executed by a thread
10222       auto processFile = [s]() {
10223         bool ambig;
10224         FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10225         auto parser = getParserForFile(s.c_str());
10226         auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10227         return fileRoot;
10228       };
10229       // dispatch the work and collect the future results
10230       results.emplace_back(threadPool.queue(processFile));
10231     }
10232     // synchronise with the Entry results produced and add them to the root
10233     for (auto &f : results)
10234     {
10235       root->moveToSubEntryAndKeep(f.get());
10236     }
10237   }
10238 }
10239
10240 //! parse the list of input files
10241 static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10242 {
10243   AUTO_TRACE();
10244 #if USE_LIBCLANG
10245   if (Doxygen::clangAssistedParsing)
10246   {
10247     StringUnorderedSet processedFiles;
10248
10249     // create a dictionary with files to process
10250     StringUnorderedSet filesToProcess;
10251     for (const auto &s : g_inputFiles)
10252     {
10253       filesToProcess.insert(s);
10254     }
10255
10256     // process source files (and their include dependencies)
10257     for (const auto &s : g_inputFiles)
10258     {
10259       bool ambig;
10260       FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10261       ASSERT(fd!=0);
10262       if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(s.c_str())==SrcLangExt_Cpp) // this is a source file
10263       {
10264         auto clangParser = ClangParser::instance()->createTUParser(fd);
10265         auto parser { getParserForFile(s.c_str()) };
10266         auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10267         root->moveToSubEntryAndKeep(fileRoot);
10268         processedFiles.insert(s);
10269
10270         // Now process any include files in the same translation unit
10271         // first. When libclang is used this is much more efficient.
10272         for (auto incFile : clangParser->filesInSameTU())
10273         {
10274           //printf("    file %s\n",incFile.c_str());
10275           if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10276               processedFiles.find(incFile)==processedFiles.end())   // and is not processed already
10277           {
10278             FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
10279             if (ifd && !ifd->isReference())
10280             {
10281               //printf("  Processing %s in same translation unit as %s\n",incFile.c_str(),s.c_str());
10282               fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10283               root->moveToSubEntryAndKeep(fileRoot);
10284               processedFiles.insert(incFile);
10285             }
10286           }
10287         }
10288       }
10289     }
10290     // process remaining files
10291     for (const auto &s : g_inputFiles)
10292     {
10293       if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10294       {
10295         bool ambig;
10296         FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10297         if (getLanguageFromFileName(s.c_str())==SrcLangExt_Cpp) // not yet processed
10298         {
10299           auto clangParser = ClangParser::instance()->createTUParser(fd);
10300           auto parser { getParserForFile(s.c_str()) };
10301           auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10302           root->moveToSubEntryAndKeep(fileRoot);
10303         }
10304         else
10305         {
10306           std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10307           std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10308           root->moveToSubEntryAndKeep(fileRoot);
10309         }
10310         processedFiles.insert(s);
10311       }
10312     }
10313   }
10314   else // normal processing
10315 #endif
10316   {
10317     for (const auto &s : g_inputFiles)
10318     {
10319       bool ambig;
10320       FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10321       ASSERT(fd!=0);
10322       std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10323       std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10324       root->moveToSubEntryAndKeep(std::move(fileRoot));
10325     }
10326   }
10327 }
10328
10329 // resolves a path that may include symlinks, if a recursive symlink is
10330 // found an empty string is returned.
10331 static std::string resolveSymlink(const std::string &path)
10332 {
10333   int sepPos=0;
10334   int oldPos=0;
10335   StringSet nonSymlinks;
10336   StringSet known;
10337   QCString result(path);
10338   QCString oldPrefix = "/";
10339   do
10340   {
10341 #if defined(_WIN32)
10342     // UNC path, skip server and share name
10343     if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
10344       sepPos = result.find('/',2);
10345     if (sepPos!=-1)
10346       sepPos = result.find('/',sepPos+1);
10347 #else
10348     sepPos = result.find('/',sepPos+1);
10349 #endif
10350     QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10351     if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10352     {
10353       FileInfo fi(prefix.str());
10354       if (fi.isSymLink())
10355       {
10356         QCString target = fi.readLink();
10357         bool isRelative = FileInfo(target.str()).isRelative();
10358         if (isRelative)
10359         {
10360           target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10361         }
10362         if (sepPos!=-1)
10363         {
10364           if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10365           {
10366             target+='/';
10367           }
10368           target+=result.mid(sepPos);
10369         }
10370         result = Dir::cleanDirPath(target.str());
10371         if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10372         known.insert(result.str());
10373         if (isRelative)
10374         {
10375           sepPos = oldPos;
10376         }
10377         else // link to absolute path
10378         {
10379           sepPos = 0;
10380           oldPrefix = "/";
10381         }
10382       }
10383       else
10384       {
10385         nonSymlinks.insert(prefix.str());
10386         oldPrefix = prefix;
10387       }
10388       oldPos = sepPos;
10389     }
10390   }
10391   while (sepPos!=-1);
10392   return Dir::cleanDirPath(result.str());
10393 }
10394
10395 static StringUnorderedSet g_pathsVisited(1009);
10396
10397 //----------------------------------------------------------------------------
10398 // Read all files matching at least one pattern in 'patList' in the
10399 // directory represented by 'fi'.
10400 // The directory is read iff the recursiveFlag is set.
10401 // The contents of all files is append to the input string
10402
10403 static void readDir(FileInfo *fi,
10404             FileNameLinkedMap *fnMap,
10405             StringUnorderedSet *exclSet,
10406             const StringVector *patList,
10407             const StringVector *exclPatList,
10408             StringVector *resultList,
10409             StringUnorderedSet *resultSet,
10410             bool errorIfNotExist,
10411             bool recursive,
10412             StringUnorderedSet *killSet,
10413             StringSet *paths
10414            )
10415 {
10416   std::string dirName = fi->absFilePath();
10417   if (paths && !dirName.empty())
10418   {
10419     paths->insert(dirName);
10420   }
10421   //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
10422   if (fi->isSymLink())
10423   {
10424     dirName = resolveSymlink(dirName);
10425     if (dirName.empty())
10426     {
10427       //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
10428       return;  // recursive symlink
10429     }
10430   }
10431
10432   if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
10433   {
10434     //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
10435     return; // already visited path
10436   }
10437   g_pathsVisited.insert(dirName);
10438
10439   Dir dir(dirName);
10440   msg("Searching for files in directory %s\n", qPrint(fi->absFilePath()));
10441   //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
10442
10443   StringVector dirResultList;
10444
10445   for (const auto &dirEntry : dir.iterator())
10446   {
10447     FileInfo cfi(dirEntry.path());
10448     if (exclSet==0 || exclSet->find(cfi.absFilePath())==exclSet->end())
10449     { // file should not be excluded
10450       //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
10451       if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
10452       {
10453       }
10454       else if (!cfi.exists() || !cfi.isReadable())
10455       {
10456         if (errorIfNotExist)
10457         {
10458           warn_uncond("source '%s' is not a readable file or directory... skipping.\n",cfi.absFilePath().c_str());
10459         }
10460       }
10461       else if (cfi.isFile() &&
10462           (patList==0 || patternMatch(cfi,*patList)) &&
10463           (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10464           (killSet==0 || killSet->find(cfi.absFilePath())==killSet->end())
10465           )
10466       {
10467         std::string name=cfi.fileName();
10468         std::string path=cfi.dirPath()+"/";
10469         std::string fullName=path+name;
10470         if (fnMap)
10471         {
10472           auto fd = createFileDef(QCString(path),QCString(name));
10473           FileName *fn=0;
10474           if (!name.empty())
10475           {
10476             fn = fnMap->add(QCString(name),QCString(fullName));
10477             fn->push_back(std::move(fd));
10478           }
10479         }
10480         dirResultList.push_back(fullName);
10481         if (resultSet) resultSet->insert(fullName);
10482         if (killSet) killSet->insert(fullName);
10483       }
10484       else if (recursive &&
10485           cfi.isDir() &&
10486           (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10487           cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
10488       {
10489         FileInfo acfi(cfi.absFilePath());
10490         readDir(&acfi,fnMap,exclSet,
10491             patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
10492             recursive,killSet,paths);
10493       }
10494     }
10495   }
10496   if (resultList && !dirResultList.empty())
10497   {
10498     // sort the resulting list to make the order platform independent.
10499     std::sort(dirResultList.begin(),
10500               dirResultList.end(),
10501               [](const auto &f1,const auto &f2) { return qstricmp(f1.c_str(),f2.c_str())<0; });
10502
10503     // append the sorted results to resultList
10504     resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
10505   }
10506 }
10507
10508
10509 //----------------------------------------------------------------------------
10510 // read a file or all files in a directory and append their contents to the
10511 // input string. The names of the files are appended to the 'fiList' list.
10512
10513 void readFileOrDirectory(const QCString &s,
10514                         FileNameLinkedMap *fnMap,
10515                         StringUnorderedSet *exclSet,
10516                         const StringVector *patList,
10517                         const StringVector *exclPatList,
10518                         StringVector *resultList,
10519                         StringUnorderedSet *resultSet,
10520                         bool recursive,
10521                         bool errorIfNotExist,
10522                         StringUnorderedSet *killSet,
10523                         StringSet *paths
10524                        )
10525 {
10526   //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
10527   // strip trailing slashes
10528   if (s.isEmpty()) return;
10529
10530   g_pathsVisited.clear();
10531
10532   FileInfo fi(s.str());
10533   //printf("readFileOrDirectory(%s)\n",s);
10534   {
10535     if (exclSet==0 || exclSet->find(fi.absFilePath())==exclSet->end())
10536     {
10537       if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
10538       {
10539       }
10540       else if (!fi.exists() || !fi.isReadable())
10541       {
10542         if (errorIfNotExist)
10543         {
10544           warn_uncond("source '%s' is not a readable file or directory... skipping.\n",qPrint(s));
10545         }
10546       }
10547       else if (fi.isFile())
10548       {
10549         std::string dirPath = fi.dirPath(true);
10550         std::string filePath = fi.absFilePath();
10551         if (paths && !dirPath.empty())
10552         {
10553           paths->insert(dirPath);
10554         }
10555         //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
10556         if (killSet==0 || killSet->find(filePath)==killSet->end())
10557         {
10558           std::string name=fi.fileName();
10559           if (fnMap)
10560           {
10561             auto fd = createFileDef(QCString(dirPath+"/"),QCString(name));
10562             if (!name.empty())
10563             {
10564               FileName *fn = fnMap->add(QCString(name),QCString(filePath));
10565               fn->push_back(std::move(fd));
10566             }
10567           }
10568           if (resultList || resultSet)
10569           {
10570             if (resultList) resultList->push_back(filePath);
10571             if (resultSet) resultSet->insert(filePath);
10572           }
10573
10574           if (killSet) killSet->insert(fi.absFilePath());
10575         }
10576       }
10577       else if (fi.isDir()) // readable dir
10578       {
10579         readDir(&fi,fnMap,exclSet,patList,
10580             exclPatList,resultList,resultSet,errorIfNotExist,
10581             recursive,killSet,paths);
10582       }
10583     }
10584   }
10585 }
10586
10587 //----------------------------------------------------------------------------
10588
10589 static void dumpSymbol(TextStream &t,Definition *d)
10590 {
10591   QCString anchor;
10592   if (d->definitionType()==Definition::TypeMember)
10593   {
10594     MemberDef *md = toMemberDef(d);
10595     anchor=":"+md->anchor();
10596   }
10597   QCString scope;
10598   QCString fn = d->getOutputFileBase();
10599   addHtmlExtensionIfMissing(fn);
10600   if (d->getOuterScope() && d->getOuterScope()!=Doxygen::globalScope)
10601   {
10602     scope = fn;
10603   }
10604   t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
10605     << fn+anchor << "','"
10606     << scope << "','"
10607     << d->name() << "','"
10608     << d->getDefFileName() << "','"
10609     << d->getDefLine()
10610     << "');\n";
10611 }
10612
10613 static void dumpSymbolMap()
10614 {
10615   std::ofstream f = Portable::openOutputStream("symbols.sql");
10616   if (f.is_open())
10617   {
10618     TextStream t(&f);
10619     for (const auto &[name,symList] : *Doxygen::symbolMap)
10620     {
10621       for (const auto &def : symList)
10622       {
10623         dumpSymbol(t,def);
10624       }
10625     }
10626   }
10627 }
10628
10629 // print developer options of doxygen
10630 static void devUsage()
10631 {
10632   msg("Developer parameters:\n");
10633   msg("  -m          dump symbol map\n");
10634   msg("  -b          making messages output unbuffered\n");
10635 #if ENABLE_TRACING
10636   msg("  -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
10637 #endif
10638   msg("  -T          activates output generation via Django like template\n");
10639   msg("  -d <level>  enable a debug level, such as (multiple invocations of -d are possible):\n");
10640   Debug::printFlags();
10641 }
10642
10643
10644 //----------------------------------------------------------------------------
10645 // print the version of doxygen
10646
10647 static void version(const bool extended)
10648 {
10649   QCString versionString = getFullVersion();
10650   msg("%s\n",qPrint(versionString));
10651   if (extended)
10652   {
10653     QCString extVers;
10654     if (!extVers.isEmpty()) extVers+= ", ";
10655     extVers += "sqlite3 ";
10656     extVers += sqlite3_libversion();
10657 #if USE_LIBCLANG
10658     if (!extVers.isEmpty()) extVers+= ", ";
10659     extVers += "clang support ";
10660     extVers += CLANG_VERSION_STRING;
10661 #endif
10662     if (!extVers.isEmpty())
10663     {
10664       int lastComma = extVers.findRev(',');
10665       if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
10666       msg("    with %s.\n",qPrint(extVers));
10667     }
10668   }
10669 }
10670
10671 //----------------------------------------------------------------------------
10672 // print the usage of doxygen
10673
10674 static void usage(const QCString &name,const QCString &versionString)
10675 {
10676   msg("Doxygen version %s\nCopyright Dimitri van Heesch 1997-2021\n\n",qPrint(versionString));
10677   msg("You can use doxygen in a number of ways:\n\n");
10678   msg("1) Use doxygen to generate a template configuration file*:\n");
10679   msg("    %s [-s] -g [configName]\n\n",qPrint(name));
10680   msg("2) Use doxygen to update an old configuration file*:\n");
10681   msg("    %s [-s] -u [configName]\n\n",qPrint(name));
10682   msg("3) Use doxygen to generate documentation using an existing ");
10683   msg("configuration file*:\n");
10684   msg("    %s [configName]\n\n",qPrint(name));
10685   msg("4) Use doxygen to generate a template file controlling the layout of the\n");
10686   msg("   generated documentation:\n");
10687   msg("    %s -l [layoutFileName]\n\n",qPrint(name));
10688   msg("    In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n");
10689   msg("    If - is used for layoutFileName doxygen will write to standard output.\n\n");
10690   msg("5) Use doxygen to generate a template style sheet file for RTF, HTML or Latex.\n");
10691   msg("    RTF:        %s -w rtf styleSheetFile\n",qPrint(name));
10692   msg("    HTML:       %s -w html headerFile footerFile styleSheetFile [configFile]\n",qPrint(name));
10693   msg("    LaTeX:      %s -w latex headerFile footerFile styleSheetFile [configFile]\n\n",qPrint(name));
10694   msg("6) Use doxygen to generate a rtf extensions file\n");
10695   msg("    %s -e rtf extensionsFile\n\n",qPrint(name));
10696   msg("    If - is used for extensionsFile doxygen will write to standard output.\n\n");
10697   msg("7) Use doxygen to compare the used configuration file with the template configuration file\n");
10698   msg("    %s -x [configFile]\n\n",qPrint(name));
10699   msg("   Use doxygen to compare the used configuration file with the template configuration file\n");
10700   msg("   without replacing the environment variables or CMake type replacement variables\n");
10701   msg("    %s -x_noenv [configFile]\n\n",qPrint(name));
10702   msg("8) Use doxygen to show a list of built-in emojis.\n");
10703   msg("    %s -f emoji outputFileName\n\n",qPrint(name));
10704   msg("    If - is used for outputFileName doxygen will write to standard output.\n\n");
10705   msg("*) If -s is specified the comments of the configuration items in the config file will be omitted.\n");
10706   msg("   If configName is omitted 'Doxyfile' will be used as a default.\n");
10707   msg("   If - is used for configFile doxygen will write / read the configuration to /from standard output / input.\n\n");
10708   msg("If -q is used for a doxygen documentation run, doxygen will see this as if QUIET=YES has been set.\n\n");
10709   msg("-v print version string, -V print extended version information\n");
10710   msg("-h,-? prints usage help information\n");
10711   msg("%s -d prints additional usage flags for debugging purposes\n",qPrint(name));
10712 }
10713
10714 //----------------------------------------------------------------------------
10715 // read the argument of option 'c' from the comment argument list and
10716 // update the option index 'optInd'.
10717
10718 static const char *getArg(int argc,char **argv,int &optInd)
10719 {
10720   char *s=0;
10721   if (qstrlen(&argv[optInd][2])>0)
10722     s=&argv[optInd][2];
10723   else if (optInd+1<argc && argv[optInd+1][0]!='-')
10724     s=argv[++optInd];
10725   return s;
10726 }
10727
10728 //----------------------------------------------------------------------------
10729
10730 /** @brief /dev/null outline parser */
10731 class NullOutlineParser : public OutlineParserInterface
10732 {
10733   public:
10734     void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) {}
10735     bool needsPreprocessing(const QCString &) const { return FALSE; }
10736     void parsePrototype(const QCString &) {}
10737 };
10738
10739
10740 template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
10741 {
10742   return []() { return std::make_unique<T>(); };
10743 }
10744
10745 void initDoxygen()
10746 {
10747   initResources();
10748   QCString lang = Portable::getenv("LC_ALL");
10749   if (!lang.isEmpty()) Portable::setenv("LANG",lang);
10750   std::setlocale(LC_ALL,"");
10751   std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
10752   std::setlocale(LC_NUMERIC,"C");
10753
10754   Doxygen::symbolMap = new SymbolMap<Definition>;
10755
10756   Portable::correct_path();
10757
10758   Debug::startTimer();
10759   Doxygen::parserManager = new ParserManager(            make_parser_factory<NullOutlineParser>(),
10760                                                          make_parser_factory<FileCodeParser>());
10761   Doxygen::parserManager->registerParser("c",            make_parser_factory<COutlineParser>(),
10762                                                          make_parser_factory<CCodeParser>());
10763   Doxygen::parserManager->registerParser("python",       make_parser_factory<PythonOutlineParser>(),
10764                                                          make_parser_factory<PythonCodeParser>());
10765   Doxygen::parserManager->registerParser("fortran",      make_parser_factory<FortranOutlineParser>(),
10766                                                          make_parser_factory<FortranCodeParser>());
10767   Doxygen::parserManager->registerParser("fortranfree",  make_parser_factory<FortranOutlineParserFree>(),
10768                                                          make_parser_factory<FortranCodeParserFree>());
10769   Doxygen::parserManager->registerParser("fortranfixed", make_parser_factory<FortranOutlineParserFixed>(),
10770                                                          make_parser_factory<FortranCodeParserFixed>());
10771   Doxygen::parserManager->registerParser("vhdl",         make_parser_factory<VHDLOutlineParser>(),
10772                                                          make_parser_factory<VHDLCodeParser>());
10773   Doxygen::parserManager->registerParser("xml",          make_parser_factory<NullOutlineParser>(),
10774                                                          make_parser_factory<XMLCodeParser>());
10775   Doxygen::parserManager->registerParser("sql",          make_parser_factory<NullOutlineParser>(),
10776                                                          make_parser_factory<SQLCodeParser>());
10777   Doxygen::parserManager->registerParser("md",           make_parser_factory<MarkdownOutlineParser>(),
10778                                                          make_parser_factory<FileCodeParser>());
10779   Doxygen::parserManager->registerParser("lex",          make_parser_factory<LexOutlineParser>(),
10780                                                          make_parser_factory<LexCodeParser>());
10781
10782   // register any additional parsers here...
10783
10784   initDefaultExtensionMapping();
10785
10786 #if USE_LIBCLANG
10787   Doxygen::clangUsrMap   = new ClangUsrMap;
10788 #endif
10789   Doxygen::memberNameLinkedMap = new MemberNameLinkedMap;
10790   Doxygen::functionNameLinkedMap = new MemberNameLinkedMap;
10791   Doxygen::groupLinkedMap = new GroupLinkedMap;
10792   Doxygen::namespaceLinkedMap = new NamespaceLinkedMap;
10793   Doxygen::classLinkedMap = new ClassLinkedMap;
10794   Doxygen::hiddenClassLinkedMap = new ClassLinkedMap;
10795   Doxygen::conceptLinkedMap = new ConceptLinkedMap;
10796   Doxygen::dirLinkedMap = new DirLinkedMap;
10797   Doxygen::pageLinkedMap = new PageLinkedMap;          // all doc pages
10798   Doxygen::exampleLinkedMap = new PageLinkedMap;       // all examples
10799   //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
10800   Doxygen::indexList = new IndexList;
10801
10802   // initialisation of these globals depends on
10803   // configuration switches so we need to postpone these
10804   Doxygen::globalScope     = 0;
10805   Doxygen::inputNameLinkedMap   = 0;
10806   Doxygen::includeNameLinkedMap = 0;
10807   Doxygen::exampleNameLinkedMap = 0;
10808   Doxygen::imageNameLinkedMap   = 0;
10809   Doxygen::dotFileNameLinkedMap = 0;
10810   Doxygen::mscFileNameLinkedMap = 0;
10811   Doxygen::diaFileNameLinkedMap = 0;
10812
10813   /**************************************************************************
10814    *            Initialize some global constants
10815    **************************************************************************/
10816
10817   g_compoundKeywords.insert("template class");
10818   g_compoundKeywords.insert("template struct");
10819   g_compoundKeywords.insert("class");
10820   g_compoundKeywords.insert("struct");
10821   g_compoundKeywords.insert("union");
10822   g_compoundKeywords.insert("interface");
10823   g_compoundKeywords.insert("exception");
10824 }
10825
10826 void cleanUpDoxygen()
10827 {
10828   FormulaManager::instance().clear();
10829   SectionManager::instance().clear();
10830   ModuleManager::instance().clear();
10831
10832   delete Doxygen::indexList;
10833   delete Doxygen::inputNameLinkedMap;
10834   delete Doxygen::includeNameLinkedMap;
10835   delete Doxygen::exampleNameLinkedMap;
10836   delete Doxygen::imageNameLinkedMap;
10837   delete Doxygen::dotFileNameLinkedMap;
10838   delete Doxygen::mscFileNameLinkedMap;
10839   delete Doxygen::diaFileNameLinkedMap;
10840   Doxygen::mainPage.reset();
10841   delete Doxygen::pageLinkedMap;
10842   delete Doxygen::exampleLinkedMap;
10843   Doxygen::globalNamespaceDef.reset();
10844   Doxygen::globalScope = 0;
10845   delete Doxygen::parserManager;
10846   delete theTranslator;
10847   delete g_outputList;
10848
10849   delete Doxygen::memberNameLinkedMap;
10850   delete Doxygen::functionNameLinkedMap;
10851   delete Doxygen::groupLinkedMap;
10852   delete Doxygen::namespaceLinkedMap;
10853   delete Doxygen::dirLinkedMap;
10854   delete Doxygen::symbolMap;
10855 }
10856
10857 static int computeIdealCacheParam(size_t v)
10858 {
10859   //printf("computeIdealCacheParam(v=%u)\n",v);
10860
10861   int r=0;
10862   while (v!=0) v>>=1,r++;
10863   // r = log2(v)
10864
10865   // convert to a valid cache size value
10866   return std::max(0,std::min(r-16,9));
10867 }
10868
10869 void readConfiguration(int argc, char **argv)
10870 {
10871   QCString versionString = getFullVersion();
10872
10873   // helper that calls \a func to write to file \a fileName via a TextStream
10874   auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
10875   {
10876     std::ofstream f;
10877     if (openOutputFile(fileName,f))
10878     {
10879       TextStream t(&f);
10880       func(t);
10881       return true;
10882     }
10883     return false;
10884   };
10885
10886
10887   /**************************************************************************
10888    *             Handle arguments                                           *
10889    **************************************************************************/
10890
10891   int optInd=1;
10892   QCString configName;
10893   QCString layoutName;
10894   QCString debugLabel;
10895   QCString formatName;
10896   QCString listName;
10897   QCString traceName;
10898   bool genConfig=FALSE;
10899   bool shortList=FALSE;
10900   Config::CompareMode diffList=Config::CompareMode::Full;
10901   bool updateConfig=FALSE;
10902   int retVal;
10903   bool quiet = false;
10904   while (optInd<argc && argv[optInd][0]=='-' &&
10905                (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
10906                 argv[optInd][1]=='-')
10907         )
10908   {
10909     switch(argv[optInd][1])
10910     {
10911       case 'g':
10912         genConfig=TRUE;
10913         break;
10914       case 'l':
10915         if (optInd+1>=argc)
10916         {
10917           layoutName="DoxygenLayout.xml";
10918         }
10919         else
10920         {
10921           layoutName=argv[optInd+1];
10922         }
10923         writeDefaultLayoutFile(layoutName);
10924         cleanUpDoxygen();
10925         exit(0);
10926         break;
10927       case 'd':
10928         debugLabel=getArg(argc,argv,optInd);
10929         if (debugLabel.isEmpty())
10930         {
10931           devUsage();
10932           cleanUpDoxygen();
10933           exit(0);
10934         }
10935         retVal = Debug::setFlagStr(debugLabel);
10936         if (!retVal)
10937         {
10938           err("option \"-d\" has unknown debug specifier: \"%s\".\n",qPrint(debugLabel));
10939           devUsage();
10940           cleanUpDoxygen();
10941           exit(1);
10942         }
10943         break;
10944       case 't':
10945         {
10946 #if ENABLE_TRACING
10947           if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
10948           {
10949             traceName="stdout";
10950           }
10951           else
10952           {
10953             traceName=argv[optInd+1];
10954             optInd++;
10955           }
10956 #else
10957           err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
10958           cleanUpDoxygen();
10959           exit(1);
10960 #endif
10961         }
10962         break;
10963       case 'x':
10964         if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
10965         else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
10966         else
10967         {
10968           err("option should be \"-x\" or \"-x_noenv\", found: \"%s\".\n",argv[optInd]);
10969           cleanUpDoxygen();
10970           exit(1);
10971         }
10972         break;
10973       case 's':
10974         shortList=TRUE;
10975         break;
10976       case 'u':
10977         updateConfig=TRUE;
10978         break;
10979       case 'e':
10980         formatName=getArg(argc,argv,optInd);
10981         if (formatName.isEmpty())
10982         {
10983           err("option \"-e\" is missing format specifier rtf.\n");
10984           cleanUpDoxygen();
10985           exit(1);
10986         }
10987         if (qstricmp(formatName.data(),"rtf")==0)
10988         {
10989           if (optInd+1>=argc)
10990           {
10991             err("option \"-e rtf\" is missing an extensions file name\n");
10992             cleanUpDoxygen();
10993             exit(1);
10994           }
10995           writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
10996           cleanUpDoxygen();
10997           exit(0);
10998         }
10999         err("option \"-e\" has invalid format specifier.\n");
11000         cleanUpDoxygen();
11001         exit(1);
11002         break;
11003       case 'f':
11004         listName=getArg(argc,argv,optInd);
11005         if (listName.isEmpty())
11006         {
11007           err("option \"-f\" is missing list specifier.\n");
11008           cleanUpDoxygen();
11009           exit(1);
11010         }
11011         if (qstricmp(listName.data(),"emoji")==0)
11012         {
11013           if (optInd+1>=argc)
11014           {
11015             err("option \"-f emoji\" is missing an output file name\n");
11016             cleanUpDoxygen();
11017             exit(1);
11018           }
11019           writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11020           cleanUpDoxygen();
11021           exit(0);
11022         }
11023         err("option \"-f\" has invalid list specifier.\n");
11024         cleanUpDoxygen();
11025         exit(1);
11026         break;
11027       case 'w':
11028         formatName=getArg(argc,argv,optInd);
11029         if (formatName.isEmpty())
11030         {
11031           err("option \"-w\" is missing format specifier rtf, html or latex\n");
11032           cleanUpDoxygen();
11033           exit(1);
11034         }
11035         if (qstricmp(formatName.data(),"rtf")==0)
11036         {
11037           if (optInd+1>=argc)
11038           {
11039             err("option \"-w rtf\" is missing a style sheet file name\n");
11040             cleanUpDoxygen();
11041             exit(1);
11042           }
11043           if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11044           {
11045             err("error opening RTF style sheet file %s!\n",argv[optInd+1]);
11046             cleanUpDoxygen();
11047             exit(1);
11048           }
11049           cleanUpDoxygen();
11050           exit(0);
11051         }
11052         else if (qstricmp(formatName.data(),"html")==0)
11053         {
11054           Config::init();
11055           if (optInd+4<argc || FileInfo("Doxyfile").exists())
11056              // explicit config file mentioned or default found on disk
11057           {
11058             QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
11059             if (!Config::parse(df)) // parse the config file
11060             {
11061               err("error opening or reading configuration file %s!\n",argv[optInd+4]);
11062               cleanUpDoxygen();
11063               exit(1);
11064             }
11065           }
11066           if (optInd+3>=argc)
11067           {
11068             err("option \"-w html\" does not have enough arguments\n");
11069             cleanUpDoxygen();
11070             exit(1);
11071           }
11072           Config::postProcess(TRUE);
11073           Config::updateObsolete();
11074           Config::checkAndCorrect(Config_getBool(QUIET), false);
11075           setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11076           writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11077           writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11078           writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11079           cleanUpDoxygen();
11080           exit(0);
11081         }
11082         else if (qstricmp(formatName.data(),"latex")==0)
11083         {
11084           Config::init();
11085           if (optInd+4<argc || FileInfo("Doxyfile").exists())
11086           {
11087             QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
11088             if (!Config::parse(df))
11089             {
11090               err("error opening or reading configuration file %s!\n",argv[optInd+4]);
11091               cleanUpDoxygen();
11092               exit(1);
11093             }
11094           }
11095           if (optInd+3>=argc)
11096           {
11097             err("option \"-w latex\" does not have enough arguments\n");
11098             cleanUpDoxygen();
11099             exit(1);
11100           }
11101           Config::postProcess(TRUE);
11102           Config::updateObsolete();
11103           Config::checkAndCorrect(Config_getBool(QUIET), false);
11104           setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11105           writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11106           writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11107           writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11108           cleanUpDoxygen();
11109           exit(0);
11110         }
11111         else
11112         {
11113           err("Illegal format specifier \"%s\": should be one of rtf, html or latex\n",qPrint(formatName));
11114           cleanUpDoxygen();
11115           exit(1);
11116         }
11117         break;
11118       case 'm':
11119         g_dumpSymbolMap = TRUE;
11120         break;
11121       case 'v':
11122         version(false);
11123         cleanUpDoxygen();
11124         exit(0);
11125         break;
11126       case 'V':
11127         version(true);
11128         cleanUpDoxygen();
11129         exit(0);
11130         break;
11131       case '-':
11132         if (qstrcmp(&argv[optInd][2],"help")==0)
11133         {
11134           usage(argv[0],versionString);
11135           exit(0);
11136         }
11137         else if (qstrcmp(&argv[optInd][2],"version")==0)
11138         {
11139           version(false);
11140           cleanUpDoxygen();
11141           exit(0);
11142         }
11143         else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11144                  (qstrcmp(&argv[optInd][2],"VERSION")==0))
11145         {
11146           version(true);
11147           cleanUpDoxygen();
11148           exit(0);
11149         }
11150         else
11151         {
11152           err("Unknown option \"-%s\"\n",&argv[optInd][1]);
11153           usage(argv[0],versionString);
11154           exit(1);
11155         }
11156         break;
11157       case 'b':
11158         setvbuf(stdout,NULL,_IONBF,0);
11159         break;
11160       case 'q':
11161         quiet = true;
11162         break;
11163       case 'h':
11164       case '?':
11165         usage(argv[0],versionString);
11166         exit(0);
11167         break;
11168       default:
11169         err("Unknown option \"-%c\"\n",argv[optInd][1]);
11170         usage(argv[0],versionString);
11171         exit(1);
11172     }
11173     optInd++;
11174   }
11175
11176   /**************************************************************************
11177    *            Parse or generate the config file                           *
11178    **************************************************************************/
11179
11180   initTracing(traceName.data());
11181   TRACE("Doxygen version used: {}",getFullVersion());
11182   Config::init();
11183
11184   FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11185   if (optInd>=argc)
11186   {
11187     if (configFileInfo1.exists())
11188     {
11189       configName="Doxyfile";
11190     }
11191     else if (configFileInfo2.exists())
11192     {
11193       configName="doxyfile";
11194     }
11195     else if (genConfig)
11196     {
11197       configName="Doxyfile";
11198     }
11199     else
11200     {
11201       err("Doxyfile not found and no input file specified!\n");
11202       usage(argv[0],versionString);
11203       exit(1);
11204     }
11205   }
11206   else
11207   {
11208     FileInfo fi(argv[optInd]);
11209     if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11210     {
11211       configName=argv[optInd];
11212     }
11213     else
11214     {
11215       err("configuration file %s not found!\n",argv[optInd]);
11216       usage(argv[0],versionString);
11217       exit(1);
11218     }
11219   }
11220
11221   if (genConfig)
11222   {
11223     generateConfigFile(configName,shortList);
11224     cleanUpDoxygen();
11225     exit(0);
11226   }
11227
11228   if (!Config::parse(configName,updateConfig,diffList))
11229   {
11230     err("could not open or read configuration file %s!\n",qPrint(configName));
11231     cleanUpDoxygen();
11232     exit(1);
11233   }
11234
11235   if (diffList!=Config::CompareMode::Full)
11236   {
11237     Config::updateObsolete();
11238     compareDoxyfile(diffList);
11239     cleanUpDoxygen();
11240     exit(0);
11241   }
11242
11243   if (updateConfig)
11244   {
11245     Config::updateObsolete();
11246     generateConfigFile(configName,shortList,TRUE);
11247     cleanUpDoxygen();
11248     exit(0);
11249   }
11250
11251   /* Perlmod wants to know the path to the config file.*/
11252   FileInfo configFileInfo(configName.str());
11253   setPerlModDoxyfile(configFileInfo.absFilePath());
11254
11255   /* handle -q option */
11256   if (quiet) Config_updateBool(QUIET,TRUE);
11257 }
11258
11259 /** check and resolve config options */
11260 void checkConfiguration()
11261 {
11262   AUTO_TRACE();
11263
11264   Config::postProcess(FALSE);
11265   Config::updateObsolete();
11266   Config::checkAndCorrect(Config_getBool(QUIET), true);
11267   initWarningFormat();
11268 }
11269
11270 /** adjust globals that depend on configuration settings. */
11271 void adjustConfiguration()
11272 {
11273   AUTO_TRACE();
11274   Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11275   Doxygen::globalScope = toNamespaceDefMutable(Doxygen::globalNamespaceDef.get());
11276   Doxygen::inputNameLinkedMap = new FileNameLinkedMap;
11277   Doxygen::includeNameLinkedMap = new FileNameLinkedMap;
11278   Doxygen::exampleNameLinkedMap = new FileNameLinkedMap;
11279   Doxygen::imageNameLinkedMap = new FileNameLinkedMap;
11280   Doxygen::dotFileNameLinkedMap = new FileNameLinkedMap;
11281   Doxygen::mscFileNameLinkedMap = new FileNameLinkedMap;
11282   Doxygen::diaFileNameLinkedMap = new FileNameLinkedMap;
11283
11284   setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11285
11286   /* Set the global html file extension. */
11287   Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11288
11289
11290   Doxygen::parseSourcesNeeded = Config_getBool(CALL_GRAPH) ||
11291                                 Config_getBool(CALLER_GRAPH) ||
11292                                 Config_getBool(REFERENCES_RELATION) ||
11293                                 Config_getBool(REFERENCED_BY_RELATION);
11294
11295   /**************************************************************************
11296    *            Add custom extension mappings
11297    **************************************************************************/
11298
11299   const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11300   for (const auto &mapping : extMaps)
11301   {
11302     QCString mapStr = mapping.c_str();
11303     int i=mapStr.find('=');
11304     if (i==-1)
11305     {
11306       continue;
11307     }
11308     else
11309     {
11310       QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11311       QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11312       if (ext.isEmpty() || language.isEmpty())
11313       {
11314         continue;
11315       }
11316
11317       if (!updateLanguageMapping(ext,language))
11318       {
11319         err("Failed to map file extension '%s' to unsupported language '%s'.\n"
11320             "Check the EXTENSION_MAPPING setting in the config file.\n",
11321             qPrint(ext),qPrint(language));
11322       }
11323       else
11324       {
11325         msg("Adding custom extension mapping: '%s' will be treated as language '%s'\n",
11326             qPrint(ext),qPrint(language));
11327       }
11328     }
11329   }
11330   // create input file exncodings
11331
11332   // check INPUT_ENCODING
11333   void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
11334   if (cd==reinterpret_cast<void *>(-1))
11335   {
11336     term("unsupported character conversion: '%s'->'%s': %s\n"
11337         "Check the 'INPUT_ENCODING' setting in the config file!\n",
11338         qPrint(Config_getString(INPUT_ENCODING)),qPrint("UTF-8"),strerror(errno));
11339   }
11340   else
11341   {
11342     portable_iconv_close(cd);
11343   }
11344
11345   // check and split INPUT_FILE_ENCODING
11346   const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
11347   for (const auto &mapping : fileEncod)
11348   {
11349     QCString mapStr = mapping.c_str();
11350     int i=mapStr.find('=');
11351     if (i==-1)
11352     {
11353       continue;
11354     }
11355     else
11356     {
11357       QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
11358       QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
11359       if (pattern.isEmpty() || encoding.isEmpty())
11360       {
11361         continue;
11362       }
11363       cd = portable_iconv_open("UTF-8",encoding.data());
11364       if (cd==reinterpret_cast<void *>(-1))
11365       {
11366         term("unsupported character conversion: '%s'->'%s': %s\n"
11367             "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
11368             qPrint(encoding),qPrint("UTF-8"),strerror(errno));
11369       }
11370       else
11371       {
11372         portable_iconv_close(cd);
11373       }
11374
11375       Doxygen::inputFileEncodingList.push_back(InputFileEncoding(pattern, encoding));
11376     }
11377   }
11378
11379   // add predefined macro name to a dictionary
11380   const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11381   for (const auto &s : expandAsDefinedList)
11382   {
11383     Doxygen::expandAsDefinedSet.insert(s.c_str());
11384   }
11385
11386   // read aliases and store them in a dictionary
11387   readAliases();
11388
11389   // store number of spaces in a tab into Doxygen::spaces
11390   int tabSize = Config_getInt(TAB_SIZE);
11391   Doxygen::spaces.resize(tabSize+1);
11392   int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' ';
11393   Doxygen::spaces.at(tabSize)='\0';
11394 }
11395
11396 #ifdef HAS_SIGNALS
11397 static void stopDoxygen(int)
11398 {
11399   signal(SIGINT,SIG_DFL);   // Re-register signal handler for default action
11400   Dir thisDir;
11401   msg("Cleaning up...\n");
11402   if (!Doxygen::filterDBFileName.isEmpty())
11403   {
11404     thisDir.remove(Doxygen::filterDBFileName.str());
11405   }
11406   killpg(0,SIGINT);
11407   cleanUpDoxygen();
11408   Doxygen::terminating=true;
11409   exitTracing();
11410   exit(1);
11411 }
11412 #endif
11413
11414 static void writeTagFile()
11415 {
11416   QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
11417   if (generateTagFile.isEmpty()) return;
11418
11419   std::ofstream f = Portable::openOutputStream(generateTagFile);
11420   if (!f.is_open())
11421   {
11422     err("cannot open tag file %s for writing\n",
11423         qPrint(generateTagFile)
11424        );
11425     return;
11426   }
11427   TextStream tagFile(&f);
11428   tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
11429   tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
11430   if (strlen(getGitVersion())>0)
11431   {
11432     tagFile << " doxygen_gitid=\"" << getGitVersion() << "\"";
11433   }
11434   tagFile << ">\n";
11435
11436   // for each file
11437   for (const auto &fn : *Doxygen::inputNameLinkedMap)
11438   {
11439     for (const auto &fd : *fn)
11440     {
11441       if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
11442     }
11443   }
11444   // for each class
11445   for (const auto &cd : *Doxygen::classLinkedMap)
11446   {
11447     ClassDefMutable *cdm = toClassDefMutable(cd.get());
11448     if (cdm && cdm->isLinkableInProject())
11449     {
11450       cdm->writeTagFile(tagFile);
11451     }
11452   }
11453   // for each concept
11454   for (const auto &cd : *Doxygen::conceptLinkedMap)
11455   {
11456     ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
11457     if (cdm && cdm->isLinkableInProject())
11458     {
11459       cdm->writeTagFile(tagFile);
11460     }
11461   }
11462   // for each namespace
11463   for (const auto &nd : *Doxygen::namespaceLinkedMap)
11464   {
11465     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
11466     if (ndm && nd->isLinkableInProject())
11467     {
11468       ndm->writeTagFile(tagFile);
11469     }
11470   }
11471   // for each group
11472   for (const auto &gd : *Doxygen::groupLinkedMap)
11473   {
11474     if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
11475   }
11476   // for each module
11477   for (const auto &mod : ModuleManager::instance().modules())
11478   {
11479     if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
11480   }
11481   // for each page
11482   for (const auto &pd : *Doxygen::pageLinkedMap)
11483   {
11484     if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
11485   }
11486   if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
11487
11488   tagFile << "</tagfile>\n";
11489 }
11490
11491 static void exitDoxygen() noexcept
11492 {
11493   if (!g_successfulRun)  // premature exit
11494   {
11495     Dir thisDir;
11496     msg("Exiting...\n");
11497     if (!Doxygen::filterDBFileName.isEmpty())
11498     {
11499       thisDir.remove(Doxygen::filterDBFileName.str());
11500     }
11501   }
11502 }
11503
11504 static QCString createOutputDirectory(const QCString &baseDirName,
11505                                   const QCString &formatDirName,
11506                                   const char *defaultDirName)
11507 {
11508   QCString result = formatDirName;
11509   if (result.isEmpty())
11510   {
11511     result = baseDirName + defaultDirName;
11512   }
11513   else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
11514   {
11515     result.prepend(baseDirName+"/");
11516   }
11517   Dir formatDir(result.str());
11518   if (!formatDir.exists() && !formatDir.mkdir(result.str()))
11519   {
11520     err("Could not create output directory %s\n", qPrint(result));
11521     cleanUpDoxygen();
11522     exit(1);
11523   }
11524   return result;
11525 }
11526
11527 void searchInputFiles()
11528 {
11529   StringUnorderedSet killSet;
11530
11531   const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11532   bool alwaysRecursive = Config_getBool(RECURSIVE);
11533   StringUnorderedSet excludeNameSet;
11534
11535   // gather names of all files in the include path
11536   g_s.begin("Searching for include files...\n");
11537   killSet.clear();
11538   const StringVector &includePathList = Config_getList(INCLUDE_PATH);
11539   for (const auto &s : includePathList)
11540   {
11541     size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
11542     const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
11543                                          Config_getList(INCLUDE_FILE_PATTERNS);
11544     readFileOrDirectory(s.c_str(),                     // s
11545                         Doxygen::includeNameLinkedMap, // fnDict
11546                         0,                             // exclSet
11547                         &pl,                           // patList
11548                         &exclPatterns,                 // exclPatList
11549                         0,                             // resultList
11550                         0,                             // resultSet
11551                         false,                         // INCLUDE_PATH isn't recursive
11552                         TRUE,                          // errorIfNotExist
11553                         &killSet);                     // killSet
11554   }
11555   g_s.end();
11556
11557   g_s.begin("Searching for example files...\n");
11558   killSet.clear();
11559   const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
11560   for (const auto &s : examplePathList)
11561   {
11562     readFileOrDirectory(s.c_str(),                                              // s
11563                         Doxygen::exampleNameLinkedMap,                          // fnDict
11564                         0,                                                      // exclSet
11565                         &Config_getList(EXAMPLE_PATTERNS),                      // patList
11566                         0,                                                      // exclPatList
11567                         0,                                                      // resultList
11568                         0,                                                      // resultSet
11569                         (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
11570                         TRUE,                                                   // errorIfNotExist
11571                         &killSet);                                              // killSet
11572   }
11573   g_s.end();
11574
11575   g_s.begin("Searching for images...\n");
11576   killSet.clear();
11577   const StringVector &imagePathList=Config_getList(IMAGE_PATH);
11578   for (const auto &s : imagePathList)
11579   {
11580     readFileOrDirectory(s.c_str(),                        // s
11581                         Doxygen::imageNameLinkedMap,      // fnDict
11582                         0,                                // exclSet
11583                         0,                                // patList
11584                         0,                                // exclPatList
11585                         0,                                // resultList
11586                         0,                                // resultSet
11587                         alwaysRecursive,                  // recursive
11588                         TRUE,                             // errorIfNotExist
11589                         &killSet);                        // killSet
11590   }
11591   g_s.end();
11592
11593   g_s.begin("Searching for dot files...\n");
11594   killSet.clear();
11595   const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
11596   for (const auto &s : dotFileList)
11597   {
11598     readFileOrDirectory(s.c_str(),                      // s
11599                         Doxygen::dotFileNameLinkedMap,  // fnDict
11600                         0,                              // exclSet
11601                         0,                              // patList
11602                         0,                              // exclPatList
11603                         0,                              // resultList
11604                         0,                              // resultSet
11605                         alwaysRecursive,                // recursive
11606                         TRUE,                           // errorIfNotExist
11607                         &killSet);                      // killSet
11608   }
11609   g_s.end();
11610
11611   g_s.begin("Searching for msc files...\n");
11612   killSet.clear();
11613   const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
11614   for (const auto &s : mscFileList)
11615   {
11616     readFileOrDirectory(s.c_str(),                       // s
11617                         Doxygen::mscFileNameLinkedMap,   // fnDict
11618                         0,                               // exclSet
11619                         0,                               // patList
11620                         0,                               // exclPatList
11621                         0,                               // resultList
11622                         0,                               // resultSet
11623                         alwaysRecursive,                 // recursive
11624                         TRUE,                            // errorIfNotExist
11625                         &killSet);                       // killSet
11626   }
11627   g_s.end();
11628
11629   g_s.begin("Searching for dia files...\n");
11630   killSet.clear();
11631   const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
11632   for (const auto &s : diaFileList)
11633   {
11634     readFileOrDirectory(s.c_str(),                         // s
11635                         Doxygen::diaFileNameLinkedMap,     // fnDict
11636                         0,                                 // exclSet
11637                         0,                                 // patList
11638                         0,                                 // exclPatList
11639                         0,                                 // resultList
11640                         0,                                 // resultSet
11641                         alwaysRecursive,                   // recursive
11642                         TRUE,                              // errorIfNotExist
11643                         &killSet);                         // killSet
11644   }
11645   g_s.end();
11646
11647   g_s.begin("Searching for files to exclude\n");
11648   const StringVector &excludeList = Config_getList(EXCLUDE);
11649   for (const auto &s : excludeList)
11650   {
11651     readFileOrDirectory(s.c_str(),                          // s
11652                         0,                                  // fnDict
11653                         0,                                  // exclSet
11654                         &Config_getList(FILE_PATTERNS),     // patList
11655                         0,                                  // exclPatList
11656                         0,                                  // resultList
11657                         &excludeNameSet,                    // resultSet
11658                         alwaysRecursive,                    // recursive
11659                         FALSE);                             // errorIfNotExist
11660   }
11661   g_s.end();
11662
11663   /**************************************************************************
11664    *             Determine Input Files                                      *
11665    **************************************************************************/
11666
11667   g_s.begin("Searching INPUT for files to process...\n");
11668   killSet.clear();
11669   Doxygen::inputPaths.clear();
11670   const StringVector &inputList=Config_getList(INPUT);
11671   for (const auto &s : inputList)
11672   {
11673     QCString path=s.c_str();
11674     uint32_t l = path.length();
11675     if (l>0)
11676     {
11677       // strip trailing slashes
11678       if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
11679
11680       readFileOrDirectory(
11681           path,                               // s
11682           Doxygen::inputNameLinkedMap,        // fnDict
11683           &excludeNameSet,                    // exclSet
11684           &Config_getList(FILE_PATTERNS),     // patList
11685           &exclPatterns,                      // exclPatList
11686           &g_inputFiles,                      // resultList
11687           0,                                  // resultSet
11688           alwaysRecursive,                    // recursive
11689           TRUE,                               // errorIfNotExist
11690           &killSet,                           // killSet
11691           &Doxygen::inputPaths);              // paths
11692     }
11693   }
11694
11695   // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
11696   std::sort(Doxygen::inputNameLinkedMap->begin(),
11697             Doxygen::inputNameLinkedMap->end(),
11698             [](const auto &f1,const auto &f2)
11699             {
11700               return  qstricmp(f1->fullName(),f2->fullName())<0;
11701             });
11702   for (auto &fileName : *Doxygen::inputNameLinkedMap)
11703   {
11704     if (fileName->size()>1)
11705     {
11706       std::sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
11707         {
11708           return qstricmp(f1->absFilePath(),f2->absFilePath())<0;
11709         });
11710     }
11711   }
11712   if (Doxygen::inputNameLinkedMap->empty())
11713   {
11714     warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
11715   }
11716   g_s.end();
11717 }
11718
11719
11720 static void checkMarkdownMainfile()
11721 {
11722   if (Config_getBool(MARKDOWN_SUPPORT))
11723   {
11724     QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
11725     if (mdfileAsMainPage.isEmpty()) return;
11726     FileInfo fi(mdfileAsMainPage.data());
11727     if (!fi.exists())
11728     {
11729       warn_uncond("Specified markdown mainpage '%s' does not exist\n",qPrint(mdfileAsMainPage));
11730       return;
11731     }
11732     bool ambig = false;
11733     if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==0)
11734     {
11735       warn_uncond("Specified markdown mainpage '%s' has not been defined as input file\n",qPrint(mdfileAsMainPage));
11736       return;
11737     }
11738   }
11739 }
11740
11741 void parseInput()
11742 {
11743   AUTO_TRACE();
11744   std::atexit(exitDoxygen);
11745
11746 #if USE_LIBCLANG
11747   Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
11748 #endif
11749
11750   // we would like to show the versionString earlier, but we first have to handle the configuration file
11751   // to know the value of the QUIET setting.
11752   QCString versionString = getFullVersion();
11753   msg("Doxygen version used: %s\n",qPrint(versionString));
11754
11755   computeVerifiedDotPath();
11756
11757   /**************************************************************************
11758    *            Make sure the output directory exists
11759    **************************************************************************/
11760   QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
11761   if (outputDirectory.isEmpty())
11762   {
11763     outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath().c_str());
11764   }
11765   else
11766   {
11767     Dir dir(outputDirectory.str());
11768     if (!dir.exists())
11769     {
11770       dir.setPath(Dir::currentDirPath());
11771       if (!dir.mkdir(outputDirectory.str()))
11772       {
11773         err("tag OUTPUT_DIRECTORY: Output directory '%s' does not "
11774             "exist and cannot be created\n",qPrint(outputDirectory));
11775         cleanUpDoxygen();
11776         exit(1);
11777       }
11778       else
11779       {
11780         msg("Notice: Output directory '%s' does not exist. "
11781             "I have created it for you.\n", qPrint(outputDirectory));
11782       }
11783       dir.setPath(outputDirectory.str());
11784     }
11785     outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath().c_str());
11786   }
11787   AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
11788
11789   /**************************************************************************
11790    *            Initialize global lists and dictionaries
11791    **************************************************************************/
11792
11793   // also scale lookup cache with SYMBOL_CACHE_SIZE
11794   int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
11795   if (cacheSize<0) cacheSize=0;
11796   if (cacheSize>9) cacheSize=9;
11797   uint32_t lookupSize = 65536 << cacheSize;
11798   Doxygen::typeLookupCache = new Cache<std::string,LookupInfo>(lookupSize);
11799   Doxygen::symbolLookupCache = new Cache<std::string,LookupInfo>(lookupSize);
11800
11801 #ifdef HAS_SIGNALS
11802   signal(SIGINT, stopDoxygen);
11803 #endif
11804
11805   uint32_t pid = Portable::pid();
11806   Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
11807   Doxygen::filterDBFileName.prepend(outputDirectory+"/");
11808
11809   /**************************************************************************
11810    *            Check/create output directories                             *
11811    **************************************************************************/
11812
11813   QCString htmlOutput;
11814   bool generateHtml = Config_getBool(GENERATE_HTML);
11815   if (generateHtml)
11816   {
11817     htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
11818     Config_updateString(HTML_OUTPUT,htmlOutput);
11819
11820     QCString sitemapUrl = Config_getString(SITEMAP_URL);
11821     bool generateSitemap = !sitemapUrl.isEmpty();
11822     if (generateSitemap && !sitemapUrl.endsWith("/"))
11823     {
11824       Config_updateString(SITEMAP_URL,sitemapUrl+"/");
11825     }
11826
11827     // add HTML indexers that are enabled
11828     bool generateHtmlHelp    = Config_getBool(GENERATE_HTMLHELP);
11829     bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
11830     bool generateQhp         = Config_getBool(GENERATE_QHP);
11831     bool generateTreeView    = Config_getBool(GENERATE_TREEVIEW);
11832     bool generateDocSet      = Config_getBool(GENERATE_DOCSET);
11833     if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
11834     if (generateHtmlHelp)    Doxygen::indexList->addIndex<HtmlHelp>();
11835     if (generateQhp)         Doxygen::indexList->addIndex<Qhp>();
11836     if (generateSitemap)     Doxygen::indexList->addIndex<Sitemap>();
11837     if (generateTreeView)    Doxygen::indexList->addIndex<FTVHelp>(TRUE);
11838     if (generateDocSet)      Doxygen::indexList->addIndex<DocSets>();
11839     Doxygen::indexList->initialize();
11840   }
11841
11842   QCString docbookOutput;
11843   bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
11844   if (generateDocbook)
11845   {
11846     docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
11847     Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
11848   }
11849
11850   QCString xmlOutput;
11851   bool generateXml = Config_getBool(GENERATE_XML);
11852   if (generateXml)
11853   {
11854     xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
11855     Config_updateString(XML_OUTPUT,xmlOutput);
11856   }
11857
11858   QCString latexOutput;
11859   bool generateLatex = Config_getBool(GENERATE_LATEX);
11860   if (generateLatex)
11861   {
11862     latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
11863     Config_updateString(LATEX_OUTPUT,latexOutput);
11864   }
11865
11866   QCString rtfOutput;
11867   bool generateRtf = Config_getBool(GENERATE_RTF);
11868   if (generateRtf)
11869   {
11870     rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
11871     Config_updateString(RTF_OUTPUT,rtfOutput);
11872   }
11873
11874   QCString manOutput;
11875   bool generateMan = Config_getBool(GENERATE_MAN);
11876   if (generateMan)
11877   {
11878     manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
11879     Config_updateString(MAN_OUTPUT,manOutput);
11880   }
11881
11882   QCString sqlOutput;
11883   bool generateSql = Config_getBool(GENERATE_SQLITE3);
11884   if (generateSql)
11885   {
11886     sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
11887     Config_updateString(SQLITE3_OUTPUT,sqlOutput);
11888   }
11889
11890   if (Config_getBool(HAVE_DOT))
11891   {
11892     QCString curFontPath = Config_getString(DOT_FONTPATH);
11893     if (curFontPath.isEmpty())
11894     {
11895       Portable::getenv("DOTFONTPATH");
11896       QCString newFontPath = ".";
11897       if (!curFontPath.isEmpty())
11898       {
11899         newFontPath+=Portable::pathListSeparator();
11900         newFontPath+=curFontPath;
11901       }
11902       Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
11903     }
11904     else
11905     {
11906       Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
11907     }
11908   }
11909
11910
11911
11912   /**************************************************************************
11913    *             Handle layout file                                         *
11914    **************************************************************************/
11915
11916   LayoutDocManager::instance().init();
11917   QCString layoutFileName = Config_getString(LAYOUT_FILE);
11918   bool defaultLayoutUsed = FALSE;
11919   if (layoutFileName.isEmpty())
11920   {
11921     layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
11922     defaultLayoutUsed = TRUE;
11923   }
11924   AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
11925
11926   FileInfo fi(layoutFileName.str());
11927   if (fi.exists())
11928   {
11929     msg("Parsing layout file %s...\n",qPrint(layoutFileName));
11930     LayoutDocManager::instance().parse(layoutFileName);
11931   }
11932   else if (!defaultLayoutUsed)
11933   {
11934       warn_uncond("failed to open layout file '%s' for reading! Using default settings.\n",qPrint(layoutFileName));
11935   }
11936
11937   /**************************************************************************
11938    *             Read and preprocess input                                  *
11939    **************************************************************************/
11940
11941   // prevent search in the output directories
11942   StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11943   if (generateHtml)    exclPatterns.push_back(htmlOutput.str());
11944   if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
11945   if (generateXml)     exclPatterns.push_back(xmlOutput.str());
11946   if (generateLatex)   exclPatterns.push_back(latexOutput.str());
11947   if (generateRtf)     exclPatterns.push_back(rtfOutput.str());
11948   if (generateMan)     exclPatterns.push_back(manOutput.str());
11949   Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
11950
11951   searchInputFiles();
11952
11953   checkMarkdownMainfile();
11954
11955   // Notice: the order of the function calls below is very important!
11956
11957   if (Config_getBool(GENERATE_HTML) && !Config_getBool(USE_MATHJAX))
11958   {
11959     FormulaManager::instance().initFromRepository(Config_getString(HTML_OUTPUT));
11960   }
11961   if (Config_getBool(GENERATE_RTF))
11962   {
11963     FormulaManager::instance().initFromRepository(Config_getString(RTF_OUTPUT));
11964   }
11965   if (Config_getBool(GENERATE_DOCBOOK))
11966   {
11967     FormulaManager::instance().initFromRepository(Config_getString(DOCBOOK_OUTPUT));
11968   }
11969
11970   FormulaManager::instance().checkRepositories();
11971
11972   /**************************************************************************
11973    *             Handle Tag Files                                           *
11974    **************************************************************************/
11975
11976   std::shared_ptr<Entry> root = std::make_shared<Entry>();
11977   msg("Reading and parsing tag files\n");
11978
11979   const StringVector &tagFileList = Config_getList(TAGFILES);
11980   for (const auto &s : tagFileList)
11981   {
11982     readTagFile(root,s.c_str());
11983   }
11984
11985   /**************************************************************************
11986    *             Parse source files                                         *
11987    **************************************************************************/
11988
11989   addSTLSupport(root);
11990
11991   g_s.begin("Parsing files\n");
11992   if (Config_getInt(NUM_PROC_THREADS)==1)
11993   {
11994     parseFilesSingleThreading(root);
11995   }
11996   else
11997   {
11998     parseFilesMultiThreading(root);
11999   }
12000   g_s.end();
12001
12002   /**************************************************************************
12003    *             Gather information                                         *
12004    **************************************************************************/
12005
12006   g_s.begin("Building macro definition list...\n");
12007   buildDefineList();
12008   g_s.end();
12009
12010   g_s.begin("Building group list...\n");
12011   buildGroupList(root.get());
12012   organizeSubGroups(root.get());
12013   g_s.end();
12014
12015   g_s.begin("Building directory list...\n");
12016   buildDirectories();
12017   findDirDocumentation(root.get());
12018   g_s.end();
12019
12020   g_s.begin("Building namespace list...\n");
12021   buildNamespaceList(root.get());
12022   findUsingDirectives(root.get());
12023   g_s.end();
12024
12025   g_s.begin("Building file list...\n");
12026   buildFileList(root.get());
12027   g_s.end();
12028
12029   g_s.begin("Building class list...\n");
12030   buildClassList(root.get());
12031   g_s.end();
12032
12033   g_s.begin("Building concept list...\n");
12034   buildConceptList(root.get());
12035   g_s.end();
12036
12037   // build list of using declarations here (global list)
12038   buildListOfUsingDecls(root.get());
12039   g_s.end();
12040
12041   g_s.begin("Computing nesting relations for classes...\n");
12042   resolveClassNestingRelations();
12043   g_s.end();
12044   // 1.8.2-20121111: no longer add nested classes to the group as well
12045   //distributeClassGroupRelations();
12046
12047   // calling buildClassList may result in cached relations that
12048   // become invalid after resolveClassNestingRelations(), that's why
12049   // we need to clear the cache here
12050   Doxygen::typeLookupCache->clear();
12051   // we don't need the list of using declaration anymore
12052   g_usingDeclarations.clear();
12053
12054   g_s.begin("Associating documentation with classes...\n");
12055   buildClassDocList(root.get());
12056   g_s.end();
12057
12058   g_s.begin("Associating documentation with concepts...\n");
12059   buildConceptDocList(root.get());
12060   distributeConceptGroups();
12061   g_s.end();
12062
12063   g_s.begin("Associating documentation with modules...\n");
12064   findModuleDocumentation(root.get());
12065   g_s.end();
12066
12067   g_s.begin("Building example list...\n");
12068   buildExampleList(root.get());
12069   g_s.end();
12070
12071   g_s.begin("Searching for enumerations...\n");
12072   findEnums(root.get());
12073   g_s.end();
12074
12075   // Since buildVarList calls isVarWithConstructor
12076   // and this calls getResolvedClass we need to process
12077   // typedefs first so the relations between classes via typedefs
12078   // are properly resolved. See bug 536385 for an example.
12079   g_s.begin("Searching for documented typedefs...\n");
12080   buildTypedefList(root.get());
12081   g_s.end();
12082
12083   if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12084   {
12085     g_s.begin("Searching for documented sequences...\n");
12086     buildSequenceList(root.get());
12087     g_s.end();
12088
12089     g_s.begin("Searching for documented dictionaries...\n");
12090     buildDictionaryList(root.get());
12091     g_s.end();
12092   }
12093
12094   g_s.begin("Searching for members imported via using declarations...\n");
12095   // this should be after buildTypedefList in order to properly import
12096   // used typedefs
12097   findUsingDeclarations(root.get(),TRUE);  // do for python packages first
12098   findUsingDeclarations(root.get(),FALSE); // then the rest
12099   g_s.end();
12100
12101   g_s.begin("Searching for included using directives...\n");
12102   findIncludedUsingDirectives();
12103   g_s.end();
12104
12105   g_s.begin("Searching for documented variables...\n");
12106   buildVarList(root.get());
12107   g_s.end();
12108
12109   g_s.begin("Building interface member list...\n");
12110   buildInterfaceAndServiceList(root.get()); // UNO IDL
12111
12112   g_s.begin("Building member list...\n"); // using class info only !
12113   buildFunctionList(root.get());
12114   g_s.end();
12115
12116   g_s.begin("Searching for friends...\n");
12117   findFriends();
12118   g_s.end();
12119
12120   g_s.begin("Searching for documented defines...\n");
12121   findDefineDocumentation(root.get());
12122   g_s.end();
12123
12124   g_s.begin("Computing class inheritance relations...\n");
12125   findClassEntries(root.get());
12126   findInheritedTemplateInstances();
12127   g_s.end();
12128
12129   g_s.begin("Computing class usage relations...\n");
12130   findUsedTemplateInstances();
12131   g_s.end();
12132
12133   if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12134   {
12135     g_s.begin("Searching for tag less structs...\n");
12136     findTagLessClasses();
12137     g_s.end();
12138   }
12139
12140   g_s.begin("Flushing cached template relations that have become invalid...\n");
12141   flushCachedTemplateRelations();
12142   g_s.end();
12143
12144   g_s.begin("Computing class relations...\n");
12145   computeTemplateClassRelations();
12146   flushUnresolvedRelations();
12147   if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12148   {
12149     VhdlDocGen::computeVhdlComponentRelations();
12150   }
12151   computeClassRelations();
12152   g_classEntries.clear();
12153   g_s.end();
12154
12155   g_s.begin("Add enum values to enums...\n");
12156   addEnumValuesToEnums(root.get());
12157   findEnumDocumentation(root.get());
12158   g_s.end();
12159
12160   g_s.begin("Searching for member function documentation...\n");
12161   findObjCMethodDefinitions(root.get());
12162   findMemberDocumentation(root.get()); // may introduce new members !
12163   findUsingDeclImports(root.get()); // may introduce new members !
12164   transferRelatedFunctionDocumentation();
12165   transferFunctionDocumentation();
12166   transferStaticInstanceInitializers();
12167   g_s.end();
12168
12169   // moved to after finding and copying documentation,
12170   // as this introduces new members see bug 722654
12171   g_s.begin("Creating members for template instances...\n");
12172   createTemplateInstanceMembers();
12173   g_s.end();
12174
12175   g_s.begin("Building page list...\n");
12176   buildPageList(root.get());
12177   g_s.end();
12178
12179   g_s.begin("Search for main page...\n");
12180   findMainPage(root.get());
12181   findMainPageTagFiles(root.get());
12182   g_s.end();
12183
12184   g_s.begin("Computing page relations...\n");
12185   computePageRelations(root.get());
12186   checkPageRelations();
12187   g_s.end();
12188
12189   g_s.begin("Determining the scope of groups...\n");
12190   findGroupScope(root.get());
12191   g_s.end();
12192
12193   g_s.begin("Computing module relations...\n");
12194   auto &mm = ModuleManager::instance();
12195   mm.resolvePartitions();
12196   mm.resolveImports();
12197   mm.collectExportedSymbols();
12198   g_s.end();
12199
12200   auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12201   {
12202     return qstricmp(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12203                     n2->memberName().data()+getPrefixIndex(n2->memberName())
12204                    )<0;
12205   };
12206
12207   auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12208   {
12209     if (Config_getBool(SORT_BY_SCOPE_NAME))
12210     {
12211       return qstricmp(c1->name(), c2->name())<0;
12212     }
12213     else
12214     {
12215       int i = qstricmp(c1->className(), c2->className());
12216       return i==0 ? qstricmp(c1->name(), c2->name())<0 : i<0;
12217     }
12218   };
12219
12220   auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12221   {
12222     return qstricmp(n1->name(),n2->name())<0;
12223   };
12224
12225   auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12226   {
12227     return qstricmp(c1->name(),c2->name())<0;
12228   };
12229
12230   g_s.begin("Sorting lists...\n");
12231   std::sort(Doxygen::memberNameLinkedMap->begin(),
12232             Doxygen::memberNameLinkedMap->end(),
12233             memberNameComp);
12234   std::sort(Doxygen::functionNameLinkedMap->begin(),
12235             Doxygen::functionNameLinkedMap->end(),
12236             memberNameComp);
12237   std::sort(Doxygen::hiddenClassLinkedMap->begin(),
12238             Doxygen::hiddenClassLinkedMap->end(),
12239             classComp);
12240   std::sort(Doxygen::classLinkedMap->begin(),
12241             Doxygen::classLinkedMap->end(),
12242             classComp);
12243   std::sort(Doxygen::conceptLinkedMap->begin(),
12244             Doxygen::conceptLinkedMap->end(),
12245             conceptComp);
12246   std::sort(Doxygen::namespaceLinkedMap->begin(),
12247             Doxygen::namespaceLinkedMap->end(),
12248             namespaceComp);
12249   g_s.end();
12250
12251   g_s.begin("Determining which enums are documented\n");
12252   findDocumentedEnumValues();
12253   g_s.end();
12254
12255   g_s.begin("Computing member relations...\n");
12256   mergeCategories();
12257   computeMemberRelations();
12258   g_s.end();
12259
12260   g_s.begin("Building full member lists recursively...\n");
12261   buildCompleteMemberLists();
12262   g_s.end();
12263
12264   g_s.begin("Adding members to member groups.\n");
12265   addMembersToMemberGroup();
12266   g_s.end();
12267
12268   if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12269   {
12270     g_s.begin("Distributing member group documentation.\n");
12271     distributeMemberGroupDocumentation();
12272     g_s.end();
12273   }
12274
12275   g_s.begin("Computing member references...\n");
12276   computeMemberReferences();
12277   g_s.end();
12278
12279   if (Config_getBool(INHERIT_DOCS))
12280   {
12281     g_s.begin("Inheriting documentation...\n");
12282     inheritDocumentation();
12283     g_s.end();
12284   }
12285
12286
12287   // compute the shortest possible names of all files
12288   // without losing the uniqueness of the file names.
12289   g_s.begin("Generating disk names...\n");
12290   generateDiskNames();
12291   g_s.end();
12292
12293   g_s.begin("Adding source references...\n");
12294   addSourceReferences();
12295   g_s.end();
12296
12297   g_s.begin("Adding xrefitems...\n");
12298   addListReferences();
12299   generateXRefPages();
12300   g_s.end();
12301
12302   g_s.begin("Sorting member lists...\n");
12303   sortMemberLists();
12304   g_s.end();
12305
12306   g_s.begin("Setting anonymous enum type...\n");
12307   setAnonymousEnumType();
12308   g_s.end();
12309
12310   g_s.begin("Computing dependencies between directories...\n");
12311   computeDirDependencies();
12312   g_s.end();
12313
12314   g_s.begin("Generating citations page...\n");
12315   CitationManager::instance().generatePage();
12316   g_s.end();
12317
12318   g_s.begin("Counting members...\n");
12319   countMembers();
12320   g_s.end();
12321
12322   g_s.begin("Counting data structures...\n");
12323   Index::instance().countDataStructures();
12324   g_s.end();
12325
12326   g_s.begin("Resolving user defined references...\n");
12327   resolveUserReferences();
12328   g_s.end();
12329
12330   g_s.begin("Finding anchors and sections in the documentation...\n");
12331   findSectionsInDocumentation();
12332   g_s.end();
12333
12334   g_s.begin("Transferring function references...\n");
12335   transferFunctionReferences();
12336   g_s.end();
12337
12338   g_s.begin("Combining using relations...\n");
12339   combineUsingRelations();
12340   g_s.end();
12341
12342   initSearchIndexer();
12343   g_s.begin("Adding members to index pages...\n");
12344   addMembersToIndex();
12345   addToIndices();
12346   g_s.end();
12347
12348   g_s.begin("Correcting members for VHDL...\n");
12349   vhdlCorrectMemberProperties();
12350   g_s.end();
12351
12352   g_s.begin("Computing tooltip texts...\n");
12353   computeTooltipTexts();
12354   g_s.end();
12355
12356   if (Config_getBool(SORT_GROUP_NAMES))
12357   {
12358     std::sort(Doxygen::groupLinkedMap->begin(),
12359               Doxygen::groupLinkedMap->end(),
12360               [](const auto &g1,const auto &g2)
12361               { return g1->groupTitle() < g2->groupTitle(); });
12362
12363     for (const auto &gd : *Doxygen::groupLinkedMap)
12364     {
12365       gd->sortSubGroups();
12366     }
12367   }
12368
12369   printNavTree(root.get(),0);
12370 }
12371
12372 void generateOutput()
12373 {
12374   AUTO_TRACE();
12375   /**************************************************************************
12376    *            Initialize output generators                                *
12377    **************************************************************************/
12378
12379   /// add extra languages for which we can only produce syntax highlighted code
12380   addCodeOnlyMappings();
12381
12382   //// dump all symbols
12383   if (g_dumpSymbolMap)
12384   {
12385     dumpSymbolMap();
12386     exit(0);
12387   }
12388
12389   bool generateHtml  = Config_getBool(GENERATE_HTML);
12390   bool generateLatex = Config_getBool(GENERATE_LATEX);
12391   bool generateMan   = Config_getBool(GENERATE_MAN);
12392   bool generateRtf   = Config_getBool(GENERATE_RTF);
12393   bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12394
12395
12396   g_outputList = new OutputList;
12397   if (generateHtml)
12398   {
12399     g_outputList->add<HtmlGenerator>();
12400     HtmlGenerator::init();
12401     HtmlGenerator::writeTabData();
12402   }
12403   if (generateLatex)
12404   {
12405     g_outputList->add<LatexGenerator>();
12406     LatexGenerator::init();
12407   }
12408   if (generateDocbook)
12409   {
12410     g_outputList->add<DocbookGenerator>();
12411     DocbookGenerator::init();
12412   }
12413   if (generateMan)
12414   {
12415     g_outputList->add<ManGenerator>();
12416     ManGenerator::init();
12417   }
12418   if (generateRtf)
12419   {
12420     g_outputList->add<RTFGenerator>();
12421     RTFGenerator::init();
12422   }
12423   if (Config_getBool(USE_HTAGS))
12424   {
12425     Htags::useHtags = TRUE;
12426     QCString htmldir = Config_getString(HTML_OUTPUT);
12427     if (!Htags::execute(htmldir))
12428        err("USE_HTAGS is YES but htags(1) failed. \n");
12429     else if (!Htags::loadFilemap(htmldir))
12430        err("htags(1) ended normally but failed to load the filemap. \n");
12431   }
12432
12433   /**************************************************************************
12434    *                        Generate documentation                          *
12435    **************************************************************************/
12436
12437   g_s.begin("Generating style sheet...\n");
12438   //printf("writing style info\n");
12439   g_outputList->writeStyleInfo(0); // write first part
12440   g_s.end();
12441
12442   bool searchEngine      = Config_getBool(SEARCHENGINE);
12443   bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
12444
12445   g_s.begin("Generating search indices...\n");
12446   if (searchEngine && !serverBasedSearch && generateHtml)
12447   {
12448     createJavaScriptSearchIndex();
12449   }
12450
12451   // generate search indices (need to do this before writing other HTML
12452   // pages as these contain a drop down menu with options depending on
12453   // what categories we find in this function.
12454   if (generateHtml && searchEngine)
12455   {
12456     QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
12457     Dir searchDir(searchDirName.str());
12458     if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
12459     {
12460       term("Could not create search results directory '%s' $PWD='%s'\n",
12461           qPrint(searchDirName),Dir::currentDirPath().c_str());
12462     }
12463     HtmlGenerator::writeSearchData(searchDirName);
12464     if (!serverBasedSearch) // client side search index
12465     {
12466       writeJavaScriptSearchIndex();
12467     }
12468   }
12469   g_s.end();
12470
12471   // copy static stuff
12472   if (generateHtml)
12473   {
12474     FTVHelp::generateTreeViewImages();
12475     copyStyleSheet();
12476     copyLogo(Config_getString(HTML_OUTPUT));
12477     copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
12478   }
12479   if (generateLatex)
12480   {
12481     copyLatexStyleSheet();
12482     copyLogo(Config_getString(LATEX_OUTPUT));
12483     copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
12484   }
12485   if (generateDocbook)
12486   {
12487     copyLogo(Config_getString(DOCBOOK_OUTPUT));
12488   }
12489   if (generateRtf)
12490   {
12491     copyLogo(Config_getString(RTF_OUTPUT));
12492   }
12493
12494   FormulaManager &fm = FormulaManager::instance();
12495   if (fm.hasFormulas() && generateHtml
12496       && !Config_getBool(USE_MATHJAX))
12497   {
12498     g_s.begin("Generating images for formulas in HTML...\n");
12499     fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
12500         FormulaManager::Format::Vector : FormulaManager::Format::Bitmap, FormulaManager::HighDPI::On);
12501     g_s.end();
12502   }
12503   if (fm.hasFormulas() && generateRtf)
12504   {
12505     g_s.begin("Generating images for formulas in RTF...\n");
12506     fm.generateImages(Config_getString(RTF_OUTPUT),FormulaManager::Format::Bitmap);
12507     g_s.end();
12508   }
12509
12510   if (fm.hasFormulas() && generateDocbook)
12511   {
12512     g_s.begin("Generating images for formulas in Docbook...\n");
12513     fm.generateImages(Config_getString(DOCBOOK_OUTPUT),FormulaManager::Format::Bitmap);
12514     g_s.end();
12515   }
12516
12517   g_s.begin("Generating example documentation...\n");
12518   generateExampleDocs();
12519   g_s.end();
12520
12521   g_s.begin("Generating file sources...\n");
12522   generateFileSources();
12523   g_s.end();
12524
12525   g_s.begin("Generating file documentation...\n");
12526   generateFileDocs();
12527   g_s.end();
12528
12529   g_s.begin("Generating page documentation...\n");
12530   generatePageDocs();
12531   g_s.end();
12532
12533   g_s.begin("Generating group documentation...\n");
12534   generateGroupDocs();
12535   g_s.end();
12536
12537   g_s.begin("Generating class documentation...\n");
12538   generateClassDocs();
12539   g_s.end();
12540
12541   g_s.begin("Generating concept documentation...\n");
12542   generateConceptDocs();
12543   g_s.end();
12544
12545   g_s.begin("Generating module documentation...\n");
12546   ModuleManager::instance().writeDocumentation(*g_outputList);
12547   g_s.end();
12548
12549   g_s.begin("Generating namespace documentation...\n");
12550   generateNamespaceDocs();
12551   g_s.end();
12552
12553   if (Config_getBool(GENERATE_LEGEND))
12554   {
12555     g_s.begin("Generating graph info page...\n");
12556     writeGraphInfo(*g_outputList);
12557     g_s.end();
12558   }
12559
12560   g_s.begin("Generating directory documentation...\n");
12561   generateDirDocs(*g_outputList);
12562   g_s.end();
12563
12564   if (g_outputList->size()>0)
12565   {
12566     writeIndexHierarchy(*g_outputList);
12567   }
12568
12569   g_s.begin("finalizing index lists...\n");
12570   Doxygen::indexList->finalize();
12571   g_s.end();
12572
12573   g_s.begin("writing tag file...\n");
12574   writeTagFile();
12575   g_s.end();
12576
12577   if (Config_getBool(GENERATE_XML))
12578   {
12579     g_s.begin("Generating XML output...\n");
12580     Doxygen::generatingXmlOutput=TRUE;
12581     generateXML();
12582     Doxygen::generatingXmlOutput=FALSE;
12583     g_s.end();
12584   }
12585   if (Config_getBool(GENERATE_SQLITE3))
12586   {
12587     g_s.begin("Generating SQLITE3 output...\n");
12588     generateSqlite3();
12589     g_s.end();
12590   }
12591
12592   if (Config_getBool(GENERATE_AUTOGEN_DEF))
12593   {
12594     g_s.begin("Generating AutoGen DEF output...\n");
12595     generateDEF();
12596     g_s.end();
12597   }
12598   if (Config_getBool(GENERATE_PERLMOD))
12599   {
12600     g_s.begin("Generating Perl module output...\n");
12601     generatePerlMod();
12602     g_s.end();
12603   }
12604   if (generateHtml && searchEngine && serverBasedSearch)
12605   {
12606     g_s.begin("Generating search index\n");
12607     if (Doxygen::searchIndex->kind()==SearchIndexIntf::Internal) // write own search index
12608     {
12609       HtmlGenerator::writeSearchPage();
12610       Doxygen::searchIndex->write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
12611     }
12612     else // write data for external search index
12613     {
12614       HtmlGenerator::writeExternalSearchPage();
12615       QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
12616       if (searchDataFile.isEmpty())
12617       {
12618         searchDataFile="searchdata.xml";
12619       }
12620       if (!Portable::isAbsolutePath(searchDataFile.data()))
12621       {
12622         searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
12623       }
12624       Doxygen::searchIndex->write(searchDataFile);
12625     }
12626     g_s.end();
12627   }
12628
12629   if (generateRtf)
12630   {
12631     g_s.begin("Combining RTF output...\n");
12632     if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
12633     {
12634       err("An error occurred during post-processing the RTF files!\n");
12635     }
12636     g_s.end();
12637   }
12638
12639   g_s.begin("Running plantuml with JAVA...\n");
12640   PlantumlManager::instance().run();
12641   g_s.end();
12642
12643   if (Config_getBool(HAVE_DOT))
12644   {
12645     g_s.begin("Running dot...\n");
12646     DotManager::instance()->run();
12647     g_s.end();
12648   }
12649
12650   if (generateHtml &&
12651       Config_getBool(GENERATE_HTMLHELP) &&
12652       !Config_getString(HHC_LOCATION).isEmpty())
12653   {
12654     g_s.begin("Running html help compiler...\n");
12655     runHtmlHelpCompiler();
12656     g_s.end();
12657   }
12658
12659   if ( generateHtml &&
12660        Config_getBool(GENERATE_QHP) &&
12661       !Config_getString(QHG_LOCATION).isEmpty())
12662   {
12663     g_s.begin("Running qhelpgenerator...\n");
12664     runQHelpGenerator();
12665     g_s.end();
12666   }
12667
12668   g_outputList->cleanup();
12669
12670   msg("type lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n",
12671       Doxygen::typeLookupCache->size(),
12672       Doxygen::typeLookupCache->capacity(),
12673       Doxygen::typeLookupCache->hits(),
12674       Doxygen::typeLookupCache->misses());
12675   msg("symbol lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n",
12676       Doxygen::symbolLookupCache->size(),
12677       Doxygen::symbolLookupCache->capacity(),
12678       Doxygen::symbolLookupCache->hits(),
12679       Doxygen::symbolLookupCache->misses());
12680   int typeCacheParam   = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
12681   int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
12682   int cacheParam = std::max(typeCacheParam,symbolCacheParam);
12683   if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
12684   {
12685     msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is %d at the cost of higher memory usage.\n",cacheParam);
12686   }
12687
12688   if (Debug::isFlagSet(Debug::Time))
12689   {
12690
12691     std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
12692     if (numThreads<1) numThreads=1;
12693     msg("Total elapsed time: %.6f seconds\n(of which an average of %.6f seconds per thread waiting for external tools to finish)\n",
12694          (static_cast<double>(Debug::elapsedTime())),
12695          Portable::getSysElapsedTime()/static_cast<double>(numThreads)
12696         );
12697     g_s.print();
12698
12699     Debug::clearFlag(Debug::Time);
12700     msg("finished...\n");
12701     Debug::setFlag(Debug::Time);
12702   }
12703   else
12704   {
12705     msg("finished...\n");
12706   }
12707
12708
12709   /**************************************************************************
12710    *                        Start cleaning up                               *
12711    **************************************************************************/
12712
12713   cleanUpDoxygen();
12714
12715   finalizeSearchIndexer();
12716   Dir thisDir;
12717   thisDir.remove(Doxygen::filterDBFileName.str());
12718   finishWarnExit();
12719   exitTracing();
12720   Config::deinit();
12721   delete Doxygen::clangUsrMap;
12722   g_successfulRun=TRUE;
12723
12724   //dumpDocNodeSizes();
12725 }