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 <stdio.h>
17 #include <stdlib.h>
18 #include <sys/stat.h>
19 #include <errno.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 "message.h"
35 #include "config.h"
36 #include "util.h"
37 #include "pre.h"
38 #include "tagreader.h"
39 #include "dot.h"
40 #include "msc.h"
41 #include "docparser.h"
42 #include "dirdef.h"
43 #include "outputlist.h"
44 #include "declinfo.h"
45 #include "htmlgen.h"
46 #include "latexgen.h"
47 #include "mangen.h"
48 #include "language.h"
49 #include "debug.h"
50 #include "htmlhelp.h"
51 #include "qhp.h"
52 #include "ftvhelp.h"
53 #include "defargs.h"
54 #include "rtfgen.h"
55 #include "sqlite3gen.h"
56 #include "xmlgen.h"
57 #include "docbookgen.h"
58 #include "defgen.h"
59 #include "perlmodgen.h"
60 #include "reflist.h"
61 #include "pagedef.h"
62 #include "bufstr.h"
63 #include "commentcnv.h"
64 #include "cmdmapper.h"
65 #include "searchindex.h"
66 #include "parserintf.h"
67 #include "htags.h"
68 #include "pycode.h"
69 #include "pyscanner.h"
70 #include "fortrancode.h"
71 #include "fortranscanner.h"
72 #include "xmlcode.h"
73 #include "sqlcode.h"
74 #include "lexcode.h"
75 #include "lexscanner.h"
76 #include "code.h"
77 #include "portable.h"
78 #include "vhdljjparser.h"
79 #include "vhdldocgen.h"
80 #include "vhdlcode.h"
81 #include "eclipsehelp.h"
82 #include "cite.h"
83 #include "markdown.h"
84 #include "arguments.h"
85 #include "memberlist.h"
86 #include "layout.h"
87 #include "groupdef.h"
88 #include "classlist.h"
89 #include "namespacedef.h"
90 #include "filename.h"
91 #include "membername.h"
92 #include "membergroup.h"
93 #include "docsets.h"
94 #include "formula.h"
95 #include "settings.h"
96 #include "context.h"
97 #include "fileparser.h"
98 #include "emoji.h"
99 #include "plantuml.h"
100 #include "stlsupport.h"
101 #include "threadpool.h"
102 #include "clangparser.h"
103 #include "symbolresolver.h"
104 #include "regex.h"
105 #include "fileinfo.h"
106 #include "dir.h"
107 #include "conceptdef.h"
108 
109 #if USE_SQLITE3
110 #include <sqlite3.h>
111 #endif
112 
113 #if USE_LIBCLANG
114 #include <clang/Basic/Version.h>
115 #endif
116 
117 // provided by the generated file resources.cpp
118 extern void initResources();
119 
120 #if !defined(_WIN32) || defined(__CYGWIN__)
121 #include <signal.h>
122 #define HAS_SIGNALS
123 #endif
124 
125 // globally accessible variables
126 ClassLinkedMap       *Doxygen::classLinkedMap = 0;
127 ClassLinkedMap       *Doxygen::hiddenClassLinkedMap = 0;
128 ConceptLinkedMap     *Doxygen::conceptLinkedMap = 0;
129 NamespaceLinkedMap   *Doxygen::namespaceLinkedMap = 0;
130 MemberNameLinkedMap  *Doxygen::memberNameLinkedMap = 0;
131 MemberNameLinkedMap  *Doxygen::functionNameLinkedMap = 0;
132 FileNameLinkedMap    *Doxygen::inputNameLinkedMap = 0;
133 GroupLinkedMap       *Doxygen::groupLinkedMap = 0;
134 PageLinkedMap        *Doxygen::pageLinkedMap = 0;
135 PageLinkedMap        *Doxygen::exampleLinkedMap = 0;
136 StringMap             Doxygen::aliasMap;                     // aliases
137 StringSet             Doxygen::inputPaths;
138 FileNameLinkedMap    *Doxygen::includeNameLinkedMap = 0;     // include names
139 FileNameLinkedMap    *Doxygen::exampleNameLinkedMap = 0;     // examples
140 FileNameLinkedMap    *Doxygen::imageNameLinkedMap = 0;       // images
141 FileNameLinkedMap    *Doxygen::dotFileNameLinkedMap = 0;     // dot files
142 FileNameLinkedMap    *Doxygen::mscFileNameLinkedMap = 0;     // msc files
143 FileNameLinkedMap    *Doxygen::diaFileNameLinkedMap = 0;     // dia files
144 StringUnorderedMap    Doxygen::namespaceAliasMap;            // all namespace aliases
145 StringMap             Doxygen::tagDestinationMap;            // all tag locations
146 StringUnorderedSet    Doxygen::expandAsDefinedSet;           // all macros that should be expanded
147 MemberGroupInfoMap    Doxygen::memberGroupInfoMap;           // dictionary of the member groups heading
148 std::unique_ptr<PageDef> Doxygen::mainPage;
149 bool                  Doxygen::insideMainPage = FALSE; // are we generating docs for the main page?
150 NamespaceDefMutable  *Doxygen::globalScope = 0;
151 bool                  Doxygen::parseSourcesNeeded = FALSE;
152 SearchIndexIntf      *Doxygen::searchIndex=0;
153 SymbolMap<Definition>*Doxygen::symbolMap;
154 ClangUsrMap          *Doxygen::clangUsrMap = 0;
155 Cache<std::string,LookupInfo> *Doxygen::lookupCache;
156 DirLinkedMap         *Doxygen::dirLinkedMap;
157 DirRelationLinkedMap  Doxygen::dirRelations;
158 ParserManager        *Doxygen::parserManager = 0;
159 QCString              Doxygen::htmlFileExtension;
160 bool                  Doxygen::suppressDocWarnings = FALSE;
161 QCString              Doxygen::filterDBFileName;
162 IndexList            *Doxygen::indexList;
163 int                   Doxygen::subpageNestingLevel = 0;
164 QCString              Doxygen::spaces;
165 bool                  Doxygen::generatingXmlOutput = FALSE;
166 DefinesPerFileList    Doxygen::macroDefinitions;
167 bool                  Doxygen::clangAssistedParsing = FALSE;
168 
169 // locally accessible globals
170 static std::multimap< std::string, const Entry* > g_classEntries;
171 static StringVector     g_inputFiles;
172 static StringSet        g_compoundKeywords;        // keywords recognised as compounds
173 static OutputList      *g_outputList = 0;          // list of output generating objects
174 static StringSet        g_usingDeclarations; // used classes
175 static bool             g_successfulRun = FALSE;
176 static bool             g_dumpSymbolMap = FALSE;
177 static bool             g_useOutputTemplate = FALSE;
178 
clearAll()179 void clearAll()
180 {
181   g_inputFiles.clear();
182   //g_excludeNameDict.clear();
183   //delete g_outputList; g_outputList=0;
184 
185   Doxygen::classLinkedMap->clear();
186   Doxygen::hiddenClassLinkedMap->clear();
187   Doxygen::conceptLinkedMap->clear();
188   Doxygen::namespaceLinkedMap->clear();
189   Doxygen::pageLinkedMap->clear();
190   Doxygen::exampleLinkedMap->clear();
191   Doxygen::inputNameLinkedMap->clear();
192   Doxygen::includeNameLinkedMap->clear();
193   Doxygen::exampleNameLinkedMap->clear();
194   Doxygen::imageNameLinkedMap->clear();
195   Doxygen::dotFileNameLinkedMap->clear();
196   Doxygen::mscFileNameLinkedMap->clear();
197   Doxygen::diaFileNameLinkedMap->clear();
198   Doxygen::tagDestinationMap.clear();
199   SectionManager::instance().clear();
200   CitationManager::instance().clear();
201   Doxygen::mainPage.reset();
202   FormulaManager::instance().clear();
203 }
204 
205 class Statistics
206 {
207   public:
Statistics()208     Statistics() {}
begin(const char * name)209     void begin(const char *name)
210     {
211       msg("%s", name);
212       stats.emplace_back(name,0);
213       startTime = std::chrono::steady_clock::now();
214     }
end()215     void end()
216     {
217       std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
218       stats.back().elapsed = std::chrono::duration_cast<
219                                 std::chrono::microseconds>(endTime - startTime).count()/1000000.0;
220     }
print()221     void print()
222     {
223       bool restore=FALSE;
224       if (Debug::isFlagSet(Debug::Time))
225       {
226         Debug::clearFlag("time");
227         restore=TRUE;
228       }
229       msg("----------------------\n");
230       for (const auto &s : stats)
231       {
232         msg("Spent %.6f seconds in %s",s.elapsed,s.name);
233       }
234       if (restore) Debug::setFlag("time");
235     }
236   private:
237     struct stat
238     {
239       const char *name;
240       double elapsed;
241       //stat() : name(NULL),elapsed(0) {}
statStatistics::stat242       stat(const char *n, double el) : name(n),elapsed(el) {}
243     };
244     std::vector<stat> stats;
245     std::chrono::steady_clock::time_point startTime;
246 } g_s;
247 
248 
249 static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
250                    const ArgumentList *al,bool over_load,uint64 spec);
251 static void findMember(const Entry *root,
252                        const QCString &relates,
253                        const QCString &type,
254                        const QCString &args,
255                        QCString funcDecl,
256                        bool overloaded,
257                        bool isFunc
258                       );
259 
260 enum FindBaseClassRelation_Mode
261 {
262   TemplateInstances,
263   DocumentedOnly,
264   Undocumented
265 };
266 
267 
268 static bool findClassRelation(
269                            const Entry *root,
270                            Definition *context,
271                            ClassDefMutable *cd,
272                            const BaseInfo *bi,
273                            const TemplateNameMap &templateNames,
274                            /*bool insertUndocumented*/
275                            FindBaseClassRelation_Mode mode,
276                            bool isArtificial
277                           );
278 
279 //----------------------------------------------------------------------------
280 
281 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
282                                               FileDef *fileScope,const TagInfo *tagInfo);
283 
addPageToContext(PageDef * pd,Entry * root)284 static void addPageToContext(PageDef *pd,Entry *root)
285 {
286   if (root->parent()) // add the page to it's scope
287   {
288     QCString scope = root->parent()->name;
289     if (root->parent()->section==Entry::PACKAGEDOC_SEC)
290     {
291       scope=substitute(scope,".","::");
292     }
293     scope = stripAnonymousNamespaceScope(scope);
294     scope+="::"+pd->name();
295     Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
296     if (d)
297     {
298       pd->setPageScope(d);
299     }
300   }
301 }
302 
addRelatedPage(Entry * root)303 static void addRelatedPage(Entry *root)
304 {
305   GroupDef *gd=0;
306   for (const Grouping &g : root->groups)
307   {
308     if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
309   }
310   //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
311   QCString doc=root->doc+root->inbodyDocs;
312 
313   PageDef *pd = addRelatedPage(root->name,root->args,doc,
314       root->docFile,
315       root->docLine,
316       root->startLine,
317       root->sli,
318       gd,root->tagInfo(),
319       FALSE,
320       root->lang
321      );
322   if (pd)
323   {
324     pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
325     pd->addSectionsToDefinition(root->anchors);
326     pd->setLocalToc(root->localToc);
327     addPageToContext(pd,root);
328   }
329 }
330 
buildGroupListFiltered(const Entry * root,bool additional,bool includeExternal)331 static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
332 {
333   if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
334         ((!includeExternal && root->tagInfo()==0) ||
335          ( includeExternal && root->tagInfo()!=0))
336      )
337   {
338     if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
339         (root->groupDocType!=Entry::GROUPDOC_NORMAL &&  additional))
340     {
341       GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
342       //printf("Processing group '%s':'%s' add=%d ext=%d gd=%p\n",
343       //    qPrint(root->type),qPrint(root->name),additional,includeExternal,gd);
344 
345       if (gd)
346       {
347         if ( !gd->hasGroupTitle() )
348         {
349           gd->setGroupTitle( root->type );
350         }
351         else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
352         {
353           warn( root->fileName,root->startLine,
354               "group %s: ignoring title \"%s\" that does not match old title \"%s\"\n",
355               qPrint(root->name), qPrint(root->type), qPrint(gd->groupTitle()) );
356         }
357         gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
358         gd->setDocumentation( root->doc, root->docFile, root->docLine );
359         gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
360         gd->addSectionsToDefinition(root->anchors);
361         gd->setRefItems(root->sli);
362         gd->setLanguage(root->lang);
363       }
364       else
365       {
366         if (root->tagInfo())
367         {
368           gd = Doxygen::groupLinkedMap->add(root->name,
369                std::unique_ptr<GroupDef>(
370                   createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
371           gd->setReference(root->tagInfo()->tagName);
372         }
373         else
374         {
375           gd = Doxygen::groupLinkedMap->add(root->name,
376                std::unique_ptr<GroupDef>(
377                   createGroupDef(root->fileName,root->startLine,root->name,root->type)));
378         }
379         gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
380         // allow empty docs for group
381         gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
382         gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
383         gd->addSectionsToDefinition(root->anchors);
384         gd->setRefItems(root->sli);
385         gd->setLanguage(root->lang);
386       }
387     }
388   }
389   for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
390 }
391 
buildGroupList(const Entry * root)392 static void buildGroupList(const Entry *root)
393 {
394   // --- first process only local groups
395   // first process the @defgroups blocks
396   buildGroupListFiltered(root,FALSE,FALSE);
397   // then process the @addtogroup, @weakgroup blocks
398   buildGroupListFiltered(root,TRUE,FALSE);
399 
400   // --- then also process external groups
401   // first process the @defgroups blocks
402   buildGroupListFiltered(root,FALSE,TRUE);
403   // then process the @addtogroup, @weakgroup blocks
404   buildGroupListFiltered(root,TRUE,TRUE);
405 }
406 
findGroupScope(const Entry * root)407 static void findGroupScope(const Entry *root)
408 {
409   if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
410       root->parent() && !root->parent()->name.isEmpty())
411   {
412     GroupDef *gd;
413     if ((gd=Doxygen::groupLinkedMap->find(root->name)))
414     {
415       QCString scope = root->parent()->name;
416       if (root->parent()->section==Entry::PACKAGEDOC_SEC)
417       {
418         scope=substitute(scope,".","::");
419       }
420       scope = stripAnonymousNamespaceScope(scope);
421       scope+="::"+gd->name();
422       Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
423       if (d)
424       {
425         gd->setGroupScope(d);
426       }
427     }
428   }
429   for (const auto &e : root->children()) findGroupScope(e.get());
430 }
431 
organizeSubGroupsFiltered(const Entry * root,bool additional)432 static void organizeSubGroupsFiltered(const Entry *root,bool additional)
433 {
434   if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty())
435   {
436     if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
437         (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
438     {
439       GroupDef *gd;
440       if ((gd=Doxygen::groupLinkedMap->find(root->name)))
441       {
442         //printf("adding %s to group %s\n",qPrint(root->name),qPrint(gd->name()));
443         addGroupToGroups(root,gd);
444       }
445     }
446   }
447   for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
448 }
449 
organizeSubGroups(const Entry * root)450 static void organizeSubGroups(const Entry *root)
451 {
452   //printf("Defining groups\n");
453   // first process the @defgroups blocks
454   organizeSubGroupsFiltered(root,FALSE);
455   //printf("Additional groups\n");
456   // then process the @addtogroup, @weakgroup blocks
457   organizeSubGroupsFiltered(root,TRUE);
458 }
459 
460 //----------------------------------------------------------------------
461 
buildFileList(const Entry * root)462 static void buildFileList(const Entry *root)
463 {
464   if (((root->section==Entry::FILEDOC_SEC) ||
465         ((root->section & Entry::FILE_MASK) && Config_getBool(EXTRACT_ALL))) &&
466       !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
467      )
468   {
469     bool ambig;
470     FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,root->name,ambig);
471     if (!fd || ambig)
472     {
473       int save_ambig = ambig;
474       // use the directory of the file to see if the described file is in the same
475       // directory as the describing file.
476       QCString fn = root->fileName;
477       int newIndex=fn.findRev('/');
478       if (newIndex<0)
479       {
480         fn = root->name;
481       }
482       else
483       {
484         fn = fn.left(newIndex)+"/"+root->name;
485       }
486       fd=findFileDef(Doxygen::inputNameLinkedMap,fn,ambig);
487       if (!fd) ambig = save_ambig;
488     }
489     //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),fd);
490     if (fd && !ambig)
491     {
492       //printf("Adding documentation!\n");
493       // using FALSE in setDocumentation is small hack to make sure a file
494       // is documented even if a \file command is used without further
495       // documentation
496       fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
497       fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
498       fd->addSectionsToDefinition(root->anchors);
499       fd->setRefItems(root->sli);
500       for (const Grouping &g : root->groups)
501       {
502         GroupDef *gd=0;
503         if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
504         {
505           gd->addFile(fd);
506           fd->makePartOfGroup(gd);
507           //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
508         }
509       }
510     }
511     else
512     {
513       QCString text(4096);
514       text.sprintf("the name '%s' supplied as "
515           "the argument in the \\file statement ",
516           qPrint(root->name));
517       if (ambig) // name is ambiguous
518       {
519         text+="matches the following input files:\n";
520         text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->name);
521         text+="Please use a more specific name by "
522           "including a (larger) part of the path!";
523       }
524       else // name is not an input file
525       {
526         text+="is not an input file";
527       }
528       warn(root->fileName,root->startLine,"%s", qPrint(text));
529     }
530   }
531   for (const auto &e : root->children()) buildFileList(e.get());
532 }
533 
534 template<class DefMutable>
addIncludeFile(DefMutable * cd,FileDef * ifd,const Entry * root)535 static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
536 {
537   if (
538       (!root->doc.stripWhiteSpace().isEmpty() ||
539        !root->brief.stripWhiteSpace().isEmpty() ||
540        Config_getBool(EXTRACT_ALL)
541       ) && root->protection!=Private
542      )
543   {
544     //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
545 
546     bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
547     QCString includeFile = root->includeFile;
548     if (!includeFile.isEmpty() && includeFile.at(0)=='"')
549     {
550       local = TRUE;
551       includeFile=includeFile.mid(1,includeFile.length()-2);
552     }
553     else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
554     {
555       local = FALSE;
556       includeFile=includeFile.mid(1,includeFile.length()-2);
557     }
558 
559     bool ambig;
560     FileDef *fd=0;
561     // see if we need to include a verbatim copy of the header file
562     //printf("root->includeFile=%s\n",qPrint(root->includeFile));
563     if (!includeFile.isEmpty() &&
564         (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==0
565        )
566     { // explicit request
567       QCString text;
568       text.sprintf("the name '%s' supplied as "
569                   "the argument of the \\class, \\struct, \\union, or \\include command ",
570                   qPrint(includeFile)
571                  );
572       if (ambig) // name is ambiguous
573       {
574         text+="matches the following input files:\n";
575         text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->includeFile);
576         text+="Please use a more specific name by "
577             "including a (larger) part of the path!";
578       }
579       else // name is not an input file
580       {
581         text+="is not an input file";
582       }
583       warn(root->fileName,root->startLine, "%s", qPrint(text));
584     }
585     else if (includeFile.isEmpty() && ifd &&
586         // see if the file extension makes sense
587         guessSection(ifd->name())==Entry::HEADER_SEC)
588     { // implicit assumption
589       fd=ifd;
590     }
591 
592     // if a file is found, we mark it as a source file.
593     if (fd)
594     {
595       QCString iName = !root->includeName.isEmpty() ?
596                        root->includeName : includeFile;
597       if (!iName.isEmpty()) // user specified include file
598       {
599         if (iName.at(0)=='<') local=FALSE; // explicit override
600         else if (iName.at(0)=='"') local=TRUE;
601         if (iName.at(0)=='"' || iName.at(0)=='<')
602         {
603           iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
604         }
605         if (iName.isEmpty())
606         {
607           iName=fd->name();
608         }
609       }
610       else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
611       {
612         iName=stripFromIncludePath(fd->absFilePath());
613       }
614       else // use name of the file containing the class definition
615       {
616         iName=fd->name();
617       }
618       if (fd->generateSourceFile()) // generate code for header
619       {
620         cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
621       }
622       else // put #include in the class documentation without link
623       {
624         cd->setIncludeFile(0,iName,local,TRUE);
625       }
626     }
627   }
628 }
629 
630 #if 0
631 static bool addNamespace(Entry *root,ClassDef *cd)
632 {
633   // see if this class is defined inside a namespace
634   if (root->section & Entry::COMPOUND_MASK)
635   {
636     Entry *e = root->parent;
637     while (e)
638     {
639       if (e->section==Entry::NAMESPACE_SEC)
640       {
641         NamespaceDef *nd=0;
642         QCString nsName = stripAnonymousNamespaceScope(e->name);
643         //printf("addNameSpace() trying: %s\n",qPrint(nsName));
644         if (!nsName.isEmpty() && nsName.at(0)!='@' &&
645             (nd=getResolvedNamespace(nsName))
646            )
647         {
648           cd->setNamespace(nd);
649           cd->setOuterScope(nd);
650           nd->insertClass(cd);
651           return TRUE;
652         }
653       }
654       e=e->parent;
655     }
656   }
657   return FALSE;
658 }
659 #endif
660 
661 #if 0
662 static Definition *findScope(Entry *root,int level=0)
663 {
664   if (root==0) return 0;
665   //printf("start findScope name=%s\n",qPrint(root->name));
666   Definition *result=0;
667   if (root->section&Entry::SCOPE_MASK)
668   {
669     result = findScope(root->parent,level+1); // traverse to the root of the tree
670     if (result)
671     {
672       //printf("Found %s inside %s at level %d\n",qPrint(root->name),qPrint(result->name()),level);
673       // TODO: look at template arguments
674       result = result->findInnerCompound(root->name);
675     }
676     else // reached the global scope
677     {
678       // TODO: look at template arguments
679       result = Doxygen::globalScope->findInnerCompound(root->name);
680       //printf("Found in globalScope %s at level %d\n",qPrint(result->name()),level);
681     }
682   }
683   //printf("end findScope(%s,%d)=%s\n",qPrint(root->name),
684   //       level,result==0 ? "<none>" : qPrint(result->name()));
685   return result;
686 }
687 #endif
688 
stripTemplateSpecifiers(const QCString & s)689 QCString stripTemplateSpecifiers(const QCString &s)
690 {
691   int l = s.length();
692   int count=0;
693   int round=0;
694   QCString result;
695   for (int i=0;i<l;i++)
696   {
697     char c=s.at(i);
698     if      (c=='(') round++;
699     else if (c==')' && round>0) round--;
700     else if (c=='<' && round==0) count++;
701     if (count==0)
702     {
703       result+=c;
704     }
705     if (c=='>' && round==0 && count>0) count--;
706   }
707   //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
708   return result;
709 }
710 
711 /*! returns the Definition object belonging to the first \a level levels of
712  *  full qualified name \a name. Creates an artificial scope if the scope is
713  *  not found and set the parent/child scope relation if the scope is found.
714  */
buildScopeFromQualifiedName(const QCString & name_,SrcLangExt lang,const TagInfo * tagInfo)715 static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
716 {
717   QCString name = stripTemplateSpecifiers(name_);
718   int level = name.contains("::");
719   //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
720   int i=0;
721   int p=0,l;
722   Definition *prevScope=Doxygen::globalScope;
723   QCString fullScope;
724   while (i<level)
725   {
726     int idx=getScopeFragment(name,p,&l);
727     if (idx==-1) return prevScope;
728     QCString nsName = name.mid(idx,l);
729     if (nsName.isEmpty()) return prevScope;
730     if (!fullScope.isEmpty()) fullScope+="::";
731     fullScope+=nsName;
732     NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
733     DefinitionMutable *innerScope = toDefinitionMutable(nd);
734     ClassDef *cd=0;
735     if (nd==0) cd = getClass(fullScope);
736     if (nd==0 && cd) // scope is a class
737     {
738       innerScope = toDefinitionMutable(cd);
739     }
740     else if (nd==0 && cd==0 && fullScope.find('<')==-1) // scope is not known and could be a namespace!
741     {
742       // introduce bogus namespace
743       //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
744       NamespaceDefMutable *newNd=
745         toNamespaceDefMutable(
746           Doxygen::namespaceLinkedMap->add(fullScope,
747             std::unique_ptr<NamespaceDef>(
748               createNamespaceDef(
749                 "[generated]",1,1,fullScope,
750                 tagInfo?tagInfo->tagName:QCString(),
751                 tagInfo?tagInfo->fileName:QCString()))));
752       if (newNd)
753       {
754         newNd->setLanguage(lang);
755         newNd->setArtificial(TRUE);
756         // add namespace to the list
757         innerScope = newNd;
758       }
759     }
760     else // scope is a namespace
761     {
762     }
763     if (innerScope)
764     {
765       // make the parent/child scope relation
766       DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
767       if (prevScopeMutable)
768       {
769         prevScopeMutable->addInnerCompound(toDefinition(innerScope));
770       }
771       innerScope->setOuterScope(prevScope);
772     }
773     else // current scope is a class, so return only the namespace part...
774     {
775       return prevScope;
776     }
777     // proceed to the next scope fragment
778     p=idx+l+2;
779     prevScope=toDefinition(innerScope);
780     i++;
781   }
782   return prevScope;
783 }
784 
findScopeFromQualifiedName(NamespaceDefMutable * startScope,const QCString & n,FileDef * fileScope,const TagInfo * tagInfo)785 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
786                                               FileDef *fileScope,const TagInfo *tagInfo)
787 {
788   //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
789   Definition *resultScope=toDefinition(startScope);
790   if (resultScope==0) resultScope=Doxygen::globalScope;
791   QCString scope=stripTemplateSpecifiersFromScope(n,FALSE);
792   int l1=0,i1;
793   i1=getScopeFragment(scope,0,&l1);
794   if (i1==-1)
795   {
796     //printf(">no fragments!\n");
797     return resultScope;
798   }
799   int p=i1+l1,l2=0,i2;
800   while ((i2=getScopeFragment(scope,p,&l2))!=-1)
801   {
802     QCString nestedNameSpecifier = scope.mid(i1,l1);
803     Definition *orgScope = resultScope;
804     //printf("  nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
805     resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
806     //printf("  resultScope=%p\n",resultScope);
807     if (resultScope==0)
808     {
809       if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
810         // also search for used namespaces
811       {
812         for (const auto &nd : fileScope->getUsedNamespaces())
813         {
814           resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
815           if (resultScope!=0) break;
816         }
817         if (resultScope)
818         {
819           // for a nested class A::I in used namespace N, we get
820           // N::A::I while looking for A, so we should compare
821           // resultScope->name() against scope.left(i2+l2)
822           //printf("  -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
823           if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
824           {
825             break;
826           }
827           goto nextFragment;
828         }
829       }
830 
831       // also search for used classes. Complication: we haven't been able
832       // to put them in the right scope yet, because we are still resolving
833       // the scope relations!
834       // Therefore loop through all used classes and see if there is a right
835       // scope match between the used class and nestedNameSpecifier.
836       for (const auto &usedName : g_usingDeclarations)
837       {
838         //printf("Checking using class %s\n",ui.currentKey());
839         if (rightScopeMatch(usedName.c_str(),nestedNameSpecifier))
840         {
841           // ui.currentKey() is the fully qualified name of nestedNameSpecifier
842           // so use this instead.
843           QCString fqn = QCString(usedName) + scope.right(scope.length()-p);
844           resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),0);
845           //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
846           if (resultScope)
847           {
848             //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
849             return resultScope;
850           }
851         }
852       }
853 
854       //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
855       return 0;
856     }
857  nextFragment:
858     i1=i2;
859     l1=l2;
860     p=i2+l2;
861   }
862   //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
863   return resultScope;
864 }
865 
getTemplateArgumentsFromName(const QCString & name,const ArgumentLists & tArgLists)866 std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
867                   const QCString &name,
868                   const ArgumentLists &tArgLists)
869 {
870   // for each scope fragment, check if it is a template and advance through
871   // the list if so.
872   int i,p=0;
873   auto alIt = tArgLists.begin();
874   while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
875   {
876     NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
877     if (nd==0)
878     {
879       ClassDef *cd = getClass(name.left(i));
880       if (cd)
881       {
882         if (!cd->templateArguments().empty())
883         {
884           ++alIt;
885         }
886       }
887     }
888     p=i+2;
889   }
890   return alIt!=tArgLists.end() ?
891          std::make_unique<ArgumentList>(*alIt) :
892          std::unique_ptr<ArgumentList>();
893 }
894 
895 static
convertToCompoundType(int section,uint64 specifier)896 ClassDef::CompoundType convertToCompoundType(int section,uint64 specifier)
897 {
898   ClassDef::CompoundType sec=ClassDef::Class;
899   if (specifier&Entry::Struct)
900     sec=ClassDef::Struct;
901   else if (specifier&Entry::Union)
902     sec=ClassDef::Union;
903   else if (specifier&Entry::Category)
904     sec=ClassDef::Category;
905   else if (specifier&Entry::Interface)
906     sec=ClassDef::Interface;
907   else if (specifier&Entry::Protocol)
908     sec=ClassDef::Protocol;
909   else if (specifier&Entry::Exception)
910     sec=ClassDef::Exception;
911   else if (specifier&Entry::Service)
912     sec=ClassDef::Service;
913   else if (specifier&Entry::Singleton)
914     sec=ClassDef::Singleton;
915 
916   switch(section)
917   {
918     //case Entry::UNION_SEC:
919     case Entry::UNIONDOC_SEC:
920       sec=ClassDef::Union;
921       break;
922       //case Entry::STRUCT_SEC:
923     case Entry::STRUCTDOC_SEC:
924       sec=ClassDef::Struct;
925       break;
926       //case Entry::INTERFACE_SEC:
927     case Entry::INTERFACEDOC_SEC:
928       sec=ClassDef::Interface;
929       break;
930       //case Entry::PROTOCOL_SEC:
931     case Entry::PROTOCOLDOC_SEC:
932       sec=ClassDef::Protocol;
933       break;
934       //case Entry::CATEGORY_SEC:
935     case Entry::CATEGORYDOC_SEC:
936       sec=ClassDef::Category;
937       break;
938       //case Entry::EXCEPTION_SEC:
939     case Entry::EXCEPTIONDOC_SEC:
940       sec=ClassDef::Exception;
941       break;
942     case Entry::SERVICEDOC_SEC:
943       sec=ClassDef::Service;
944       break;
945     case Entry::SINGLETONDOC_SEC:
946       sec=ClassDef::Singleton;
947       break;
948   }
949   return sec;
950 }
951 
952 
addClassToContext(const Entry * root)953 static void addClassToContext(const Entry *root)
954 {
955   FileDef *fd = root->fileDef();
956 
957   QCString scName;
958   if (root->parent()->section&Entry::SCOPE_MASK)
959   {
960      scName=root->parent()->name;
961   }
962   // name without parent's scope
963   QCString fullName = root->name;
964 
965   // strip off any template parameters (but not those for specializations)
966   fullName=stripTemplateSpecifiersFromScope(fullName);
967 
968   // name with scope (if not present already)
969   QCString qualifiedName = fullName;
970   if (!scName.isEmpty() && !leftScopeMatch(fullName,scName))
971   {
972     qualifiedName.prepend(scName+"::");
973   }
974 
975   // see if we already found the class before
976   ClassDefMutable *cd = getClassMutable(qualifiedName);
977 
978   Debug::print(Debug::Classes,0, "  Found class with name %s (qualifiedName=%s -> cd=%p)\n",
979       cd ? qPrint(cd->name()) : qPrint(root->name), qPrint(qualifiedName),cd);
980 
981   if (cd)
982   {
983     fullName=cd->name();
984     Debug::print(Debug::Classes,0,"  Existing class %s!\n",qPrint(cd->name()));
985     //if (cd->templateArguments()==0)
986     //{
987     //  //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
988     //  cd->setTemplateArguments(tArgList);
989     //}
990 
991     cd->setDocumentation(root->doc,root->docFile,root->docLine);
992     cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
993 
994     if ((root->spec&Entry::ForwardDecl)==0 && cd->isForwardDeclared())
995     {
996       cd->setDefFile(root->fileName,root->startLine,root->startColumn);
997       if (root->bodyLine!=-1)
998       {
999         cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1000         cd->setBodyDef(fd);
1001       }
1002     }
1003 
1004     if (cd->templateArguments().empty() || (cd->isForwardDeclared() && (root->spec&Entry::ForwardDecl)==0))
1005     {
1006       // this happens if a template class declared with @class is found
1007       // before the actual definition or if a forward declaration has different template
1008       // parameter names.
1009       std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1010       if (tArgList)
1011       {
1012         cd->setTemplateArguments(*tArgList);
1013       }
1014     }
1015     if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1016     {
1017       cd->setRequiresClause(root->req);
1018     }
1019 
1020     cd->setCompoundType(convertToCompoundType(root->section,root->spec));
1021 
1022     cd->setMetaData(root->metaData);
1023   }
1024   else // new class
1025   {
1026     ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec);
1027 
1028     QCString className;
1029     QCString namespaceName;
1030     extractNamespaceName(fullName,className,namespaceName);
1031 
1032     //printf("New class: fullname %s namespace '%s' name='%s' brief='%s' docs='%s'\n",
1033     //    qPrint(fullName),qPrint(namespaceName),qPrint(className),qPrint(root->brief),qPrint(root->doc));
1034 
1035     QCString tagName;
1036     QCString refFileName;
1037     const TagInfo *tagInfo = root->tagInfo();
1038     int i;
1039     if (tagInfo)
1040     {
1041       tagName     = tagInfo->tagName;
1042       refFileName = tagInfo->fileName;
1043       if (fullName.find("::")!=-1)
1044         // symbols imported via tag files may come without the parent scope,
1045         // so we artificially create it here
1046       {
1047         buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1048       }
1049     }
1050     std::unique_ptr<ArgumentList> tArgList;
1051     if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) && (i=fullName.findRev('<'))!=-1)
1052     {
1053       // a Java/C# generic class looks like a C++ specialization, so we need to split the
1054       // name and template arguments here
1055       tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1056       fullName=fullName.left(i);
1057     }
1058     else
1059     {
1060       tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1061     }
1062     // add class to the list
1063     //printf("ClassDict.insert(%s)\n",qPrint(fullName));
1064     cd = toClassDefMutable(
1065         Doxygen::classLinkedMap->add(fullName,
1066           std::unique_ptr<ClassDef>(
1067             createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1068                fullName,sec,tagName,refFileName,TRUE,root->spec&Entry::Enum) )));
1069     if (cd)
1070     {
1071       Debug::print(Debug::Classes,0,"  New class '%s' (sec=0x%08x)! #tArgLists=%d tagInfo=%p hidden=%d artificial=%d\n",
1072           qPrint(fullName),sec,root->tArgLists.size(), tagInfo,root->hidden,root->artificial);
1073       cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1074       cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1075       cd->setLanguage(root->lang);
1076       cd->setId(root->id);
1077       cd->setHidden(root->hidden);
1078       cd->setArtificial(root->artificial);
1079       cd->setClassSpecifier(root->spec);
1080       cd->setTypeConstraints(root->typeConstr);
1081       //printf("new ClassDef %s tempArgList=%p specScope=%s\n",qPrint(fullName),root->tArgList,qPrint(root->scopeSpec));
1082 
1083       //printf("class %s template args=%s\n",qPrint(fullName),
1084       //    tArgList ? tempArgListToString(tArgList,qPrint(root->lang)) : "<none>");
1085       if (tArgList)
1086       {
1087         cd->setTemplateArguments(*tArgList);
1088       }
1089       cd->setRequiresClause(root->req);
1090       cd->setProtection(root->protection);
1091       cd->setIsStatic(root->stat);
1092 
1093       // file definition containing the class cd
1094       cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1095       cd->setBodyDef(fd);
1096 
1097       cd->setMetaData(root->metaData);
1098 
1099       // see if the class is found inside a namespace
1100       //bool found=addNamespace(root,cd);
1101 
1102       cd->insertUsedFile(fd);
1103     }
1104     else
1105     {
1106       Debug::print(Debug::Classes,0,"  Not added class '%s', already exists as alias\n", qPrint(fullName));
1107     }
1108   }
1109 
1110   if (cd)
1111   {
1112     cd->addSectionsToDefinition(root->anchors);
1113     if (!root->subGrouping) cd->setSubGrouping(FALSE);
1114     if ((root->spec&Entry::ForwardDecl)==0)
1115     {
1116       if (cd->hasDocumentation())
1117       {
1118         addIncludeFile(cd,fd,root);
1119       }
1120       if (fd && (root->section & Entry::COMPOUND_MASK))
1121       {
1122         //printf(">> Inserting class '%s' in file '%s' (root->fileName='%s')\n",
1123         //    qPrint(cd->name()),
1124         //    qPrint(fd->name()),
1125         //    qPrint(root->fileName)
1126         //   );
1127         cd->setFileDef(fd);
1128         fd->insertClass(cd);
1129       }
1130     }
1131     addClassToGroups(root,cd);
1132     cd->setRefItems(root->sli);
1133   }
1134 }
1135 
1136 //----------------------------------------------------------------------
1137 // build a list of all classes mentioned in the documentation
1138 // and all classes that have a documentation block before their definition.
buildClassList(const Entry * root)1139 static void buildClassList(const Entry *root)
1140 {
1141   if (
1142         ((root->section & Entry::COMPOUND_MASK) ||
1143          root->section==Entry::OBJCIMPL_SEC) && !root->name.isEmpty()
1144      )
1145   {
1146     addClassToContext(root);
1147   }
1148   for (const auto &e : root->children()) buildClassList(e.get());
1149 }
1150 
buildClassDocList(const Entry * root)1151 static void buildClassDocList(const Entry *root)
1152 {
1153   if (
1154        (root->section & Entry::COMPOUNDDOC_MASK) && !root->name.isEmpty()
1155      )
1156   {
1157     addClassToContext(root);
1158   }
1159   for (const auto &e : root->children()) buildClassDocList(e.get());
1160 }
1161 
1162 //----------------------------------------------------------------------
1163 // build a list of all classes mentioned in the documentation
1164 // and all classes that have a documentation block before their definition.
1165 
addConceptToContext(const Entry * root)1166 static void addConceptToContext(const Entry *root)
1167 {
1168   FileDef *fd = root->fileDef();
1169 
1170   QCString scName;
1171   if (root->parent()->section&Entry::SCOPE_MASK)
1172   {
1173      scName=root->parent()->name;
1174   }
1175 
1176   // name with scope (if not present already)
1177   QCString qualifiedName = root->name;
1178   if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1179   {
1180     qualifiedName.prepend(scName+"::");
1181   }
1182 
1183   // see if we already found the concept before
1184   ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1185 
1186   Debug::print(Debug::Classes,0, "  Found concept with name %s (qualifiedName=%s -> cd=%p)\n",
1187       cd ? qPrint(cd->name()) : qPrint(root->name), qPrint(qualifiedName),cd);
1188 
1189   if (cd)
1190   {
1191     qualifiedName=cd->name();
1192     Debug::print(Debug::Classes,0,"  Existing concept %s!\n",qPrint(cd->name()));
1193 
1194     cd->setDocumentation(root->doc,root->docFile,root->docLine);
1195     cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1196 
1197     addIncludeFile(cd,fd,root);
1198   }
1199   else // new concept
1200   {
1201     QCString className;
1202     QCString namespaceName;
1203     extractNamespaceName(qualifiedName,className,namespaceName);
1204 
1205     //printf("New concept: fullname %s namespace '%s' name='%s' brief='%s' docs='%s'\n",
1206     //    qPrint(qualifiedName),qPrint(namespaceName),qPrint(className),qPrint(root->brief),qPrint(root->doc));
1207 
1208     QCString tagName;
1209     QCString refFileName;
1210     const TagInfo *tagInfo = root->tagInfo();
1211     if (tagInfo)
1212     {
1213       tagName     = tagInfo->tagName;
1214       refFileName = tagInfo->fileName;
1215       if (qualifiedName.find("::")!=-1)
1216         // symbols imported via tag files may come without the parent scope,
1217         // so we artificially create it here
1218       {
1219         buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1220       }
1221     }
1222     std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1223     // add concept to the list
1224     //printf("ClassDict.insert(%s)\n",qPrint(fullName));
1225     cd = toConceptDefMutable(
1226         Doxygen::conceptLinkedMap->add(qualifiedName,
1227           std::unique_ptr<ConceptDef>(
1228             createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1229                qualifiedName,tagName,refFileName))));
1230     if (cd)
1231     {
1232       Debug::print(Debug::Classes,0,"  New concept '%s' #tArgLists=%d tagInfo=%p\n",
1233           qPrint(qualifiedName),root->tArgLists.size(),tagInfo);
1234       cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1235       cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1236       cd->setLanguage(root->lang);
1237       cd->setId(root->id);
1238       cd->setHidden(root->hidden);
1239       cd->setGroupId(root->mGrpId);
1240       if (tArgList)
1241       {
1242         cd->setTemplateArguments(*tArgList);
1243       }
1244       cd->setInitializer(root->initializer.str().c_str());
1245       // file definition containing the class cd
1246       cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1247       cd->setBodyDef(fd);
1248       addIncludeFile(cd,fd,root);
1249 
1250       // also add namespace to the correct structural context
1251       Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,0,tagInfo);
1252       if (d && d->definitionType()==Definition::TypeNamespace)
1253       {
1254         DefinitionMutable *dm = toDefinitionMutable(d);
1255         if (dm)
1256         {
1257           dm->addInnerCompound(cd);
1258         }
1259         cd->setOuterScope(d);
1260       }
1261     }
1262     else
1263     {
1264       Debug::print(Debug::Classes,0,"  Not added concept '%s', already exists (as alias)\n", qPrint(qualifiedName));
1265     }
1266   }
1267 
1268   if (cd)
1269   {
1270     cd->addSectionsToDefinition(root->anchors);
1271     if (fd)
1272     {
1273       //printf(">> Inserting concept '%s' in file '%s' (root->fileName='%s')\n",
1274       //    qPrint(cd->name()),
1275       //    qPrint(fd->name()),
1276       //    qPrint(root->fileName)
1277       //   );
1278       cd->setFileDef(fd);
1279       fd->insertConcept(cd);
1280     }
1281     addConceptToGroups(root,cd);
1282     cd->setRefItems(root->sli);
1283   }
1284 }
buildConceptList(const Entry * root)1285 static void buildConceptList(const Entry *root)
1286 {
1287   if (root->section & Entry::CONCEPT_SEC)
1288   {
1289     addConceptToContext(root);
1290   }
1291   for (const auto &e : root->children()) buildConceptList(e.get());
1292 }
1293 
buildConceptDocList(const Entry * root)1294 static void buildConceptDocList(const Entry *root)
1295 {
1296   if (root->section & Entry::CONCEPTDOC_SEC)
1297   {
1298     addConceptToContext(root);
1299   }
1300   for (const auto &e : root->children()) buildConceptDocList(e.get());
1301 }
1302 
1303 // This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1304 // (same also works for variable and functions because of logic in MemberGroup::insertMember)
distributeConceptGroups()1305 static void distributeConceptGroups()
1306 {
1307   for (const auto &cd : *Doxygen::conceptLinkedMap)
1308   {
1309     if (cd->groupId()!=DOX_NOGROUP)
1310     {
1311       for (const auto &ocd : *Doxygen::conceptLinkedMap)
1312       {
1313         if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1314             !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1315         {
1316           ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1317           if (ocdm)
1318           {
1319             for (const auto &gd : cd->partOfGroups())
1320             {
1321               if (gd)
1322               {
1323                 ocdm->makePartOfGroup(gd);
1324                 const_cast<GroupDef*>(gd)->addConcept(ocd.get());
1325               }
1326             }
1327           }
1328         }
1329       }
1330     }
1331   }
1332 }
1333 
1334 //----------------------------------------------------------------------
1335 
resolveClassNestingRelations()1336 static void resolveClassNestingRelations()
1337 {
1338   ClassDefSet visitedClasses;
1339 
1340   bool done=FALSE;
1341   int iteration=0;
1342   while (!done)
1343   {
1344     done=TRUE;
1345     ++iteration;
1346     struct ClassAlias
1347     {
1348       ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1349         aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1350       QCString aliasFullName;
1351       std::unique_ptr<ClassDef> aliasCd;
1352       DefinitionMutable *aliasContext;
1353     };
1354     std::vector<ClassAlias> aliases;
1355     for (const auto &icd : *Doxygen::classLinkedMap)
1356     {
1357       ClassDefMutable *cd = toClassDefMutable(icd.get());
1358       if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1359       {
1360         QCString name = stripAnonymousNamespaceScope(icd->name());
1361         //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1362         // also add class to the correct structural context
1363         Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,
1364                                                  name,icd->getFileDef(),0);
1365         if (d)
1366         {
1367           //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1368           DefinitionMutable *dm = toDefinitionMutable(d);
1369           if (dm)
1370           {
1371             dm->addInnerCompound(cd);
1372           }
1373           cd->setOuterScope(d);
1374 
1375           // for inline namespace add an alias of the class to the outer scope
1376           while (d->definitionType()==Definition::TypeNamespace)
1377           {
1378             NamespaceDef *nd = toNamespaceDef(d);
1379             //printf("nd->isInline()=%d\n",nd->isInline());
1380             if (nd && nd->isInline())
1381             {
1382               d = d->getOuterScope();
1383               if (d)
1384               {
1385                 dm = toDefinitionMutable(d);
1386                 if (dm)
1387                 {
1388                   std::unique_ptr<ClassDef> aliasCd { createClassDefAlias(d,cd) };
1389                   QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1390                   aliases.push_back(ClassAlias(aliasFullName,std::move(aliasCd),dm));
1391                   //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1392                 }
1393               }
1394             }
1395             else
1396             {
1397               break;
1398             }
1399           }
1400 
1401           visitedClasses.insert(icd.get());
1402           done=FALSE;
1403         }
1404         //else
1405         //{
1406         //  printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1407         //}
1408       }
1409     }
1410     // add aliases
1411     for (auto &alias : aliases)
1412     {
1413        ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1414        if (aliasCd)
1415        {
1416          alias.aliasContext->addInnerCompound(aliasCd);
1417        }
1418     }
1419   }
1420 
1421   //give warnings for unresolved compounds
1422   for (const auto &icd : *Doxygen::classLinkedMap)
1423   {
1424     ClassDefMutable *cd = toClassDefMutable(icd.get());
1425     if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1426     {
1427       QCString name = stripAnonymousNamespaceScope(cd->name());
1428       //printf("processing unresolved=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1429       /// create the scope artificially
1430       // anyway, so we can at least relate scopes properly.
1431       Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),0);
1432       if (d && d!=cd && !cd->getDefFileName().isEmpty())
1433                  // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1434                  // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1435                  // also avoid warning for stuff imported via a tagfile.
1436       {
1437         DefinitionMutable *dm = toDefinitionMutable(d);
1438         if (dm)
1439         {
1440           dm->addInnerCompound(cd);
1441         }
1442         cd->setOuterScope(d);
1443         warn(cd->getDefFileName(),cd->getDefLine(),
1444             "Internal inconsistency: scope for class %s not "
1445             "found!",qPrint(name)
1446             );
1447       }
1448     }
1449   }
1450 }
1451 
distributeClassGroupRelations()1452 void distributeClassGroupRelations()
1453 {
1454   //static bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1455   //if (!inlineGroupedClasses) return;
1456   //printf("** distributeClassGroupRelations()\n");
1457 
1458   ClassDefSet visitedClasses;
1459   for (const auto &cd : *Doxygen::classLinkedMap)
1460   {
1461     //printf("Checking %s\n",qPrint(cd->name()));
1462     // distribute the group to nested classes as well
1463     if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1464     {
1465       //printf("  Candidate for merging\n");
1466       const GroupDef *gd = cd->partOfGroups().front();
1467       for (const auto &ncd : cd->getClasses())
1468       {
1469         ClassDefMutable *ncdm = toClassDefMutable(ncd);
1470         if (ncdm && ncdm->partOfGroups().empty())
1471         {
1472           //printf("  Adding %s to group '%s'\n",qPrint(ncd->name()),
1473           //    gd->groupTitle());
1474           ncdm->makePartOfGroup(gd);
1475           const_cast<GroupDef*>(gd)->addClass(ncdm);
1476         }
1477       }
1478       visitedClasses.insert(cd.get()); // only visit every class once
1479     }
1480   }
1481 }
1482 
1483 //----------------------------
1484 
createTagLessInstance(const ClassDef * rootCd,const ClassDef * templ,const QCString & fieldName)1485 static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1486 {
1487   QCString fullName = removeAnonymousScopes(templ->name());
1488   if (fullName.right(2)=="::") fullName=fullName.left(fullName.length()-2);
1489   fullName+="."+fieldName;
1490 
1491   //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1492   ClassDefMutable *cd = toClassDefMutable(
1493       Doxygen::classLinkedMap->add(fullName,
1494          std::unique_ptr<ClassDef>(
1495            createClassDef(templ->getDefFileName(),
1496                               templ->getDefLine(),
1497                               templ->getDefColumn(),
1498                               fullName,
1499                               templ->compoundType()))));
1500   if (cd)
1501   {
1502     cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1503     cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1504     cd->setLanguage(templ->getLanguage());
1505     cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1506     cd->setBodyDef(templ->getBodyDef());
1507 
1508     cd->setOuterScope(rootCd->getOuterScope());
1509     if (rootCd->getOuterScope()!=Doxygen::globalScope)
1510     {
1511       DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1512       if (outerScope)
1513       {
1514         outerScope->addInnerCompound(cd);
1515       }
1516     }
1517 
1518     FileDef *fd = templ->getFileDef();
1519     if (fd)
1520     {
1521       cd->setFileDef(fd);
1522       fd->insertClass(cd);
1523     }
1524     for (const auto &gd : rootCd->partOfGroups())
1525     {
1526       cd->makePartOfGroup(gd);
1527       const_cast<GroupDef*>(gd)->addClass(cd);
1528     }
1529 
1530     MemberList *ml = templ->getMemberList(MemberListType_pubAttribs);
1531     if (ml)
1532     {
1533       for (const auto &md : *ml)
1534       {
1535         //printf("    Member %s type=%s\n",qPrint(md->name()),md->typeString());
1536         MemberDefMutable *imd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1537             md->typeString(),md->name(),md->argsString(),md->excpString(),
1538             md->protection(),md->virtualness(),md->isStatic(),Member,
1539             md->memberType(),
1540             ArgumentList(),ArgumentList(),"");
1541         imd->setMemberClass(cd);
1542         imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1543         imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1544         imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1545         imd->setMemberSpecifiers(md->getMemberSpecifiers());
1546         imd->setMemberGroupId(md->getMemberGroupId());
1547         imd->setInitializer(md->initializer());
1548         imd->setRequiresClause(md->requiresClause());
1549         imd->setMaxInitLines(md->initializerLines());
1550         imd->setBitfields(md->bitfieldString());
1551         imd->setLanguage(md->getLanguage());
1552         cd->insertMember(imd);
1553       }
1554     }
1555   }
1556   return cd;
1557 }
1558 
1559 /** Look through the members of class \a cd and its public members.
1560  *  If there is a member m of a tag less struct/union,
1561  *  then we create a duplicate of the struct/union with the name of the
1562  *  member to identify it.
1563  *  So if cd has name S, then the tag less struct/union will get name S.m
1564  *  Since tag less structs can be nested we need to call this function
1565  *  recursively. Later on we need to patch the member types so we keep
1566  *  track of the hierarchy of classes we create.
1567  */
processTagLessClasses(const ClassDef * rootCd,const ClassDef * cd,ClassDefMutable * tagParentCd,const QCString & prefix,int count)1568 static void processTagLessClasses(const ClassDef *rootCd,
1569                                   const ClassDef *cd,
1570                                   ClassDefMutable *tagParentCd,
1571                                   const QCString &prefix,int count)
1572 {
1573   //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1574   //printf("checking members for %s\n",qPrint(cd->name()));
1575   if (tagParentCd && !cd->getClasses().empty())
1576   {
1577     MemberList *ml = cd->getMemberList(MemberListType_pubAttribs);
1578     if (ml)
1579     {
1580       for (const auto &md : *ml)
1581       {
1582         QCString type = md->typeString();
1583         if (type.find("::@")!=-1) // member of tag less struct/union
1584         {
1585           for (const auto &icd : cd->getClasses())
1586           {
1587             //printf("  member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1588             //printf("  comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1589             if (type.find(icd->name())!=-1) // matching tag less struct/union
1590             {
1591               QCString name = md->name();
1592               if (md->isAnonymous()) name = "__unnamed" + name.right(name.length()-1)+"__";
1593               if (!prefix.isEmpty()) name.prepend(prefix+".");
1594               //printf("    found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1595               ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1596               if (ncd)
1597               {
1598                 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1599                 //printf("    addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1600                 ncd->setTagLessReference(icd);
1601 
1602                 // replace tag-less type for generated/original member
1603                 // by newly created class name.
1604                 // note the difference between changing cd and tagParentCd.
1605                 // for the initial call this is the same pointer, but for
1606                 // recursive calls cd is the original tag-less struct (of which
1607                 // there is only one instance) and tagParentCd is the newly
1608                 // generated tagged struct of which there can be multiple instances!
1609                 MemberList *pml = tagParentCd->getMemberList(MemberListType_pubAttribs);
1610                 if (pml)
1611                 {
1612                   for (const auto &pmd : *pml)
1613                   {
1614                     MemberDefMutable *pmdm = toMemberDefMutable(pmd);
1615                     if (pmdm && pmd->name()==md->name())
1616                     {
1617                       pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1618                       //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1619                     }
1620                   }
1621                 }
1622               }
1623             }
1624           }
1625         }
1626       }
1627     }
1628   }
1629 }
1630 
findTagLessClasses(std::vector<ClassDefMutable * > & candidates,const ClassDef * cd)1631 static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,const ClassDef *cd)
1632 {
1633   for (const auto &icd : cd->getClasses())
1634   {
1635     if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1636     {
1637       findTagLessClasses(candidates,icd);
1638     }
1639   }
1640 
1641   ClassDefMutable *cdm = toClassDefMutable(cd);
1642   if (cdm)
1643   {
1644     candidates.push_back(cdm);
1645   }
1646 }
1647 
findTagLessClasses()1648 static void findTagLessClasses()
1649 {
1650   std::vector<ClassDefMutable *> candidates;
1651   for (const auto &cd : *Doxygen::classLinkedMap)
1652   {
1653     Definition *scope = cd->getOuterScope();
1654     if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1655     {
1656       findTagLessClasses(candidates,cd.get());
1657     }
1658   }
1659 
1660   // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1661   // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1662   for (auto &cd : candidates)
1663   {
1664     processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1665   }
1666 }
1667 
1668 
1669 //----------------------------------------------------------------------
1670 // build a list of all namespaces mentioned in the documentation
1671 // and all namespaces that have a documentation block before their definition.
buildNamespaceList(const Entry * root)1672 static void buildNamespaceList(const Entry *root)
1673 {
1674   if (
1675        (root->section==Entry::NAMESPACE_SEC ||
1676         root->section==Entry::NAMESPACEDOC_SEC ||
1677         root->section==Entry::PACKAGEDOC_SEC
1678        ) &&
1679        !root->name.isEmpty()
1680      )
1681   {
1682     //printf("** buildNamespaceList(%s)\n",qPrint(root->name));
1683 
1684     QCString fName = root->name;
1685     if (root->section==Entry::PACKAGEDOC_SEC)
1686     {
1687       fName=substitute(fName,".","::");
1688     }
1689 
1690     QCString fullName = stripAnonymousNamespaceScope(fName);
1691     if (!fullName.isEmpty())
1692     {
1693       //printf("Found namespace %s in %s at line %d\n",qPrint(root->name),
1694       //        qPrint(root->fileName), root->startLine);
1695       NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1696       if (ndi) // existing namespace
1697       {
1698         NamespaceDefMutable *nd = toNamespaceDefMutable(ndi);
1699         if (nd) // non-inline namespace
1700         {
1701           nd->setDocumentation(root->doc,root->docFile,root->docLine);
1702           nd->setName(fullName); // change name to match docs
1703           nd->addSectionsToDefinition(root->anchors);
1704           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1705           if (nd->getLanguage()==SrcLangExt_Unknown)
1706           {
1707             nd->setLanguage(root->lang);
1708           }
1709           if (root->tagInfo()==0) // if we found the namespace in a tag file
1710             // and also in a project file, then remove
1711             // the tag file reference
1712           {
1713             nd->setReference("");
1714             nd->setFileName(fullName);
1715           }
1716           nd->setMetaData(root->metaData);
1717 
1718           // file definition containing the namespace nd
1719           FileDef *fd=root->fileDef();
1720           if (nd->isArtificial())
1721           {
1722             nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1723             nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1724           }
1725           // insert the namespace in the file definition
1726           if (fd) fd->insertNamespace(nd);
1727           addNamespaceToGroups(root,nd);
1728           nd->setRefItems(root->sli);
1729         }
1730       }
1731       else // fresh namespace
1732       {
1733         QCString tagName;
1734         QCString tagFileName;
1735         const TagInfo *tagInfo = root->tagInfo();
1736         if (tagInfo)
1737         {
1738           tagName     = tagInfo->tagName;
1739           tagFileName = tagInfo->fileName;
1740         }
1741         //printf("++ new namespace %s lang=%s tagName=%s\n",qPrint(fullName),qPrint(langToString(root->lang)),qPrint(tagName));
1742         // add namespace to the list
1743         NamespaceDefMutable *nd = toNamespaceDefMutable(
1744             Doxygen::namespaceLinkedMap->add(fullName,
1745               std::unique_ptr<NamespaceDef>(
1746                 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1747                   root->startColumn,fullName,tagName,tagFileName,
1748                   root->type,root->spec&Entry::Published))));
1749         if (nd)
1750         {
1751           nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1752           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1753           nd->addSectionsToDefinition(root->anchors);
1754           nd->setHidden(root->hidden);
1755           nd->setArtificial(root->artificial);
1756           nd->setLanguage(root->lang);
1757           nd->setId(root->id);
1758           nd->setMetaData(root->metaData);
1759           nd->setInline((root->spec&Entry::Inline)!=0);
1760 
1761           //printf("Adding namespace to group\n");
1762           addNamespaceToGroups(root,nd);
1763           nd->setRefItems(root->sli);
1764 
1765           // file definition containing the namespace nd
1766           FileDef *fd=root->fileDef();
1767           // insert the namespace in the file definition
1768           if (fd) fd->insertNamespace(nd);
1769 
1770           // the empty string test is needed for extract all case
1771           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1772           nd->insertUsedFile(fd);
1773           nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1774           nd->setBodyDef(fd);
1775 
1776           // also add namespace to the correct structural context
1777           Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,0,tagInfo);
1778           //printf("adding namespace %s to context %s\n",qPrint(nd->name()),d?qPrint(d->name()):"<none>");
1779           if (d==0) // we didn't find anything, create the scope artificially
1780             // anyway, so we can at least relate scopes properly.
1781           {
1782             d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1783             DefinitionMutable *dm = toDefinitionMutable(d);
1784             if (dm)
1785             {
1786               dm->addInnerCompound(nd);
1787             }
1788             nd->setOuterScope(d);
1789             // TODO: Due to the order in which the tag file is written
1790             // a nested class can be found before its parent!
1791           }
1792           else
1793           {
1794             DefinitionMutable *dm = toDefinitionMutable(d);
1795             if (dm)
1796             {
1797               dm->addInnerCompound(nd);
1798             }
1799             nd->setOuterScope(d);
1800             // in case of d is an inline namespace, alias insert nd in the part scope of d.
1801             while (d->definitionType()==Definition::TypeNamespace)
1802             {
1803               NamespaceDef *pnd = toNamespaceDef(d);
1804               if (pnd && pnd->isInline())
1805               {
1806                 d = d->getOuterScope();
1807                 if (d)
1808                 {
1809                   dm = toDefinitionMutable(d);
1810                   if (dm)
1811                   {
1812                     NamespaceDef *aliasNd = createNamespaceDefAlias(d,nd);
1813                     dm->addInnerCompound(aliasNd);
1814                     QCString aliasName = aliasNd->name();
1815                     //printf("adding alias %s (%p) to %s\n",qPrint(aliasName),aliasNd,qPrint(d->name()));
1816                     Doxygen::namespaceLinkedMap->add(
1817                         aliasName,std::unique_ptr<NamespaceDef>(aliasNd));
1818                   }
1819                 }
1820                 else
1821                 {
1822                   break;
1823                 }
1824               }
1825               else
1826               {
1827                 break;
1828               }
1829             }
1830           }
1831         }
1832       }
1833     }
1834   }
1835   for (const auto &e : root->children()) buildNamespaceList(e.get());
1836 }
1837 
1838 //----------------------------------------------------------------------
1839 
findUsedNamespace(const LinkedRefMap<const NamespaceDef> & unl,const QCString & name)1840 static const NamespaceDef *findUsedNamespace(const LinkedRefMap<const NamespaceDef> &unl,
1841                               const QCString &name)
1842 {
1843   const NamespaceDef *usingNd =0;
1844   for (const auto &und : unl)
1845   {
1846     QCString uScope=und->name()+"::";
1847     usingNd = getResolvedNamespace(uScope+name);
1848     if (usingNd!=0) break;
1849   }
1850   return usingNd;
1851 }
1852 
findUsingDirectives(const Entry * root)1853 static void findUsingDirectives(const Entry *root)
1854 {
1855   if (root->section==Entry::USINGDIR_SEC)
1856   {
1857     //printf("Found using directive %s at line %d of %s\n",
1858     //    qPrint(root->name),root->startLine,qPrint(root->fileName));
1859     QCString name=substitute(root->name,".","::");
1860     if (name.right(2)=="::")
1861     {
1862       name=name.left(name.length()-2);
1863     }
1864     if (!name.isEmpty())
1865     {
1866       const NamespaceDef *usingNd = 0;
1867       NamespaceDefMutable *nd = 0;
1868       FileDef      *fd = root->fileDef();
1869       QCString nsName;
1870 
1871       // see if the using statement was found inside a namespace or inside
1872       // the global file scope.
1873       if (root->parent() && root->parent()->section==Entry::NAMESPACE_SEC &&
1874           (fd==0 || fd->getLanguage()!=SrcLangExt_Java) // not a .java file
1875          )
1876       {
1877         nsName=stripAnonymousNamespaceScope(root->parent()->name);
1878         if (!nsName.isEmpty())
1879         {
1880           nd = getResolvedNamespaceMutable(nsName);
1881         }
1882       }
1883 
1884       // find the scope in which the 'using' namespace is defined by prepending
1885       // the possible scopes in which the using statement was found, starting
1886       // with the most inner scope and going to the most outer scope (i.e.
1887       // file scope).
1888       int scopeOffset = nsName.length();
1889       do
1890       {
1891         QCString scope=scopeOffset>0 ?
1892                       nsName.left(scopeOffset)+"::" : QCString();
1893         usingNd = getResolvedNamespace(scope+name);
1894         //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1895         if (scopeOffset==0)
1896         {
1897           scopeOffset=-1;
1898         }
1899         else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1900         {
1901           scopeOffset=0;
1902         }
1903       } while (scopeOffset>=0 && usingNd==0);
1904 
1905       if (usingNd==0 && nd) // not found, try used namespaces in this scope
1906                             // or in one of the parent namespace scopes
1907       {
1908         const NamespaceDefMutable *pnd = nd;
1909         while (pnd && usingNd==0)
1910         {
1911           // also try with one of the used namespaces found earlier
1912           usingNd = toNamespaceDefMutable(findUsedNamespace(pnd->getUsedNamespaces(),name));
1913 
1914           // goto the parent
1915           const Definition *s = pnd->getOuterScope();
1916           if (s && s->definitionType()==Definition::TypeNamespace)
1917           {
1918             pnd = toNamespaceDefMutable(toNamespaceDef(s));
1919           }
1920           else
1921           {
1922             pnd = 0;
1923           }
1924         }
1925       }
1926       if (usingNd==0 && fd) // still nothing, also try used namespace in the
1927                             // global scope
1928       {
1929         usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1930       }
1931 
1932       //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1933 
1934       // add the namespace the correct scope
1935       if (usingNd)
1936       {
1937         //printf("using fd=%p nd=%p\n",fd,nd);
1938         if (nd)
1939         {
1940           //printf("Inside namespace %s\n",qPrint(nd->name()));
1941           nd->addUsingDirective(usingNd);
1942         }
1943         else if (fd)
1944         {
1945           //printf("Inside file %s\n",qPrint(fd->name()));
1946           fd->addUsingDirective(usingNd);
1947         }
1948       }
1949       else // unknown namespace, but add it anyway.
1950       {
1951         //printf("++ new unknown namespace %s lang=%s\n",qPrint(name),qPrint(langToString(root->lang)));
1952         // add namespace to the list
1953         nd = toNamespaceDefMutable(
1954             Doxygen::namespaceLinkedMap->add(name,
1955               std::unique_ptr<NamespaceDef>(
1956                  createNamespaceDef(root->fileName,root->startLine,root->startColumn,name))));
1957         if (nd)
1958         {
1959           nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1960           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1961           nd->addSectionsToDefinition(root->anchors);
1962           //printf("** Adding namespace %s hidden=%d\n",qPrint(name),root->hidden);
1963           nd->setHidden(root->hidden);
1964           nd->setArtificial(TRUE);
1965           nd->setLanguage(root->lang);
1966           nd->setId(root->id);
1967           nd->setMetaData(root->metaData);
1968           nd->setInline((root->spec&Entry::Inline)!=0);
1969 
1970           for (const Grouping &g : root->groups)
1971           {
1972             GroupDef *gd=0;
1973             if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1974               gd->addNamespace(nd);
1975           }
1976 
1977           // insert the namespace in the file definition
1978           if (fd)
1979           {
1980             fd->insertNamespace(nd);
1981             fd->addUsingDirective(nd);
1982           }
1983 
1984           // the empty string test is needed for extract all case
1985           nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1986           nd->insertUsedFile(fd);
1987           nd->setRefItems(root->sli);
1988         }
1989       }
1990     }
1991   }
1992   for (const auto &e : root->children()) findUsingDirectives(e.get());
1993 }
1994 
1995 //----------------------------------------------------------------------
1996 
buildListOfUsingDecls(const Entry * root)1997 static void buildListOfUsingDecls(const Entry *root)
1998 {
1999   if (root->section==Entry::USINGDECL_SEC &&
2000       !(root->parent()->section&Entry::COMPOUND_MASK) // not a class/struct member
2001      )
2002   {
2003     QCString name = substitute(root->name,".","::");
2004     g_usingDeclarations.insert(name.str());
2005   }
2006   for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2007 }
2008 
2009 
findUsingDeclarations(const Entry * root,bool filterPythonPackages)2010 static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2011 {
2012   if (root->section==Entry::USINGDECL_SEC &&
2013       !(root->parent()->section&Entry::COMPOUND_MASK) && // not a class/struct member
2014       (!filterPythonPackages || (root->lang==SrcLangExt_Python && root->fileName.endsWith("__init__.py")))
2015      )
2016   {
2017     //printf("Found using declaration %s at line %d of %s inside section %x\n",
2018     //   qPrint(root->name),root->startLine,qPrint(root->fileName),
2019     //   root->parent()->section);
2020     if (!root->name.isEmpty())
2021     {
2022       ClassDefMutable *usingCd = 0;
2023       NamespaceDefMutable *nd = 0;
2024       FileDef      *fd = root->fileDef();
2025       QCString scName;
2026 
2027       // see if the using statement was found inside a namespace or inside
2028       // the global file scope.
2029       if (root->parent()->section == Entry::NAMESPACE_SEC)
2030       {
2031         scName=root->parent()->name;
2032         if (!scName.isEmpty())
2033         {
2034           nd = getResolvedNamespaceMutable(scName);
2035         }
2036       }
2037 
2038       // Assume the using statement was used to import a class.
2039       // Find the scope in which the 'using' namespace is defined by prepending
2040       // the possible scopes in which the using statement was found, starting
2041       // with the most inner scope and going to the most outer scope (i.e.
2042       // file scope).
2043 
2044       QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2045       usingCd = getClassMutable(name); // try direct lookup first, this is needed to get
2046                                 // builtin STL classes to properly resolve, e.g.
2047                                 // vector -> std::vector
2048       if (usingCd==0)
2049       {
2050         SymbolResolver resolver(fd);
2051         usingCd = resolver.resolveClassMutable(nd,name); // try via resolving (see also bug757509)
2052       }
2053       if (usingCd==0)
2054       {
2055         usingCd = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(name)); // check if it is already hidden
2056       }
2057 
2058       //printf("%s -> %p\n",qPrint(root->name),(void*)usingCd);
2059       if (usingCd==0) // definition not in the input => add an artificial class
2060       {
2061         Debug::print(Debug::Classes,0,"  New using class '%s' (sec=0x%08x)! #tArgLists=%d\n",
2062              qPrint(name),root->section,root->tArgLists.size());
2063         usingCd = toClassDefMutable(
2064              Doxygen::hiddenClassLinkedMap->add(name,
2065                std::unique_ptr<ClassDef>(
2066                  createClassDef( "<using>",1,1, name, ClassDef::Class))));
2067         if (usingCd)
2068         {
2069           usingCd->setArtificial(TRUE);
2070           usingCd->setLanguage(root->lang);
2071         }
2072       }
2073       else
2074       {
2075         Debug::print(Debug::Classes,0,"  Found used class %s in scope=%s\n",
2076             qPrint(usingCd->name()),
2077                         nd?qPrint(nd->name()):
2078                         fd?qPrint(fd->name()):
2079                         "<unknown>");
2080       }
2081 
2082       if (nd)
2083       {
2084         //printf("Inside namespace %s\n",qPrint(nd->name()));
2085         nd->addUsingDeclaration(usingCd);
2086       }
2087       else if (fd)
2088       {
2089         //printf("Inside file %s\n",qPrint(fd->name()));
2090         fd->addUsingDeclaration(usingCd);
2091       }
2092     }
2093   }
2094   for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2095 }
2096 
2097 //----------------------------------------------------------------------
2098 
findUsingDeclImports(const Entry * root)2099 static void findUsingDeclImports(const Entry *root)
2100 {
2101   if (root->section==Entry::USINGDECL_SEC &&
2102       (root->parent()->section&Entry::COMPOUND_MASK) // in a class/struct member
2103      )
2104   {
2105     //printf("Found using declaration %s inside section %x\n",
2106     //    qPrint(root->name), root->parent()->section);
2107     QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2108     fullName=stripAnonymousNamespaceScope(fullName);
2109     fullName=stripTemplateSpecifiersFromScope(fullName);
2110     ClassDefMutable *cd = getClassMutable(fullName);
2111     if (cd)
2112     {
2113       //printf("found class %s\n",qPrint(cd->name()));
2114       int i=root->name.find("::");
2115       if (i!=-1)
2116       {
2117         QCString scope=root->name.left(i);
2118         QCString memName=root->name.right(root->name.length()-i-2);
2119         SymbolResolver resolver;
2120         const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2121         if (bcd && bcd!=cd)
2122         {
2123           //printf("found class %s memName=%s\n",qPrint(bcd->name()),qPrint(memName));
2124           const MemberNameInfoLinkedMap &mnlm=bcd->memberNameInfoLinkedMap();
2125           const MemberNameInfo *mni = mnlm.find(memName);
2126           if (mni)
2127           {
2128             for (auto &mi : *mni)
2129             {
2130               const MemberDef *md = mi->memberDef();
2131               if (md && md->protection()!=Private)
2132               {
2133                 //printf("found member %s\n",mni->memberName());
2134                 QCString fileName = root->fileName;
2135                 if (fileName.isEmpty() && root->tagInfo())
2136                 {
2137                   fileName = root->tagInfo()->tagName;
2138                 }
2139                 const ArgumentList &templAl = md->templateArguments();
2140                 const ArgumentList &al = md->argumentList();
2141                 std::unique_ptr<MemberDefMutable> newMd { createMemberDef(
2142                     fileName,root->startLine,root->startColumn,
2143                     md->typeString(),memName,md->argsString(),
2144                     md->excpString(),root->protection,root->virt,
2145                     md->isStatic(),Member,md->memberType(),
2146                     templAl,al,root->metaData
2147                     ) };
2148                 newMd->setMemberClass(cd);
2149                 cd->insertMember(newMd.get());
2150                 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2151                 {
2152                   newMd->setDocumentation(root->doc,root->docFile,root->docLine);
2153                   newMd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2154                   newMd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2155                 }
2156                 else
2157                 {
2158                   newMd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2159                   newMd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2160                   newMd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2161                 }
2162                 newMd->setDefinition(md->definition());
2163                 newMd->enableCallGraph(root->callGraph);
2164                 newMd->enableCallerGraph(root->callerGraph);
2165                 newMd->enableReferencedByRelation(root->referencedByRelation);
2166                 newMd->enableReferencesRelation(root->referencesRelation);
2167                 newMd->setBitfields(md->bitfieldString());
2168                 newMd->addSectionsToDefinition(root->anchors);
2169                 newMd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2170                 newMd->setBodyDef(md->getBodyDef());
2171                 newMd->setInitializer(md->initializer());
2172                 newMd->setRequiresClause(md->requiresClause());
2173                 newMd->setMaxInitLines(md->initializerLines());
2174                 newMd->setMemberGroupId(root->mGrpId);
2175                 newMd->setMemberSpecifiers(md->getMemberSpecifiers());
2176                 newMd->setLanguage(root->lang);
2177                 newMd->setId(root->id);
2178                 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2179                 mn->push_back(std::move(newMd));
2180               }
2181             }
2182           }
2183         }
2184       }
2185     }
2186 
2187   }
2188   for (const auto &e : root->children()) findUsingDeclImports(e.get());
2189 }
2190 
2191 //----------------------------------------------------------------------
2192 
findIncludedUsingDirectives()2193 static void findIncludedUsingDirectives()
2194 {
2195   FileDefSet visitedFiles;
2196   // then recursively add using directives found in #include files
2197   // to files that have not been visited.
2198   for (const auto &fn : *Doxygen::inputNameLinkedMap)
2199   {
2200     for (const auto &fd : *fn)
2201     {
2202       //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2203       fd->addIncludedUsingDirectives(visitedFiles);
2204     }
2205   }
2206 }
2207 
2208 //----------------------------------------------------------------------
2209 
addVariableToClass(const Entry * root,ClassDefMutable * cd,MemberType mtype,const QCString & type,const QCString & name,const QCString & args,bool fromAnnScope,MemberDef * fromAnnMemb,Protection prot,Relationship related)2210 static MemberDef *addVariableToClass(
2211     const Entry *root,
2212     ClassDefMutable *cd,
2213     MemberType mtype,
2214     const QCString &type,
2215     const QCString &name,
2216     const QCString &args,
2217     bool fromAnnScope,
2218     MemberDef *fromAnnMemb,
2219     Protection prot,
2220     Relationship related)
2221 {
2222   QCString qualScope = cd->qualifiedNameWithTemplateParameters();
2223   QCString scopeSeparator="::";
2224   SrcLangExt lang = cd->getLanguage();
2225   if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2226   {
2227     qualScope = substitute(qualScope,"::",".");
2228     scopeSeparator=".";
2229   }
2230   Debug::print(Debug::Variables,0,
2231       "  class variable:\n"
2232       "    '%s' '%s'::'%s' '%s' prot=%d ann=%d init='%s'\n",
2233       qPrint(type),
2234       qPrint(qualScope),
2235       qPrint(name),
2236       qPrint(args),
2237       root->protection,
2238       fromAnnScope,
2239       qPrint(root->initializer.str())
2240               );
2241 
2242   QCString def;
2243   if (!type.isEmpty())
2244   {
2245     if (related || mtype==MemberType_Friend || Config_getBool(HIDE_SCOPE_NAMES))
2246     {
2247       if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2248       {
2249         def="using "+name+" = "+type.mid(7);
2250       }
2251       else
2252       {
2253         def=type+" "+name+args;
2254       }
2255     }
2256     else
2257     {
2258       if (root->spec&Entry::Alias) // turn 'typedef B C::A' into 'using C::A = B'
2259       {
2260         def="using "+qualScope+scopeSeparator+name+" = "+type.mid(7);
2261       }
2262       else
2263       {
2264         def=type+" "+qualScope+scopeSeparator+name+args;
2265       }
2266     }
2267   }
2268   else
2269   {
2270     if (Config_getBool(HIDE_SCOPE_NAMES))
2271     {
2272       def=name+args;
2273     }
2274     else
2275     {
2276       def=qualScope+scopeSeparator+name+args;
2277     }
2278   }
2279   def.stripPrefix("static ");
2280 
2281   // see if the member is already found in the same scope
2282   // (this may be the case for a static member that is initialized
2283   //  outside the class)
2284   MemberName *mn=Doxygen::memberNameLinkedMap->find(name);
2285   if (mn)
2286   {
2287     for (const auto &imd : *mn)
2288     {
2289       //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2290       //    md->getClassDef(),cd,qPrint(type),md->typeString());
2291       MemberDefMutable *md = toMemberDefMutable(imd.get());
2292       if (md &&
2293           md->getClassDef()==cd &&
2294           removeRedundantWhiteSpace(type)==md->typeString())
2295         // member already in the scope
2296       {
2297 
2298         if (root->lang==SrcLangExt_ObjC &&
2299             root->mtype==Property &&
2300             md->memberType()==MemberType_Variable)
2301         { // Objective-C 2.0 property
2302           // turn variable into a property
2303           md->setProtection(root->protection);
2304           cd->reclassifyMember(md,MemberType_Property);
2305         }
2306         addMemberDocs(root,md,def,0,FALSE,root->spec);
2307         //printf("    Member already found!\n");
2308         return md;
2309       }
2310     }
2311   }
2312 
2313   QCString fileName = root->fileName;
2314   if (fileName.isEmpty() && root->tagInfo())
2315   {
2316     fileName = root->tagInfo()->tagName;
2317   }
2318 
2319   // new member variable, typedef or enum value
2320   std::unique_ptr<MemberDefMutable> md { createMemberDef(
2321       fileName,root->startLine,root->startColumn,
2322       type,name,args,root->exception,
2323       prot,Normal,root->stat,related,
2324       mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2325       ArgumentList(), root->metaData) };
2326   md->setTagInfo(root->tagInfo());
2327   md->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2328   md->setDocumentation(root->doc,root->docFile,root->docLine);
2329   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2330   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2331   md->setDefinition(def);
2332   md->setBitfields(root->bitfields);
2333   md->addSectionsToDefinition(root->anchors);
2334   md->setFromAnonymousScope(fromAnnScope);
2335   md->setFromAnonymousMember(fromAnnMemb);
2336   //md->setIndentDepth(indentDepth);
2337   md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2338   std::string init = root->initializer.str();
2339   md->setInitializer(init.c_str());
2340   md->setMaxInitLines(root->initLines);
2341   md->setMemberGroupId(root->mGrpId);
2342   md->setMemberSpecifiers(root->spec);
2343   md->setReadAccessor(root->read);
2344   md->setWriteAccessor(root->write);
2345   md->enableCallGraph(root->callGraph);
2346   md->enableCallerGraph(root->callerGraph);
2347   md->enableReferencedByRelation(root->referencedByRelation);
2348   md->enableReferencesRelation(root->referencesRelation);
2349   md->setHidden(root->hidden);
2350   md->setArtificial(root->artificial);
2351   md->setLanguage(root->lang);
2352   md->setId(root->id);
2353   addMemberToGroups(root,md.get());
2354   md->setBodyDef(root->fileDef());
2355 
2356   //printf("    New member adding to %s (%p)!\n",qPrint(cd->name()),cd);
2357   cd->insertMember(md.get());
2358   md->setRefItems(root->sli);
2359 
2360   //TODO: insert FileDef instead of filename strings.
2361   cd->insertUsedFile(root->fileDef());
2362   root->markAsProcessed();
2363 
2364   //printf("    Adding member=%s\n",qPrint(md->name()));
2365   // add the member to the global list
2366   MemberDef *result = md.get();
2367   mn = Doxygen::memberNameLinkedMap->add(name);
2368   mn->push_back(std::move(md));
2369 
2370   return result;
2371 }
2372 
2373 //----------------------------------------------------------------------
2374 
addVariableToFile(const Entry * root,MemberType mtype,const QCString & scope,const QCString & type,const QCString & name,const QCString & args,bool fromAnnScope,MemberDef * fromAnnMemb)2375 static MemberDef *addVariableToFile(
2376     const Entry *root,
2377     MemberType mtype,
2378     const QCString &scope,
2379     const QCString &type,
2380     const QCString &name,
2381     const QCString &args,
2382     bool fromAnnScope,
2383     /*int indentDepth,*/
2384     MemberDef *fromAnnMemb)
2385 {
2386   Debug::print(Debug::Variables,0,
2387       "  global variable:\n"
2388       "    file='%s' type='%s' scope='%s' name='%s' args='%s' prot=`%d mtype=%d lang=%d\n",
2389       qPrint(root->fileName),
2390       qPrint(type),
2391       qPrint(scope),
2392       qPrint(name),
2393       qPrint(args),
2394       root->protection,
2395       mtype,
2396       root->lang
2397               );
2398 
2399   FileDef *fd = root->fileDef();
2400 
2401   // see if we have a typedef that should hide a struct or union
2402   if (mtype==MemberType_Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2403   {
2404     QCString ttype = type;
2405     ttype.stripPrefix("typedef ");
2406     if (ttype.left(7)=="struct " || ttype.left(6)=="union ")
2407     {
2408       ttype.stripPrefix("struct ");
2409       ttype.stripPrefix("union ");
2410       static const reg::Ex re(R"(\a\w*)");
2411       reg::Match match;
2412       std::string typ = ttype.str();
2413       if (reg::search(typ,match,re))
2414       {
2415         QCString typeValue = match.str();
2416         ClassDefMutable *cd = getClassMutable(typeValue);
2417         if (cd)
2418         {
2419           // this typedef should hide compound name cd, so we
2420           // change the name that is displayed from cd.
2421           cd->setClassName(name);
2422           cd->setDocumentation(root->doc,root->docFile,root->docLine);
2423           cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2424           return 0;
2425         }
2426       }
2427     }
2428   }
2429 
2430   // see if the function is inside a namespace
2431   NamespaceDefMutable *nd = 0;
2432   if (!scope.isEmpty())
2433   {
2434     if (scope.find('@')!=-1) return 0; // anonymous scope!
2435     nd = getResolvedNamespaceMutable(scope);
2436   }
2437   QCString def;
2438 
2439   // determine the definition of the global variable
2440   if (nd && !nd->isAnonymous() &&
2441       !Config_getBool(HIDE_SCOPE_NAMES)
2442      )
2443     // variable is inside a namespace, so put the scope before the name
2444   {
2445     SrcLangExt lang = nd->getLanguage();
2446     QCString sep=getLanguageSpecificSeparator(lang);
2447 
2448     if (!type.isEmpty())
2449     {
2450       if (root->spec&Entry::Alias) // turn 'typedef B NS::A' into 'using NS::A = B'
2451       {
2452         def="using "+nd->name()+sep+name+" = "+type;
2453       }
2454       else // normal member
2455       {
2456         def=type+" "+nd->name()+sep+name+args;
2457       }
2458     }
2459     else
2460     {
2461       def=nd->name()+sep+name+args;
2462     }
2463   }
2464   else
2465   {
2466     if (!type.isEmpty() && !root->name.isEmpty())
2467     {
2468       if (name.at(0)=='@') // dummy variable representing anonymous union
2469       {
2470         def=type;
2471       }
2472       else
2473       {
2474         if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2475         {
2476           def="using "+root->name+" = "+type.mid(7);
2477         }
2478         else // normal member
2479         {
2480           def=type+" "+name+args;
2481         }
2482       }
2483     }
2484     else
2485     {
2486       def=name+args;
2487     }
2488   }
2489   def.stripPrefix("static ");
2490 
2491   MemberName *mn=Doxygen::functionNameLinkedMap->find(name);
2492   if (mn)
2493   {
2494     //QCString nscope=removeAnonymousScopes(scope);
2495     //NamespaceDef *nd=0;
2496     //if (!nscope.isEmpty())
2497     if (!scope.isEmpty())
2498     {
2499       nd = getResolvedNamespaceMutable(scope);
2500     }
2501     for (const auto &imd : *mn)
2502     {
2503       MemberDefMutable *md = toMemberDefMutable(imd.get());
2504       if (md &&
2505           ((nd==0 && md->getNamespaceDef()==0 && md->getFileDef() &&
2506             root->fileName==md->getFileDef()->absFilePath()
2507            ) // both variable names in the same file
2508            || (nd!=0 && md->getNamespaceDef()==nd) // both in same namespace
2509           )
2510           && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2511           && !md->isEnumerate() // in C# an enum value and enum can have the same name
2512          )
2513         // variable already in the scope
2514       {
2515         bool isPHPArray = md->getLanguage()==SrcLangExt_PHP &&
2516                           md->argsString()!=args &&
2517                           args.find('[')!=-1;
2518         bool staticsInDifferentFiles =
2519                           root->stat && md->isStatic() &&
2520                           root->fileName!=md->getDefFileName();
2521 
2522         if (md->getFileDef() &&
2523             !isPHPArray && // not a php array
2524             !staticsInDifferentFiles
2525            )
2526           // not a php array variable
2527         {
2528           Debug::print(Debug::Variables,0,
2529               "    variable already found: scope=%s\n",qPrint(md->getOuterScope()->name()));
2530           addMemberDocs(root,md,def,0,FALSE,root->spec);
2531           md->setRefItems(root->sli);
2532           // if md is a variable forward declaration and root is the definition that
2533           // turn md into the definition
2534           if (!root->explicitExternal && md->isExternal())
2535           {
2536             md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2537             md->setExplicitExternal(FALSE,root->fileName,root->startLine,root->startColumn);
2538           }
2539           // if md is the definition and root point at a declaration, then add the
2540           // declaration info
2541           else if (root->explicitExternal && !md->isExternal())
2542           {
2543             md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2544           }
2545           return md;
2546         }
2547       }
2548     }
2549   }
2550 
2551   QCString fileName = root->fileName;
2552   if (fileName.isEmpty() && root->tagInfo())
2553   {
2554     fileName = root->tagInfo()->tagName;
2555   }
2556 
2557   Debug::print(Debug::Variables,0,
2558     "    new variable, nd=%s tagInfo=%p!\n",nd?qPrint(nd->name()):"<global>",root->tagInfo());
2559   // new global variable, enum value or typedef
2560   std::unique_ptr<MemberDefMutable> md { createMemberDef(
2561       fileName,root->startLine,root->startColumn,
2562       type,name,args,QCString(),
2563       root->protection, Normal,root->stat,Member,
2564       mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2565       root->argList, root->metaData) };
2566   md->setTagInfo(root->tagInfo());
2567   md->setMemberSpecifiers(root->spec);
2568   md->setDocumentation(root->doc,root->docFile,root->docLine);
2569   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2570   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2571   md->addSectionsToDefinition(root->anchors);
2572   md->setFromAnonymousScope(fromAnnScope);
2573   md->setFromAnonymousMember(fromAnnMemb);
2574   std::string init = root->initializer.str();
2575   md->setInitializer(init.c_str());
2576   md->setMaxInitLines(root->initLines);
2577   md->setMemberGroupId(root->mGrpId);
2578   md->setDefinition(def);
2579   md->setLanguage(root->lang);
2580   md->setId(root->id);
2581   md->enableCallGraph(root->callGraph);
2582   md->enableCallerGraph(root->callerGraph);
2583   md->enableReferencedByRelation(root->referencedByRelation);
2584   md->enableReferencesRelation(root->referencesRelation);
2585   md->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2586   //md->setOuterScope(fd);
2587   if (!root->explicitExternal)
2588   {
2589     md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2590     md->setBodyDef(fd);
2591   }
2592   addMemberToGroups(root,md.get());
2593 
2594   md->setRefItems(root->sli);
2595   if (nd && !nd->isAnonymous())
2596   {
2597     md->setNamespace(nd);
2598     nd->insertMember(md.get());
2599   }
2600 
2601   // add member to the file (we do this even if we have already inserted
2602   // it into the namespace.
2603   if (fd)
2604   {
2605     md->setFileDef(fd);
2606     fd->insertMember(md.get());
2607   }
2608 
2609   root->markAsProcessed();
2610 
2611   // add member definition to the list of globals
2612   MemberDef *result = md.get();
2613   mn = Doxygen::functionNameLinkedMap->add(name);
2614   mn->push_back(std::move(md));
2615 
2616   return result;
2617 }
2618 
2619 /*! See if the return type string \a type is that of a function pointer
2620  *  \returns -1 if this is not a function pointer variable or
2621  *           the index at which the closing brace of (...*name) was found.
2622  */
findFunctionPtr(const std::string & type,SrcLangExt lang,int * pLength=0)2623 static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=0)
2624 {
2625   if (lang == SrcLangExt_Fortran || lang == SrcLangExt_VHDL)
2626   {
2627     return -1; // Fortran and VHDL do not have function pointers
2628   }
2629 
2630   static const reg::Ex re(R"(\([^)]*[*^][^)]*\))");
2631   reg::Match match;
2632   size_t i=std::string::npos;
2633   size_t l=0;
2634   if (reg::search(type,match,re)) // contains (...*...)
2635   {
2636     i = match.position();
2637     l = match.length();
2638   }
2639   size_t bb=type.find('<');
2640   size_t be=type.rfind('>');
2641 
2642   if (!type.empty()                            &&  // return type is non-empty
2643       i!=std::string::npos                     &&   // contains (...*...)
2644       type.find("operator")==std::string::npos &&   // not an operator
2645       (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2646                                                     // not a function pointer return type
2647       !(bb<i && i<be) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2648      )
2649   {
2650     if (pLength) *pLength=(int)l;
2651     //printf("findFunctionPtr=%d\n",(int)i);
2652     return (int)i;
2653   }
2654   else
2655   {
2656     //printf("findFunctionPtr=%d\n",-1);
2657     return -1;
2658   }
2659 }
2660 
2661 
2662 /*! Returns TRUE iff \a type is a class within scope \a context.
2663  *  Used to detect variable declarations that look like function prototypes.
2664  */
isVarWithConstructor(const Entry * root)2665 static bool isVarWithConstructor(const Entry *root)
2666 {
2667   bool result=FALSE;
2668   bool typeIsClass = false;
2669   bool typePtrType = false;
2670   QCString type;
2671   Definition *ctx = 0;
2672   FileDef *fd = root->fileDef();
2673   int ti;
2674   SymbolResolver resolver(fd);
2675 
2676   //printf("isVarWithConstructor(%s)\n",qPrint(rootNav->name()));
2677   if (root->parent()->section & Entry::COMPOUND_MASK)
2678   { // inside a class
2679     result=FALSE;
2680     goto done;
2681   }
2682   else if ((fd != nullptr) && (fd->name().right(2)==".c" || fd->name().right(2)==".h"))
2683   { // inside a .c file
2684     result=FALSE;
2685     goto done;
2686   }
2687   if (root->type.isEmpty())
2688   {
2689     result=FALSE;
2690     goto done;
2691   }
2692   if (!root->parent()->name.isEmpty())
2693   {
2694     ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2695   }
2696   type = root->type;
2697   // remove qualifiers
2698   findAndRemoveWord(type,"const");
2699   findAndRemoveWord(type,"static");
2700   findAndRemoveWord(type,"volatile");
2701   typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2702   //if (type.left(6)=="const ") type=type.right(type.length()-6);
2703   if (!typePtrType)
2704   {
2705     typeIsClass = resolver.resolveClass(ctx,type)!=0;
2706     if (!typeIsClass && (ti=type.find('<'))!=-1)
2707     {
2708       typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=0;
2709     }
2710   }
2711   if (typeIsClass) // now we still have to check if the arguments are
2712                    // types or values. Since we do not have complete type info
2713                    // we need to rely on heuristics :-(
2714   {
2715     //printf("typeIsClass\n");
2716     if (root->argList.empty())
2717     {
2718       result=FALSE; // empty arg list -> function prototype.
2719       goto done;
2720     }
2721     for (const Argument &a : root->argList)
2722     {
2723       static const reg::Ex initChars(R"([\d"'&*!^]+)");
2724       reg::Match match;
2725       if (!a.name.isEmpty() || !a.defval.isEmpty())
2726       {
2727         std::string name = a.name.str();
2728         if (reg::search(name,match,initChars) && match.position()==0)
2729         {
2730           result=TRUE;
2731         }
2732         else
2733         {
2734           result=FALSE; // arg has (type,name) pair -> function prototype
2735         }
2736         goto done;
2737       }
2738       if (!a.type.isEmpty() &&
2739           (a.type.at(a.type.length()-1)=='*' ||
2740            a.type.at(a.type.length()-1)=='&'))
2741            // type ends with * or & => pointer or reference
2742       {
2743         result=FALSE;
2744         goto done;
2745       }
2746       if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=0)
2747       {
2748         result=FALSE; // arg type is a known type
2749         goto done;
2750       }
2751       if (checkIfTypedef(ctx,fd,a.type))
2752       {
2753          //printf("%s:%d: false (arg is typedef)\n",__FILE__,__LINE__);
2754          result=FALSE; // argument is a typedef
2755          goto done;
2756       }
2757       std::string atype = a.type.str();
2758       if (reg::search(atype,match,initChars) && match.position()==0)
2759       {
2760         result=TRUE; // argument type starts with typical initializer char
2761         goto done;
2762       }
2763       std::string resType=resolveTypeDef(ctx,a.type).str();
2764       if (resType.empty()) resType=atype;
2765       static const reg::Ex idChars(R"(\a\w*)");
2766       if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2767       {
2768         resType=match.str();
2769         //printf("resType=%s\n",resType.data());
2770         if (resType=="int"    || resType=="long"     ||
2771             resType=="float"  || resType=="double"   ||
2772             resType=="char"   || resType=="void"     ||
2773             resType=="signed" || resType=="unsigned" ||
2774             resType=="const"  || resType=="volatile" )
2775         {
2776           result=FALSE; // type keyword -> function prototype
2777           goto done;
2778         }
2779       }
2780     }
2781     result=TRUE;
2782   }
2783 
2784 done:
2785   //printf("isVarWithConstructor(%s,%s)=%d\n",qPrint(rootNav->parent()->name()),
2786   //                                          qPrint(root->type),result);
2787   return result;
2788 }
2789 
2790 static void addVariable(const Entry *root,int isFuncPtr=-1)
2791 {
2792     static bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
2793 
2794     Debug::print(Debug::Variables,0,
2795                   "VARIABLE_SEC: \n"
2796                   "  type='%s' name='%s' args='%s' bodyLine=%d mGrpId=%d relates='%s'\n",
2797                    qPrint(root->type),
2798                    qPrint(root->name),
2799                    qPrint(root->args),
2800                    root->bodyLine,
2801                    root->mGrpId,
2802                    qPrint(root->relates)
2803                 );
2804     //printf("root->parent->name=%s\n",qPrint(root->parent->name));
2805 
2806     QCString type = root->type;
2807     QCString name = root->name;
2808     QCString args = root->args;
2809     if (type.isEmpty() && name.find("operator")==-1 &&
2810         (name.find('*')!=-1 || name.find('&')!=-1))
2811     {
2812       // recover from parse error caused by redundant braces
2813       // like in "int *(var[10]);", which is parsed as
2814       // type="" name="int *" args="(var[10])"
2815 
2816       type=name;
2817       std::string sargs = args.str();
2818       static const reg::Ex reName(R"(\a\w*)");
2819       reg::Match match;
2820       if (reg::search(sargs,match,reName))
2821       {
2822         name  = match.str();           // e.g. 'var'  in '(var[10])'
2823         sargs = match.suffix().str();  // e.g. '[10]) in '(var[10])'
2824         size_t j = sargs.find(')');
2825         if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
2826       }
2827       //printf("new: type='%s' name='%s' args='%s'\n",
2828       //    qPrint(type),qPrint(name),qPrint(args));
2829     }
2830     else
2831     {
2832       int i=isFuncPtr;
2833       if (i==-1 && (root->spec&Entry::Alias)==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
2834       Debug::print(Debug::Variables,0,"  functionPtr? %s\n",i!=-1?"yes":"no");
2835       if (i>=0) // function pointer
2836       {
2837         int ai = type.find('[',i);
2838         if (ai>i) // function pointer array
2839         {
2840           args.prepend(type.right(type.length()-ai));
2841           type=type.left(ai);
2842         }
2843         else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
2844         {
2845           type=type.left(type.length()-1);
2846           args.prepend(") ");
2847           //printf("type=%s args=%s\n",qPrint(type),qPrint(args));
2848         }
2849       }
2850     }
2851 
2852     QCString scope;
2853     name=removeRedundantWhiteSpace(name);
2854 
2855     // find the scope of this variable
2856     Entry *p = root->parent();
2857     while ((p->section & Entry::SCOPE_MASK))
2858     {
2859       QCString scopeName = p->name;
2860       if (!scopeName.isEmpty())
2861       {
2862         scope.prepend(scopeName);
2863         break;
2864       }
2865       p=p->parent();
2866     }
2867 
2868     MemberType mtype;
2869     type=type.stripWhiteSpace();
2870     ClassDefMutable *cd=0;
2871     bool isRelated=FALSE;
2872     bool isMemberOf=FALSE;
2873 
2874     QCString classScope=stripAnonymousNamespaceScope(scope);
2875     classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
2876     QCString annScopePrefix=scope.left(scope.length()-classScope.length());
2877 
2878     if (name.findRev("::")!=-1)
2879     {
2880       if (type=="friend class" || type=="friend struct" ||
2881           type=="friend union")
2882       {
2883          cd=getClassMutable(scope);
2884          if (cd)
2885          {
2886            addVariableToClass(root,  // entry
2887                               cd,    // class to add member to
2888                               MemberType_Friend, // type of member
2889                               type,   // type value as string
2890                               name,   // name of the member
2891                               args,   // arguments as string
2892                               FALSE,  // from Anonymous scope
2893                               0,      // anonymous member
2894                               Public, // protection
2895                               Member  // related to a class
2896                              );
2897          }
2898       }
2899       return;  /* skip this member, because it is a
2900                 * static variable definition (always?), which will be
2901                 * found in a class scope as well, but then we know the
2902                 * correct protection level, so only then it will be
2903                 * inserted in the correct list!
2904                 */
2905     }
2906 
2907     if (type=="@")
2908       mtype=MemberType_EnumValue;
2909     else if (type.left(8)=="typedef ")
2910       mtype=MemberType_Typedef;
2911     else if (type.left(7)=="friend ")
2912       mtype=MemberType_Friend;
2913     else if (root->mtype==Property)
2914       mtype=MemberType_Property;
2915     else if (root->mtype==Event)
2916       mtype=MemberType_Event;
2917     else if (type.find("sequence<") != -1)
2918       mtype=sliceOpt ? MemberType_Sequence : MemberType_Typedef;
2919     else if (type.find("dictionary<") != -1)
2920       mtype=sliceOpt ? MemberType_Dictionary : MemberType_Typedef;
2921     else
2922       mtype=MemberType_Variable;
2923 
2924     if (!root->relates.isEmpty()) // related variable
2925     {
2926       isRelated=TRUE;
2927       isMemberOf=(root->relatesType == MemberOf);
2928       if (getClass(root->relates)==0 && !scope.isEmpty())
2929         scope=mergeScopes(scope,root->relates);
2930       else
2931         scope=root->relates;
2932     }
2933 
2934     cd=getClassMutable(scope);
2935     if (cd==0 && classScope!=scope) cd=getClassMutable(classScope);
2936     if (cd)
2937     {
2938       MemberDef *md=0;
2939 
2940       // if cd is an anonymous (=tag less) scope we insert the member
2941       // into a non-anonymous parent scope as well. This is needed to
2942       // be able to refer to it using \var or \fn
2943 
2944       //int indentDepth=0;
2945       int si=scope.find('@');
2946       //int anonyScopes = 0;
2947       //bool added=FALSE;
2948 
2949       static bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
2950       if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
2951       {
2952         QCString pScope;
2953         ClassDefMutable *pcd=0;
2954         pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
2955         if (!pScope.isEmpty())
2956           pScope.prepend(annScopePrefix);
2957         else if (annScopePrefix.length()>2)
2958           pScope=annScopePrefix.left(annScopePrefix.length()-2);
2959         if (name.at(0)!='@')
2960         {
2961           if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
2962           {
2963             md=addVariableToClass(root,  // entry
2964                                   pcd,   // class to add member to
2965                                   mtype, // member type
2966                                   type,  // type value as string
2967                                   name,  // member name
2968                                   args,  // arguments as string
2969                                   TRUE,  // from anonymous scope
2970                                   0,     // from anonymous member
2971                                   root->protection,
2972                                   isMemberOf ? Foreign : isRelated ? Related : Member
2973                                  );
2974             //added=TRUE;
2975           }
2976           else // anonymous scope inside namespace or file => put variable in the global scope
2977           {
2978             if (mtype==MemberType_Variable)
2979             {
2980               md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,0);
2981             }
2982             //added=TRUE;
2983           }
2984         }
2985       }
2986 
2987       //printf("name='%s' scope=%s scope.right=%s\n",
2988       //                   qPrint(name),qPrint(scope),
2989       //                   qPrint(scope.right(scope.length()-si)));
2990       addVariableToClass(root,   // entry
2991                          cd,     // class to add member to
2992                          mtype,  // member type
2993                          type,   // type value as string
2994                          name,   // name of the member
2995                          args,   // arguments as string
2996                          FALSE,  // from anonymous scope
2997                          md,     // from anonymous member
2998                          root->protection,
2999                          isMemberOf ? Foreign : isRelated ? Related : Member);
3000     }
3001     else if (!name.isEmpty()) // global variable
3002     {
3003       //printf("Inserting member in global scope %s!\n",qPrint(scope));
3004       addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*0,*/0);
3005     }
3006 
3007 }
3008 
3009 //----------------------------------------------------------------------
3010 // Searches the Entry tree for typedef documentation sections.
3011 // If found they are stored in their class or in the global list.
buildTypedefList(const Entry * root)3012 static void buildTypedefList(const Entry *root)
3013 {
3014   //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3015   if (!root->name.isEmpty() &&
3016       root->section==Entry::VARIABLE_SEC &&
3017       root->type.find("typedef ")!=-1 // its a typedef
3018      )
3019   {
3020     addVariable(root);
3021   }
3022   for (const auto &e : root->children())
3023     if (e->section!=Entry::ENUM_SEC)
3024       buildTypedefList(e.get());
3025 }
3026 
3027 //----------------------------------------------------------------------
3028 // Searches the Entry tree for sequence documentation sections.
3029 // If found they are stored in the global list.
buildSequenceList(const Entry * root)3030 static void buildSequenceList(const Entry *root)
3031 {
3032   if (!root->name.isEmpty() &&
3033       root->section==Entry::VARIABLE_SEC &&
3034       root->type.find("sequence<")!=-1 // it's a sequence
3035      )
3036   {
3037     addVariable(root);
3038   }
3039   for (const auto &e : root->children())
3040     if (e->section!=Entry::ENUM_SEC)
3041       buildSequenceList(e.get());
3042 }
3043 
3044 //----------------------------------------------------------------------
3045 // Searches the Entry tree for dictionary documentation sections.
3046 // If found they are stored in the global list.
buildDictionaryList(const Entry * root)3047 static void buildDictionaryList(const Entry *root)
3048 {
3049   if (!root->name.isEmpty() &&
3050       root->section==Entry::VARIABLE_SEC &&
3051       root->type.find("dictionary<")!=-1 // it's a dictionary
3052      )
3053   {
3054     addVariable(root);
3055   }
3056   for (const auto &e : root->children())
3057     if (e->section!=Entry::ENUM_SEC)
3058       buildDictionaryList(e.get());
3059 }
3060 
3061 //----------------------------------------------------------------------
3062 // Searches the Entry tree for Variable documentation sections.
3063 // If found they are stored in their class or in the global list.
3064 
buildVarList(const Entry * root)3065 static void buildVarList(const Entry *root)
3066 {
3067   //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3068   int isFuncPtr=-1;
3069   if (!root->name.isEmpty() &&
3070       (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3071       (
3072        (root->section==Entry::VARIABLE_SEC    // it's a variable
3073        ) ||
3074        (root->section==Entry::FUNCTION_SEC && // or maybe a function pointer variable
3075         (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3076        ) ||
3077        (root->section==Entry::FUNCTION_SEC && // class variable initialized by constructor
3078         isVarWithConstructor(root)
3079        )
3080       )
3081      ) // documented variable
3082   {
3083     addVariable(root,isFuncPtr);
3084   }
3085   for (const auto &e : root->children())
3086     if (e->section!=Entry::ENUM_SEC)
3087       buildVarList(e.get());
3088 }
3089 
3090 //----------------------------------------------------------------------
3091 // Searches the Entry tree for Interface sections (UNO IDL only).
3092 // If found they are stored in their service or in the global list.
3093 //
3094 
addInterfaceOrServiceToServiceOrSingleton(const Entry * root,ClassDefMutable * cd,QCString const & rname)3095 static void addInterfaceOrServiceToServiceOrSingleton(
3096         const Entry *root,
3097         ClassDefMutable *cd,
3098         QCString const& rname)
3099 {
3100   FileDef *fd = root->fileDef();
3101   enum MemberType type = (root->section==Entry::EXPORTED_INTERFACE_SEC)
3102       ? MemberType_Interface
3103       : MemberType_Service;
3104   QCString fileName = root->fileName;
3105   if (fileName.isEmpty() && root->tagInfo())
3106   {
3107     fileName = root->tagInfo()->tagName;
3108   }
3109   std::unique_ptr<MemberDefMutable> md { createMemberDef(
3110       fileName, root->startLine, root->startColumn, root->type, rname,
3111       "", "", root->protection, root->virt, root->stat, Member,
3112       type, ArgumentList(), root->argList, root->metaData) };
3113   md->setTagInfo(root->tagInfo());
3114   md->setMemberClass(cd);
3115   md->setDocumentation(root->doc,root->docFile,root->docLine);
3116   md->setDocsForDefinition(false);
3117   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3118   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3119   md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3120   md->setMemberSpecifiers(root->spec);
3121   md->setMemberGroupId(root->mGrpId);
3122   md->setTypeConstraints(root->typeConstr);
3123   md->setLanguage(root->lang);
3124   md->setBodyDef(fd);
3125   md->setFileDef(fd);
3126   md->addSectionsToDefinition(root->anchors);
3127   QCString const def = root->type + " " + rname;
3128   md->setDefinition(def);
3129   md->enableCallGraph(root->callGraph);
3130   md->enableCallerGraph(root->callerGraph);
3131   md->enableReferencedByRelation(root->referencedByRelation);
3132   md->enableReferencesRelation(root->referencesRelation);
3133 
3134   Debug::print(Debug::Functions,0,
3135       "  Interface Member:\n"
3136       "    '%s' '%s' proto=%d\n"
3137       "    def='%s'\n",
3138       qPrint(root->type),
3139       qPrint(rname),
3140       root->proto,
3141       qPrint(def)
3142               );
3143 
3144 
3145   // add member to the class cd
3146   cd->insertMember(md.get());
3147   // also add the member as a "base" (to get nicer diagrams)
3148   // "optional" interface/service get Protected which turns into dashed line
3149   BaseInfo base(rname,
3150           (root->spec & (Entry::Optional)) ? Protected : Public,Normal);
3151   TemplateNameMap templateNames;
3152   findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3153        findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3154   // add file to list of used files
3155   cd->insertUsedFile(fd);
3156 
3157   addMemberToGroups(root,md.get());
3158   root->markAsProcessed();
3159   md->setRefItems(root->sli);
3160 
3161   // add member to the global list of all members
3162   MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3163   mn->push_back(std::move(md));
3164 }
3165 
buildInterfaceAndServiceList(const Entry * root)3166 static void buildInterfaceAndServiceList(const Entry *root)
3167 {
3168   if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
3169       root->section==Entry::INCLUDED_SERVICE_SEC)
3170   {
3171     Debug::print(Debug::Functions,0,
3172                  "EXPORTED_INTERFACE_SEC:\n"
3173                  "  '%s' '%s'::'%s' '%s' relates='%s' relatesType='%d' file='%s' line='%d' bodyLine='%d' #tArgLists=%d mGrpId=%d spec=%lld proto=%d docFile=%s\n",
3174                  qPrint(root->type),
3175                  qPrint(root->parent()->name),
3176                  qPrint(root->name),
3177                  qPrint(root->args),
3178                  qPrint(root->relates),
3179                  root->relatesType,
3180                  qPrint(root->fileName),
3181                  root->startLine,
3182                  root->bodyLine,
3183                  root->tArgLists.size(),
3184                  root->mGrpId,
3185                  root->spec,
3186                  root->proto,
3187                  qPrint(root->docFile)
3188                 );
3189 
3190     QCString rname = removeRedundantWhiteSpace(root->name);
3191 
3192     if (!rname.isEmpty())
3193     {
3194       QCString scope = root->parent()->name;
3195       ClassDefMutable *cd = getClassMutable(scope);
3196       assert(cd);
3197       if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3198                  (ClassDef::Service   == cd->compoundType()) ||
3199                  (ClassDef::Singleton == cd->compoundType())))
3200       {
3201         addInterfaceOrServiceToServiceOrSingleton(root,cd,rname);
3202       }
3203       else
3204       {
3205         assert(false); // was checked by scanner.l
3206       }
3207     }
3208     else if (rname.isEmpty())
3209     {
3210       warn(root->fileName,root->startLine,
3211            "Illegal member name found.");
3212     }
3213   }
3214   // can only have these in IDL anyway
3215   switch (root->lang)
3216   {
3217     case SrcLangExt_Unknown: // fall through (root node always is Unknown)
3218     case SrcLangExt_IDL:
3219         for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3220         break;
3221     default:
3222         return; // nothing to do here
3223   }
3224 }
3225 
3226 
3227 //----------------------------------------------------------------------
3228 // Searches the Entry tree for Function sections.
3229 // If found they are stored in their class or in the global list.
3230 
addMethodToClass(const Entry * root,ClassDefMutable * cd,const QCString & rtype,const QCString & rname,const QCString & rargs,bool isFriend,Protection protection,bool stat,Specifier virt,uint64 spec,const QCString & relates)3231 static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3232                   const QCString &rtype,const QCString &rname,const QCString &rargs,
3233                   bool isFriend,
3234                   Protection protection,bool stat,Specifier virt,uint64 spec,
3235                   const QCString &relates
3236                   )
3237 {
3238   FileDef *fd=root->fileDef();
3239 
3240   QCString type = rtype;
3241   QCString args = rargs;
3242 
3243   QCString name=removeRedundantWhiteSpace(rname);
3244   if (name.left(2)=="::") name=name.right(name.length()-2);
3245 
3246   MemberType mtype;
3247   if (isFriend)                 mtype=MemberType_Friend;
3248   else if (root->mtype==Signal) mtype=MemberType_Signal;
3249   else if (root->mtype==Slot)   mtype=MemberType_Slot;
3250   else if (root->mtype==DCOP)   mtype=MemberType_DCOP;
3251   else                          mtype=MemberType_Function;
3252 
3253   // strip redundant template specifier for constructors
3254   int i = -1;
3255   int j = -1;
3256   if ((fd==0 || fd->getLanguage()==SrcLangExt_Cpp) &&
3257       name.left(9)!="operator " &&   // not operator
3258       (i=name.find('<'))!=-1    &&   // containing <
3259       (j=name.find('>'))!=-1    &&   // or >
3260       (j!=i+2 || name.at(i+1)!='=')  // but not the C++20 spaceship operator <=>
3261      )
3262   {
3263     name=name.left(i);
3264   }
3265 
3266   QCString fileName = root->fileName;
3267   if (fileName.isEmpty() && root->tagInfo())
3268   {
3269     fileName = root->tagInfo()->tagName;
3270   }
3271 
3272   //printf("root->name='%s; args='%s' root->argList='%s'\n",
3273   //    qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3274   //   );
3275 
3276   // adding class member
3277   std::unique_ptr<MemberDefMutable> md { createMemberDef(
3278       fileName,root->startLine,root->startColumn,
3279       type,name,args,root->exception,
3280       protection,virt,
3281       stat && root->relatesType != MemberOf,
3282       relates.isEmpty() ? Member :
3283           root->relatesType == MemberOf ? Foreign : Related,
3284       mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3285       root->argList, root->metaData) };
3286   md->setTagInfo(root->tagInfo());
3287   md->setMemberClass(cd);
3288   md->setDocumentation(root->doc,root->docFile,root->docLine);
3289   md->setDocsForDefinition(!root->proto);
3290   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3291   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3292   md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3293   md->setMemberSpecifiers(spec);
3294   md->setMemberGroupId(root->mGrpId);
3295   md->setTypeConstraints(root->typeConstr);
3296   md->setLanguage(root->lang);
3297   md->setRequiresClause(root->req);
3298   md->setId(root->id);
3299   md->setBodyDef(fd);
3300   md->setFileDef(fd);
3301   md->addSectionsToDefinition(root->anchors);
3302   QCString def;
3303   QCString qualScope = cd->qualifiedNameWithTemplateParameters();
3304   SrcLangExt lang = cd->getLanguage();
3305   QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3306   if (scopeSeparator!="::")
3307   {
3308     qualScope = substitute(qualScope,"::",scopeSeparator);
3309   }
3310   if (lang==SrcLangExt_PHP)
3311   {
3312     // for PHP we use Class::method and Namespace\method
3313     scopeSeparator="::";
3314   }
3315 //  QCString optArgs = root->argList.empty() ? args : QCString();
3316   if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3317   {
3318     if (!type.isEmpty())
3319     {
3320       def=type+" "+name; //+optArgs;
3321     }
3322     else
3323     {
3324       def=name; //+optArgs;
3325     }
3326   }
3327   else
3328   {
3329     if (!type.isEmpty())
3330     {
3331       def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3332     }
3333     else
3334     {
3335       def=qualScope+scopeSeparator+name; //+optArgs;
3336     }
3337   }
3338   if (def.left(7)=="friend ") def=def.right(def.length()-7);
3339   md->setDefinition(def);
3340   md->enableCallGraph(root->callGraph);
3341   md->enableCallerGraph(root->callerGraph);
3342   md->enableReferencedByRelation(root->referencedByRelation);
3343   md->enableReferencesRelation(root->referencesRelation);
3344 
3345   Debug::print(Debug::Functions,0,
3346       "  Func Member:\n"
3347       "    '%s' '%s'::'%s' '%s' proto=%d\n"
3348       "    def='%s'\n",
3349       qPrint(type),
3350       qPrint(qualScope),
3351       qPrint(rname),
3352       qPrint(args),
3353       root->proto,
3354       qPrint(def)
3355               );
3356 
3357   // add member to the class cd
3358   cd->insertMember(md.get());
3359   // add file to list of used files
3360   cd->insertUsedFile(fd);
3361 
3362   addMemberToGroups(root,md.get());
3363   root->markAsProcessed();
3364   md->setRefItems(root->sli);
3365 
3366   // add member to the global list of all members
3367   //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3368   MemberName *mn = Doxygen::memberNameLinkedMap->add(name);
3369   mn->push_back(std::move(md));
3370 }
3371 
3372 //------------------------------------------------------------------------------------------
3373 
addGlobalFunction(const Entry * root,const QCString & rname,const QCString & sc)3374 static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3375 {
3376   QCString scope = sc;
3377   Debug::print(Debug::Functions,0,"  --> new function %s found!\n",qPrint(rname));
3378   //printf("New function type='%s' name='%s' args='%s' bodyLine=%d\n",
3379   //       qPrint(root->type),qPrint(rname),qPrint(root->args),root->bodyLine);
3380 
3381   // new global function
3382   QCString name=removeRedundantWhiteSpace(rname);
3383   std::unique_ptr<MemberDefMutable> md { createMemberDef(
3384       root->fileName,root->startLine,root->startColumn,
3385       root->type,name,root->args,root->exception,
3386       root->protection,root->virt,root->stat,Member,
3387       MemberType_Function,
3388       !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3389       root->argList,root->metaData) };
3390 
3391   md->setTagInfo(root->tagInfo());
3392   md->setLanguage(root->lang);
3393   md->setId(root->id);
3394   md->setDocumentation(root->doc,root->docFile,root->docLine);
3395   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3396   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3397   md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3398   md->setDocsForDefinition(!root->proto);
3399   md->setTypeConstraints(root->typeConstr);
3400   //md->setBody(root->body);
3401   md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3402   FileDef *fd=root->fileDef();
3403   md->setBodyDef(fd);
3404   md->addSectionsToDefinition(root->anchors);
3405   md->setMemberSpecifiers(root->spec);
3406   md->setMemberGroupId(root->mGrpId);
3407   md->setRequiresClause(root->req);
3408 
3409   NamespaceDefMutable *nd = 0;
3410   // see if the function is inside a namespace that was not part of
3411   // the name already (in that case nd should be non-zero already)
3412   if (root->parent()->section == Entry::NAMESPACE_SEC )
3413   {
3414     //QCString nscope=removeAnonymousScopes(root->parent()->name);
3415     QCString nscope=root->parent()->name;
3416     if (!nscope.isEmpty())
3417     {
3418       nd = getResolvedNamespaceMutable(nscope);
3419     }
3420   }
3421 
3422   if (!scope.isEmpty())
3423   {
3424     QCString sep = getLanguageSpecificSeparator(root->lang);
3425     if (sep!="::")
3426     {
3427       scope = substitute(scope,"::",sep);
3428     }
3429     scope+=sep;
3430   }
3431 
3432   QCString def;
3433   //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3434   if (!root->type.isEmpty())
3435   {
3436     def=root->type+" "+scope+name; //+optArgs;
3437   }
3438   else
3439   {
3440     def=scope+name; //+optArgs;
3441   }
3442   Debug::print(Debug::Functions,0,
3443       "  Global Function:\n"
3444       "    '%s' '%s'::'%s' '%s' proto=%d\n"
3445       "    def='%s'\n",
3446       qPrint(root->type),
3447       qPrint(root->parent()->name),
3448       qPrint(rname),
3449       qPrint(root->args),
3450       root->proto,
3451       qPrint(def)
3452       );
3453   md->setDefinition(def);
3454   md->enableCallGraph(root->callGraph);
3455   md->enableCallerGraph(root->callerGraph);
3456   md->enableReferencedByRelation(root->referencedByRelation);
3457   md->enableReferencesRelation(root->referencesRelation);
3458   //if (root->mGrpId!=-1)
3459   //{
3460   //  md->setMemberGroup(memberGroupDict[root->mGrpId]);
3461   //}
3462 
3463   md->setRefItems(root->sli);
3464   if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3465   {
3466     // add member to namespace
3467     md->setNamespace(nd);
3468     nd->insertMember(md.get());
3469   }
3470   if (fd)
3471   {
3472     // add member to the file (we do this even if we have already
3473     // inserted it into the namespace)
3474     md->setFileDef(fd);
3475     fd->insertMember(md.get());
3476   }
3477 
3478   addMemberToGroups(root,md.get());
3479   if (root->relatesType == Simple) // if this is a relatesalso command,
3480                                    // allow find Member to pick it up
3481   {
3482     root->markAsProcessed(); // Otherwise we have finished with this entry.
3483   }
3484 
3485   // add member to the list of file members
3486   //printf("Adding member=%s\n",qPrint(md->name()));
3487   MemberName *mn = Doxygen::functionNameLinkedMap->add(name);
3488   mn->push_back(std::move(md));
3489 }
3490 
3491 //------------------------------------------------------------------------------------------
3492 
buildFunctionList(const Entry * root)3493 static void buildFunctionList(const Entry *root)
3494 {
3495   if (root->section==Entry::FUNCTION_SEC)
3496   {
3497     Debug::print(Debug::Functions,0,
3498                  "FUNCTION_SEC:\n"
3499                  "  '%s' '%s'::'%s' '%s' relates='%s' relatesType='%d' file='%s' line='%d' bodyLine='%d' #tArgLists=%d mGrpId=%d spec=%lld proto=%d docFile=%s\n",
3500                  qPrint(root->type),
3501                  qPrint(root->parent()->name),
3502                  qPrint(root->name),
3503                  qPrint(root->args),
3504                  qPrint(root->relates),
3505                  root->relatesType,
3506                  qPrint(root->fileName),
3507                  root->startLine,
3508                  root->bodyLine,
3509                  root->tArgLists.size(),
3510                  root->mGrpId,
3511                  root->spec,
3512                  root->proto,
3513                  qPrint(root->docFile)
3514                 );
3515 
3516     bool isFriend=root->type.find("friend ")!=-1;
3517     QCString rname = removeRedundantWhiteSpace(root->name);
3518     //printf("rname=%s\n",qPrint(rname));
3519 
3520     QCString scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3521     if (!rname.isEmpty() && scope.find('@')==-1)
3522     {
3523       ClassDefMutable *cd=0;
3524       // check if this function's parent is a class
3525       scope=stripTemplateSpecifiersFromScope(scope,FALSE);
3526 
3527       FileDef *rfd=root->fileDef();
3528 
3529       int memIndex=rname.findRev("::");
3530 
3531       cd=getClassMutable(scope);
3532       if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3533       {
3534         // strip scope from name
3535         rname=rname.right(rname.length()-root->parent()->name.length()-2);
3536       }
3537 
3538       bool isMember=FALSE;
3539       if (memIndex!=-1)
3540       {
3541         int ts=rname.find('<');
3542         int te=rname.find('>');
3543         if (memIndex>0 && (ts==-1 || te==-1))
3544         {
3545           // note: the following code was replaced by inMember=TRUE to deal with a
3546           // function rname='X::foo' of class X inside a namespace also called X...
3547           // bug id 548175
3548           //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3549           //isMember = nd==0;
3550           //if (nd)
3551           //{
3552           //  // strip namespace scope from name
3553           //  scope=rname.left(memIndex);
3554           //  rname=rname.right(rname.length()-memIndex-2);
3555           //}
3556           isMember = TRUE;
3557         }
3558         else
3559         {
3560           isMember=memIndex<ts || memIndex>te;
3561         }
3562       }
3563 
3564       if (!root->parent()->name.isEmpty() &&
3565           (root->parent()->section & Entry::COMPOUND_MASK) &&
3566           cd
3567          )
3568       {
3569         Debug::print(Debug::Functions,0,"  --> member %s of class %s!\n",
3570             qPrint(rname),qPrint(cd->name()));
3571         addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3572                          root->protection,root->stat,root->virt,root->spec,root->relates);
3573       }
3574       else if (!((root->parent()->section & Entry::COMPOUND_MASK)
3575                  || root->parent()->section==Entry::OBJCIMPL_SEC
3576                 ) &&
3577                !isMember &&
3578                (root->relates.isEmpty() || root->relatesType == Duplicate) &&
3579                root->type.left(7)!="extern " && root->type.left(8)!="typedef "
3580               )
3581       // no member => unrelated function
3582       {
3583         /* check the uniqueness of the function name in the file.
3584          * A file could contain a function prototype and a function definition
3585          * or even multiple function prototypes.
3586          */
3587         bool found=FALSE;
3588         MemberName *mn;
3589         MemberDef *md_found=0;
3590         if ((mn=Doxygen::functionNameLinkedMap->find(rname)))
3591         {
3592           Debug::print(Debug::Functions,0,"  --> function %s already found!\n",qPrint(rname));
3593           for (const auto &imd : *mn)
3594           {
3595             MemberDefMutable *md = toMemberDefMutable(imd.get());
3596             if (md)
3597             {
3598               const NamespaceDef *mnd = md->getNamespaceDef();
3599               NamespaceDef *rnd = 0;
3600               //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
3601               QCString fullScope = scope;
3602               QCString parentScope = root->parent()->name;
3603               if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
3604               {
3605                 if (!scope.isEmpty()) fullScope.prepend("::");
3606                 fullScope.prepend(parentScope);
3607               }
3608               //printf("fullScope=%s\n",qPrint(fullScope));
3609               rnd = getResolvedNamespace(fullScope);
3610               const FileDef *mfd = md->getFileDef();
3611               QCString nsName,rnsName;
3612               if (mnd)  nsName = mnd->name();
3613               if (rnd) rnsName = rnd->name();
3614               //printf("matching arguments for %s%s %s%s\n",
3615               //    qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
3616               const ArgumentList &mdAl = md->argumentList();
3617               const ArgumentList &mdTempl = md->templateArguments();
3618 
3619               // in case of template functions, we need to check if the
3620               // functions have the same number of template parameters
3621               bool sameNumTemplateArgs = TRUE;
3622               bool matchingReturnTypes = TRUE;
3623               bool sameRequiresClause = TRUE;
3624               if (!mdTempl.empty() && !root->tArgLists.empty())
3625               {
3626                 if (mdTempl.size()!=root->tArgLists.back().size())
3627                 {
3628                   sameNumTemplateArgs = FALSE;
3629                 }
3630                 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
3631                 {
3632                   matchingReturnTypes = FALSE;
3633                 }
3634                 if (md->requiresClause()!=root->req)
3635                 {
3636                   sameRequiresClause = FALSE;
3637                 }
3638               }
3639               else if (!mdTempl.empty() || !root->tArgLists.empty())
3640               { // if one has template parameters and the other doesn't then that also counts as a
3641                 // difference
3642                 sameNumTemplateArgs = FALSE;
3643               }
3644 
3645               bool staticsInDifferentFiles =
3646                 root->stat && md->isStatic() && root->fileName!=md->getDefFileName();
3647 
3648               if (
3649                   matchArguments2(md->getOuterScope(),mfd,&mdAl,
3650                     rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
3651                     FALSE) &&
3652                   sameNumTemplateArgs &&
3653                   matchingReturnTypes &&
3654                   sameRequiresClause &&
3655                   !staticsInDifferentFiles
3656                  )
3657               {
3658                 GroupDef *gd=0;
3659                 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
3660                 {
3661                   gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
3662                 }
3663                 //printf("match!\n");
3664                 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
3665                 // see if we need to create a new member
3666                 found=(mnd && rnd && nsName==rnsName) ||   // members are in the same namespace
3667                   ((mnd==0 && rnd==0 && mfd!=0 &&       // no external reference and
3668                     mfd->absFilePath()==root->fileName // prototype in the same file
3669                    )
3670                   );
3671                 // otherwise, allow a duplicate global member with the same argument list
3672                 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
3673                 {
3674                   // member is already in the group, so we don't want to add it again.
3675                   found=TRUE;
3676                 }
3677 
3678                 //printf("combining function with prototype found=%d in namespace %s\n",
3679                 //    found,qPrint(nsName));
3680 
3681                 if (found)
3682                 {
3683                   // merge argument lists
3684                   ArgumentList mergedArgList = root->argList;
3685                   mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
3686                   // merge documentation
3687                   if (md->documentation().isEmpty() && !root->doc.isEmpty())
3688                   {
3689                     if (root->proto)
3690                     {
3691                       //printf("setDeclArgumentList to %p\n",argList);
3692                       md->moveDeclArgumentList(stringToArgumentList(root->lang,root->args));
3693                     }
3694                     else
3695                     {
3696                       md->moveArgumentList(stringToArgumentList(root->lang,root->args));
3697                     }
3698                   }
3699 
3700                   md->setDocumentation(root->doc,root->docFile,root->docLine);
3701                   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3702                   md->setDocsForDefinition(!root->proto);
3703                   if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
3704                   {
3705                     md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3706                     md->setBodyDef(rfd);
3707                   }
3708 
3709                   if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
3710                   {
3711                     md->setArgsString(root->args);
3712                   }
3713                   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3714 
3715                   md->addSectionsToDefinition(root->anchors);
3716 
3717                   md->enableCallGraph(md->hasCallGraph() || root->callGraph);
3718                   md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
3719                   md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
3720                   md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
3721 
3722                   // merge ingroup specifiers
3723                   if (md->getGroupDef()==0 && !root->groups.empty())
3724                   {
3725                     addMemberToGroups(root,md);
3726                   }
3727                   else if (md->getGroupDef()!=0 && root->groups.empty())
3728                   {
3729                     //printf("existing member is grouped, new member not\n");
3730                   }
3731                   else if (md->getGroupDef()!=0 && !root->groups.empty())
3732                   {
3733                     //printf("both members are grouped\n");
3734                   }
3735 
3736                   // if md is a declaration and root is the corresponding
3737                   // definition, then turn md into a definition.
3738                   if (md->isPrototype() && !root->proto)
3739                   {
3740                     md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
3741                     md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
3742                   }
3743                   // if md is already the definition, then add the declaration info
3744                   else if (!md->isPrototype() && root->proto)
3745                   {
3746                     md->setDeclFile(root->fileName,root->startLine,root->startColumn);
3747                   }
3748                 }
3749               }
3750             }
3751             if (found)
3752             {
3753               md_found = md;
3754               break;
3755             }
3756           }
3757         }
3758         if (!found) /* global function is unique with respect to the file */
3759         {
3760           addGlobalFunction(root,rname,scope);
3761         }
3762         else
3763         {
3764           FileDef *fd=root->fileDef();
3765           if (fd)
3766           {
3767             // add member to the file (we do this even if we have already
3768             // inserted it into the namespace)
3769             fd->insertMember(md_found);
3770           }
3771         }
3772 
3773         //printf("unrelated function %d '%s' '%s' '%s'\n",
3774         //    root->parent->section,qPrint(root->type),qPrint(rname),qPrint(root->args));
3775       }
3776       else
3777       {
3778           Debug::print(Debug::Functions,0,"  --> %s not processed!\n",qPrint(rname));
3779       }
3780     }
3781     else if (rname.isEmpty())
3782     {
3783         warn(root->fileName,root->startLine,
3784              "Illegal member name found."
3785             );
3786     }
3787   }
3788   for (const auto &e : root->children()) buildFunctionList(e.get());
3789 }
3790 
3791 //----------------------------------------------------------------------
3792 
findFriends()3793 static void findFriends()
3794 {
3795   //printf("findFriends()\n");
3796   for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
3797   {
3798     //printf("Function name='%s'\n",fn->memberName());
3799     MemberName *mn;
3800     if ((mn=Doxygen::memberNameLinkedMap->find(fn->memberName())))
3801     { // there are members with the same name
3802       //printf("Function name is also a member name\n");
3803       // for each function with that name
3804       for (const auto &ifmd : *fn)
3805       {
3806         MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
3807         // for each member with that name
3808         for (const auto &immd : *mn)
3809         {
3810            MemberDefMutable *mmd = toMemberDefMutable(immd.get());
3811           //printf("Checking for matching arguments
3812           //        mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
3813           //    mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
3814           if (fmd && mmd &&
3815               (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
3816               matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
3817                               fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
3818                               TRUE
3819                              )
3820 
3821              ) // if the member is related and the arguments match then the
3822                // function is actually a friend.
3823           {
3824             const ArgumentList &mmdAl = mmd->argumentList();
3825             const ArgumentList &fmdAl = fmd->argumentList();
3826             mergeArguments(const_cast<ArgumentList&>(mmdAl),const_cast<ArgumentList&>(fmdAl));
3827             if (!fmd->documentation().isEmpty())
3828             {
3829               mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
3830             }
3831             else if (!mmd->documentation().isEmpty())
3832             {
3833               fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
3834             }
3835             if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3836             {
3837               mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
3838             }
3839             else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3840             {
3841               fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
3842             }
3843             if (!fmd->inbodyDocumentation().isEmpty())
3844             {
3845               mmd->setInbodyDocumentation(fmd->inbodyDocumentation(),fmd->inbodyFile(),fmd->inbodyLine());
3846             }
3847             else if (!mmd->inbodyDocumentation().isEmpty())
3848             {
3849               fmd->setInbodyDocumentation(mmd->inbodyDocumentation(),mmd->inbodyFile(),mmd->inbodyLine());
3850             }
3851             //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
3852             if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
3853             {
3854               mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
3855               mmd->setBodyDef(fmd->getBodyDef());
3856               //mmd->setBodyMember(fmd);
3857             }
3858             else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
3859             {
3860               fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
3861               fmd->setBodyDef(mmd->getBodyDef());
3862               //fmd->setBodyMember(mmd);
3863             }
3864             mmd->setDocsForDefinition(fmd->isDocsForDefinition());
3865 
3866             mmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3867             mmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3868             mmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3869             mmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3870 
3871             fmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3872             fmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3873             fmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3874             fmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3875           }
3876         }
3877       }
3878     }
3879   }
3880 }
3881 
3882 //----------------------------------------------------------------------
3883 
transferFunctionDocumentation()3884 static void transferFunctionDocumentation()
3885 {
3886   //printf("---- transferFunctionDocumentation()\n");
3887 
3888   // find matching function declaration and definitions.
3889   for (const auto &mn : *Doxygen::functionNameLinkedMap)
3890   {
3891     //printf("memberName=%s count=%d\n",mn->memberName(),mn->count());
3892     /* find a matching function declaration and definition for this function */
3893     for (const auto &imdec : *mn)
3894     {
3895       MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
3896       if (mdec &&
3897           (mdec->isPrototype() ||
3898            (mdec->isVariable() && mdec->isExternal())
3899          ))
3900       {
3901         for (const auto &imdef : *mn)
3902         {
3903           MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
3904           if (mdef && mdec!=mdef &&
3905               mdec->getNamespaceDef()==mdef->getNamespaceDef())
3906           {
3907             combineDeclarationAndDefinition(mdec,mdef);
3908           }
3909         }
3910       }
3911     }
3912   }
3913 }
3914 
3915 //----------------------------------------------------------------------
3916 
transferFunctionReferences()3917 static void transferFunctionReferences()
3918 {
3919   for (const auto &mn : *Doxygen::functionNameLinkedMap)
3920   {
3921     MemberDefMutable *mdef=0,*mdec=0;
3922     /* find a matching function declaration and definition for this function */
3923     for (const auto &imd : *mn)
3924     {
3925       MemberDefMutable *md = toMemberDefMutable(imd.get());
3926       if (md)
3927       {
3928         if (md->isPrototype())
3929           mdec=md;
3930         else if (md->isVariable() && md->isExternal())
3931           mdec=md;
3932 
3933         if (md->isFunction() && !md->isStatic() && !md->isPrototype())
3934           mdef=md;
3935         else if (md->isVariable() && !md->isExternal() && !md->isStatic())
3936           mdef=md;
3937       }
3938 
3939       if (mdef && mdec) break;
3940     }
3941     if (mdef && mdec)
3942     {
3943       const ArgumentList &mdefAl = mdef->argumentList();
3944       const ArgumentList &mdecAl = mdec->argumentList();
3945       if (
3946           matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
3947                           mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
3948                           TRUE
3949             )
3950          ) /* match found */
3951       {
3952         mdef->mergeReferences(mdec);
3953         mdec->mergeReferences(mdef);
3954         mdef->mergeReferencedBy(mdec);
3955         mdec->mergeReferencedBy(mdef);
3956       }
3957     }
3958   }
3959 }
3960 
3961 //----------------------------------------------------------------------
3962 
transferRelatedFunctionDocumentation()3963 static void transferRelatedFunctionDocumentation()
3964 {
3965   // find match between function declaration and definition for
3966   // related functions
3967   for (const auto &mn : *Doxygen::functionNameLinkedMap)
3968   {
3969     /* find a matching function declaration and definition for this function */
3970     // for each global function
3971     for (const auto &imd : *mn)
3972     {
3973       MemberDefMutable *md = toMemberDefMutable(imd.get());
3974       if (md)
3975       {
3976         //printf("  Function '%s'\n",qPrint(md->name()));
3977         MemberName *rmn;
3978         if ((rmn=Doxygen::memberNameLinkedMap->find(md->name()))) // check if there is a member with the same name
3979         {
3980           //printf("  Member name found\n");
3981           // for each member with the same name
3982           for (const auto &irmd : *rmn)
3983           {
3984             MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
3985             //printf("  Member found: related='%d'\n",rmd->isRelated());
3986             if (rmd &&
3987                 (rmd->isRelated() || rmd->isForeign()) && // related function
3988                 matchArguments2( md->getOuterScope(), md->getFileDef(), &md->argumentList(),
3989                   rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
3990                   TRUE
3991                   )
3992                )
3993             {
3994               //printf("  Found related member '%s'\n",qPrint(md->name()));
3995               if (rmd->relatedAlso())
3996                 md->setRelatedAlso(rmd->relatedAlso());
3997               else if (rmd->isForeign())
3998                 md->makeForeign();
3999               else
4000                 md->makeRelated();
4001             }
4002           }
4003         }
4004       }
4005     }
4006   }
4007 }
4008 
4009 //----------------------------------------------------------------------
4010 
4011 /*! make a dictionary of all template arguments of class cd
4012  * that are part of the base class name.
4013  * Example: A template class A with template arguments <R,S,T>
4014  * that inherits from B<T,T,S> will have T and S in the dictionary.
4015  */
getTemplateArgumentsInName(const ArgumentList & templateArguments,const std::string & name)4016 static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4017 {
4018   std::map<std::string,int> templateNames;
4019   int count=0;
4020   for (const Argument &arg : templateArguments)
4021   {
4022     static const reg::Ex re(R"(\a[\w:]*)");
4023     reg::Iterator it(name,re);
4024     reg::Iterator end;
4025     for (; it!=end ; ++it)
4026     {
4027       const auto &match = *it;
4028       std::string n = match.str();
4029       if (n==arg.name.str())
4030       {
4031         if (templateNames.find(n)==templateNames.end())
4032         {
4033           templateNames.insert(std::make_pair(n,count));
4034         }
4035       }
4036     }
4037   }
4038   return templateNames;
4039 }
4040 
4041 /*! Searches a class from within \a context and \a cd and returns its
4042  *  definition if found (otherwise 0 is returned).
4043  */
findClassWithinClassContext(Definition * context,ClassDef * cd,const QCString & name)4044 static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,const QCString &name)
4045 {
4046   ClassDef *result=0;
4047   if (cd==0)
4048   {
4049     return result;
4050   }
4051   FileDef *fd=cd->getFileDef();
4052   SymbolResolver resolver(fd);
4053   if (context && cd!=context)
4054   {
4055     result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4056   }
4057   //printf("1. result=%p\n",result);
4058   if (result==0)
4059   {
4060     result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4061   }
4062   //printf("2. result=%p\n",result);
4063   if (result==0) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4064   {
4065     result = getClass(name);
4066   }
4067   //printf("3. result=%p\n",result);
4068   //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4069   //       qPrint(name),
4070   //       context ? qPrint(context->name()) : "<none>",
4071   //       cd      ? qPrint(cd->name())      : "<none>",
4072   //       result  ? qPrint(result->name())  : "<none>",
4073   //       Doxygen::classLinkedMap->find(name)
4074   //      );
4075   return result;
4076 }
4077 
4078 
findUsedClassesForClass(const Entry * root,Definition * context,ClassDefMutable * masterCd,ClassDefMutable * instanceCd,bool isArtificial,const std::unique_ptr<ArgumentList> & actualArgs=std::unique_ptr<ArgumentList> (),const TemplateNameMap & templateNames=TemplateNameMap ())4079 static void findUsedClassesForClass(const Entry *root,
4080                            Definition *context,
4081                            ClassDefMutable *masterCd,
4082                            ClassDefMutable *instanceCd,
4083                            bool isArtificial,
4084                            const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4085                            const TemplateNameMap &templateNames = TemplateNameMap()
4086                            )
4087 {
4088   const ArgumentList &formalArgs = masterCd->templateArguments();
4089   for (auto &mni : masterCd->memberNameInfoLinkedMap())
4090   {
4091     for (auto &mi : *mni)
4092     {
4093       const MemberDef *md=mi->memberDef();
4094       if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4095       {
4096         //printf("    Found variable %s in class %s\n",qPrint(md->name()),qPrint(masterCd->name()));
4097         QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4098         QCString typedefValue = resolveTypeDef(masterCd,type);
4099         if (!typedefValue.isEmpty())
4100         {
4101           type = typedefValue;
4102         }
4103         int pos=0;
4104         QCString usedClassName;
4105         QCString templSpec;
4106         bool found=FALSE;
4107         // the type can contain template variables, replace them if present
4108         type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4109 
4110         //printf("      template substitution gives=%s\n",qPrint(type));
4111         while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4112         {
4113           // find the type (if any) that matches usedClassName
4114           SymbolResolver resolver(masterCd->getFileDef());
4115           const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4116           //printf("====>  usedClassName=%s -> typeCd=%s\n",
4117           //     qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4118           if (typeCd)
4119           {
4120             usedClassName = typeCd->name();
4121           }
4122 
4123           int sp=usedClassName.find('<');
4124           if (sp==-1) sp=0;
4125           int si=usedClassName.findRev("::",sp);
4126           if (si!=-1)
4127           {
4128             // replace any namespace aliases
4129             replaceNamespaceAliases(usedClassName,si);
4130           }
4131           // add any template arguments to the class
4132           QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4133           //printf("    usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4134 
4135           TemplateNameMap formTemplateNames;
4136           if (templateNames.empty())
4137           {
4138             formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4139           }
4140           BaseInfo bi(usedName,Public,Normal);
4141           findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4142 
4143           for (const Argument &arg : masterCd->templateArguments())
4144           {
4145             if (arg.name==usedName) // type is a template argument
4146             {
4147               Debug::print(Debug::Classes,0,"    New used class '%s'\n", qPrint(usedName));
4148 
4149               ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4150               ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4151               if (usedCd==0)
4152               {
4153                 usedCdm = toClassDefMutable(
4154                     Doxygen::hiddenClassLinkedMap->add(usedName,
4155                       std::unique_ptr<ClassDef>(
4156                         createClassDef(
4157                           masterCd->getDefFileName(),masterCd->getDefLine(),
4158                           masterCd->getDefColumn(),
4159                           usedName,
4160                           ClassDef::Class))));
4161                 if (usedCdm)
4162                 {
4163                   //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4164                   usedCdm->makeTemplateArgument();
4165                   usedCdm->setUsedOnly(TRUE);
4166                   usedCdm->setLanguage(masterCd->getLanguage());
4167                   usedCd = usedCdm;
4168                 }
4169               }
4170               if (usedCd)
4171               {
4172                 found=TRUE;
4173                 Debug::print(Debug::Classes,0,"      Adding used class '%s' (1)\n", qPrint(usedCd->name()));
4174                 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4175                 if (usedCdm)
4176                 {
4177                   if (isArtificial) usedCdm->setArtificial(TRUE);
4178                   usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4179                 }
4180               }
4181             }
4182           }
4183 
4184           if (!found)
4185           {
4186             ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4187             //printf("Looking for used class %s: result=%s master=%s\n",
4188             //    qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4189 
4190             if (usedCd)
4191             {
4192               found=TRUE;
4193               Debug::print(Debug::Classes,0,"    Adding used class '%s' (2)\n", qPrint(usedCd->name()));
4194               instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4195               ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4196               if (usedCdm)
4197               {
4198                 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4199               }
4200             }
4201           }
4202         }
4203         if (!found && !type.isEmpty()) // used class is not documented in any scope
4204         {
4205           ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4206           ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4207           if (usedCd==0 && !Config_getBool(HIDE_UNDOC_RELATIONS))
4208           {
4209             if (type.right(2)=="(*" || type.right(2)=="(^") // type is a function pointer
4210             {
4211               type+=md->argsString();
4212             }
4213             Debug::print(Debug::Classes,0,"  New undocumented used class '%s'\n", qPrint(type));
4214             usedCdm = toClassDefMutable(
4215                        Doxygen::hiddenClassLinkedMap->add(type,
4216                          std::unique_ptr<ClassDef>(
4217                            createClassDef(
4218                              masterCd->getDefFileName(),masterCd->getDefLine(),
4219                              masterCd->getDefColumn(),
4220                              type,ClassDef::Class))));
4221             if (usedCdm)
4222             {
4223               usedCdm->setUsedOnly(TRUE);
4224               usedCdm->setLanguage(masterCd->getLanguage());
4225               usedCd = usedCdm;
4226             }
4227           }
4228           if (usedCd)
4229           {
4230             Debug::print(Debug::Classes,0,"    Adding used class '%s' (3)\n", qPrint(usedCd->name()));
4231             instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4232             if (usedCdm)
4233             {
4234               if (isArtificial) usedCdm->setArtificial(TRUE);
4235               usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4236             }
4237           }
4238         }
4239       }
4240     }
4241   }
4242 }
4243 
findBaseClassesForClass(const Entry * root,Definition * context,ClassDefMutable * masterCd,ClassDefMutable * instanceCd,FindBaseClassRelation_Mode mode,bool isArtificial,const std::unique_ptr<ArgumentList> & actualArgs=std::unique_ptr<ArgumentList> (),const TemplateNameMap & templateNames=TemplateNameMap ())4244 static void findBaseClassesForClass(
4245       const Entry *root,
4246       Definition *context,
4247       ClassDefMutable *masterCd,
4248       ClassDefMutable *instanceCd,
4249       FindBaseClassRelation_Mode mode,
4250       bool isArtificial,
4251       const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4252       const TemplateNameMap &templateNames=TemplateNameMap()
4253     )
4254 {
4255   // The base class could ofcouse also be a non-nested class
4256   const ArgumentList &formalArgs = masterCd->templateArguments();
4257   for (const BaseInfo &bi : root->extends)
4258   {
4259     //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4260     //    qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4261     TemplateNameMap formTemplateNames;
4262     if (templateNames.empty())
4263     {
4264       formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4265     }
4266     BaseInfo tbi = bi;
4267     tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4268     //printf("bi->name=%s tbi.name=%s\n",qPrint(bi->name),qPrint(tbi.name));
4269 
4270     if (mode==DocumentedOnly)
4271     {
4272       // find a documented base class in the correct scope
4273       if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4274       {
4275         // 1.8.2: decided to show inheritance relations even if not documented,
4276         //        we do make them artificial, so they do not appear in the index
4277         //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4278         bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4279         //{
4280           // no documented base class -> try to find an undocumented one
4281           findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4282         //}
4283       }
4284     }
4285     else if (mode==TemplateInstances)
4286     {
4287       findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4288     }
4289   }
4290 }
4291 
4292 //----------------------------------------------------------------------
4293 
findTemplateInstanceRelation(const Entry * root,Definition * context,ClassDefMutable * templateClass,const QCString & templSpec,const TemplateNameMap & templateNames,bool isArtificial)4294 static void findTemplateInstanceRelation(const Entry *root,
4295             Definition *context,
4296             ClassDefMutable *templateClass,const QCString &templSpec,
4297             const TemplateNameMap &templateNames,
4298             bool isArtificial)
4299 {
4300   Debug::print(Debug::Classes,0,"    derived from template %s with parameters %s isArtificial=%d\n",
4301          qPrint(templateClass->name()),qPrint(templSpec),isArtificial);
4302   //printf("findTemplateInstanceRelation(base=%s templSpec=%s templateNames=",
4303   //    qPrint(templateClass->name()),qPrint(templSpec));
4304   //for (const auto &kv : templNames)
4305   //{
4306   //  printf("(%s->%d) ",kv.first.c_str(),kv.second);
4307   //}
4308   //printf("\n");
4309 
4310   bool existingClass = (templSpec ==
4311                         tempArgListToString(templateClass->templateArguments(),root->lang,false)
4312                        );
4313   if (existingClass) return;
4314 
4315   bool freshInstance=FALSE;
4316   ClassDefMutable *instanceClass = toClassDefMutable(
4317                      templateClass->insertTemplateInstance(
4318                      root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4319   if (instanceClass)
4320   {
4321     instanceClass->setArtificial(TRUE);
4322     instanceClass->setLanguage(root->lang);
4323 
4324     if (freshInstance)
4325     {
4326       Debug::print(Debug::Classes,0,"      found fresh instance '%s'!\n",qPrint(instanceClass->name()));
4327       instanceClass->setTemplateBaseClassNames(templateNames);
4328 
4329       // search for new template instances caused by base classes of
4330       // instanceClass
4331       auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4332       for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4333       {
4334         const Entry *templateRoot = it->second;
4335         Debug::print(Debug::Classes,0,"        template root found %s templSpec=%s!\n",
4336             qPrint(templateRoot->name),qPrint(templSpec));
4337         std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4338         findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4339             TemplateInstances,isArtificial,templArgs,templateNames);
4340 
4341         findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4342             isArtificial,templArgs,templateNames);
4343       }
4344 
4345       //Debug::print(Debug::Classes,0,"    Template instance %s : \n",qPrint(instanceClass->name()));
4346       //ArgumentList *tl = templateClass->templateArguments();
4347     }
4348     else
4349     {
4350       Debug::print(Debug::Classes,0,"      instance already exists!\n");
4351     }
4352   }
4353 }
4354 
isRecursiveBaseClass(const QCString & scope,const QCString & name)4355 static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4356 {
4357   QCString n=name;
4358   int index=n.find('<');
4359   if (index!=-1)
4360   {
4361     n=n.left(index);
4362   }
4363   bool result = rightScopeMatch(scope,n);
4364   return result;
4365 }
4366 
4367 /*! Searches for the end of a template in prototype \a s starting from
4368  *  character position \a startPos. If the end was found the position
4369  *  of the closing \> is returned, otherwise -1 is returned.
4370  *
4371  *  Handles exotic cases such as
4372  *  \code
4373  *    Class<(id<0)>
4374  *    Class<bits<<2>
4375  *    Class<"<">
4376  *    Class<'<'>
4377  *    Class<(")<")>
4378  *  \endcode
4379  */
findEndOfTemplate(const QCString & s,int startPos)4380 static int findEndOfTemplate(const QCString &s,int startPos)
4381 {
4382   // locate end of template
4383   int e=startPos;
4384   int brCount=1;
4385   int roundCount=0;
4386   int len = s.length();
4387   bool insideString=FALSE;
4388   bool insideChar=FALSE;
4389   char pc = 0;
4390   while (e<len && brCount!=0)
4391   {
4392     char c=s.at(e);
4393     switch(c)
4394     {
4395       case '<':
4396         if (!insideString && !insideChar)
4397         {
4398           if (e<len-1 && s.at(e+1)=='<')
4399             e++;
4400           else if (roundCount==0)
4401             brCount++;
4402         }
4403         break;
4404       case '>':
4405         if (!insideString && !insideChar)
4406         {
4407           if (e<len-1 && s.at(e+1)=='>')
4408             e++;
4409           else if (roundCount==0)
4410             brCount--;
4411         }
4412         break;
4413       case '(':
4414         if (!insideString && !insideChar)
4415           roundCount++;
4416         break;
4417       case ')':
4418         if (!insideString && !insideChar)
4419           roundCount--;
4420         break;
4421       case '"':
4422         if (!insideChar)
4423         {
4424           if (insideString && pc!='\\')
4425             insideString=FALSE;
4426           else
4427             insideString=TRUE;
4428         }
4429         break;
4430       case '\'':
4431         if (!insideString)
4432         {
4433           if (insideChar && pc!='\\')
4434             insideChar=FALSE;
4435           else
4436             insideChar=TRUE;
4437         }
4438         break;
4439     }
4440     pc = c;
4441     e++;
4442   }
4443   return brCount==0 ? e : -1;
4444 }
4445 
findTemplateSpecializationPosition(const QCString & name)4446 static int findTemplateSpecializationPosition(const QCString &name)
4447 {
4448   if (name.isEmpty()) return 0;
4449   int l = static_cast<int>(name.length());
4450   if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4451   {
4452     int count=1;
4453     int i=l-2;
4454     char insideQuote=0;
4455     while (count>0 && i>=0)
4456     {
4457       char c = name[i--];
4458       switch (c)
4459       {
4460         case '>':  if (!insideQuote) count++; break;
4461         case '<':  if (!insideQuote) count--; break;
4462         case '\'': if (!insideQuote) insideQuote=c;
4463                    else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4464                    break;
4465         case '"':  if (!insideQuote) insideQuote=c;
4466                    else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4467                    break;
4468         default: break;
4469       }
4470     }
4471     if (i>=0) l=i+1;
4472   }
4473   return l;
4474 }
4475 
findClassRelation(const Entry * root,Definition * context,ClassDefMutable * cd,const BaseInfo * bi,const TemplateNameMap & templateNames,FindBaseClassRelation_Mode mode,bool isArtificial)4476 static bool findClassRelation(
4477                            const Entry *root,
4478                            Definition *context,
4479                            ClassDefMutable *cd,
4480                            const BaseInfo *bi,
4481                            const TemplateNameMap &templateNames,
4482                            FindBaseClassRelation_Mode mode,
4483                            bool isArtificial
4484                           )
4485 {
4486   //printf("findClassRelation(class=%s base=%s templateNames=",
4487   //    qPrint(cd->name()),qPrint(bi->name));
4488   //for (const auto &kv : templateNames)
4489   //{
4490   //  printf("(%s->%d) ",kv.first.c_str(),kv.second);
4491   //}
4492   //printf("\n");
4493 
4494   QCString biName=bi->name;
4495   bool explicitGlobalScope=FALSE;
4496   //printf("findClassRelation: biName='%s'\n",qPrint(biName));
4497   if (biName.left(2)=="::") // explicit global scope
4498   {
4499      biName=biName.right(biName.length()-2);
4500      explicitGlobalScope=TRUE;
4501   }
4502 
4503   Entry *parentNode=root->parent();
4504   bool lastParent=FALSE;
4505   do // for each parent scope, starting with the largest scope
4506      // (in case of nested classes)
4507   {
4508     QCString scopeName= parentNode ? parentNode->name : QCString();
4509     int scopeOffset=explicitGlobalScope ? 0 : scopeName.length();
4510     do // try all parent scope prefixes, starting with the largest scope
4511     {
4512       //printf("scopePrefix='%s' biName='%s'\n",
4513       //    qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4514 
4515       QCString baseClassName=biName;
4516       if (scopeOffset>0)
4517       {
4518         baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4519       }
4520       //QCString stripped;
4521       //baseClassName=stripTemplateSpecifiersFromScope
4522       //                    (removeRedundantWhiteSpace(baseClassName),TRUE,
4523       //                    &stripped);
4524       SymbolResolver resolver(cd->getFileDef());
4525       ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4526                                            baseClassName,
4527                                            mode==Undocumented,
4528                                            true
4529                                           );
4530       const MemberDef *baseClassTypeDef = resolver.getTypedef();
4531       QCString templSpec = resolver.getTemplateSpec();
4532       //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4533       //    qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4534       //printf("    scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4535       //                    cd ? qPrint(cd->name()):"<none>",
4536       //                    qPrint(baseClassName),
4537       //                    baseClass?qPrint(baseClass->name()):"<none>",
4538       //                    qPrint(templSpec)
4539       //      );
4540       //if (baseClassName.left(root->name.length())!=root->name ||
4541       //    baseClassName.at(root->name.length())!='<'
4542       //   ) // Check for base class with the same name.
4543       //     // If found then look in the outer scope for a match
4544       //     // and prevent recursion.
4545       if (!isRecursiveBaseClass(root->name,baseClassName)
4546           || explicitGlobalScope
4547           // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4548           // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4549           || (root->lang==SrcLangExt_IDL &&
4550               (root->section==Entry::EXPORTED_INTERFACE_SEC ||
4551                root->section==Entry::INCLUDED_SERVICE_SEC)))
4552       {
4553         Debug::print(
4554             Debug::Classes,0,"    class relation %s inherited/used by %s found (%s and %s) templSpec='%s'\n",
4555             qPrint(baseClassName),
4556             qPrint(root->name),
4557             (bi->prot==Private)?"private":((bi->prot==Protected)?"protected":"public"),
4558             (bi->virt==Normal)?"normal":"virtual",
4559             qPrint(templSpec)
4560            );
4561 
4562         int i=findTemplateSpecializationPosition(baseClassName);
4563         int si=baseClassName.findRev("::",i);
4564         if (si==-1) si=0;
4565         if (baseClass==0 && static_cast<uint>(i)!=baseClassName.length())
4566           // base class has template specifiers
4567         {
4568           // TODO: here we should try to find the correct template specialization
4569           // but for now, we only look for the unspecialized base class.
4570           int e=findEndOfTemplate(baseClassName,i+1);
4571           //printf("baseClass==0 i=%d e=%d\n",i,e);
4572           if (e!=-1) // end of template was found at e
4573           {
4574             templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4575             baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4576             baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4577                   baseClassName,
4578                   mode==Undocumented,
4579                   true
4580                   );
4581             baseClassTypeDef = resolver.getTypedef();
4582             //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4583             //      baseClass,qPrint(baseClassName),qPrint(templSpec));
4584           }
4585         }
4586         else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4587                                                     // know it is a template, so see if
4588                                                     // we can also link to the explicit
4589                                                     // instance (for instance if a class
4590                                                     // derived from a template argument)
4591         {
4592           //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4593           ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4594           if (templClass)
4595           {
4596             // use the template instance instead of the template base.
4597             baseClass = templClass;
4598             templSpec.resize(0);
4599           }
4600         }
4601 
4602         //printf("cd=%p baseClass=%p\n",cd,baseClass);
4603         bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances);
4604         //printf("1. found=%d\n",found);
4605         if (!found && si!=-1)
4606         {
4607           // replace any namespace aliases
4608           replaceNamespaceAliases(baseClassName,si);
4609           baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4610                                      baseClassName,
4611                                      mode==Undocumented,
4612                                      true
4613                                     );
4614           baseClassTypeDef = resolver.getTypedef();
4615           QCString tmpTemplSpec = resolver.getTemplateSpec();
4616           found=baseClass!=0 && baseClass!=cd;
4617           if (found) templSpec = tmpTemplSpec;
4618         }
4619         //printf("2. found=%d\n",found);
4620 
4621         if (!found)
4622         {
4623           baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4624           //printf("findClassWithinClassContext(%s,%s)=%p\n",
4625           //    qPrint(cd->name()),qPrint(baseClassName),baseClass);
4626           found = baseClass!=0 && baseClass!=cd;
4627 
4628         }
4629         //printf("3. found=%d\n",found);
4630         if (!found)
4631         {
4632           // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4633           // the class name also in the alias mapping.
4634           auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4635           if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4636           {
4637             baseClass=getClassMutable(it->second.c_str());
4638             found = baseClass!=0 && baseClass!=cd;
4639           }
4640         }
4641         bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
4642         // make templSpec canonical
4643         // warning: the following line doesn't work for Mixin classes (see bug 560623)
4644         // templSpec = getCanonicalTemplateSpec(cd, cd->getFileDef(), templSpec);
4645 
4646         //printf("4. found=%d\n",found);
4647         if (found)
4648         {
4649           Debug::print(Debug::Classes,0,"    Documented base class '%s' templSpec=%s\n",qPrint(biName),qPrint(templSpec));
4650           // add base class to this class
4651 
4652           // if templSpec is not empty then we should "instantiate"
4653           // the template baseClass. A new ClassDef should be created
4654           // to represent the instance. To be able to add the (instantiated)
4655           // members and documentation of a template class
4656           // (inserted in that template class at a later stage),
4657           // the template should know about its instances.
4658           // the instantiation process, should be done in a recursive way,
4659           // since instantiating a template may introduce new inheritance
4660           // relations.
4661           if (!templSpec.isEmpty() && mode==TemplateInstances)
4662           {
4663             // if baseClass is actually a typedef then we should not
4664             // instantiate it, since typedefs are in a different namespace
4665             // see bug531637 for an example where this would otherwise hang
4666             // doxygen
4667             if (baseClassTypeDef==0)
4668             {
4669               //printf("       => findTemplateInstanceRelation: %p\n",baseClassTypeDef);
4670               findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
4671             }
4672           }
4673           else if (mode==DocumentedOnly || mode==Undocumented)
4674           {
4675             //printf("       => insert base class\n");
4676             QCString usedName;
4677             if (baseClassTypeDef || cd->isCSharp())
4678             {
4679               usedName=biName;
4680               //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
4681             }
4682             Protection prot = bi->prot;
4683             if (Config_getBool(SIP_SUPPORT)) prot=Public;
4684             if (!cd->isSubClass(baseClass)) // check for recursion, see bug690787
4685             {
4686               cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
4687               // add this class as super class to the base class
4688               baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
4689             }
4690             else
4691             {
4692               warn(root->fileName,root->startLine,
4693                   "Detected potential recursive class relation "
4694                   "between class %s and base class %s!",
4695                   qPrint(cd->name()),qPrint(baseClass->name())
4696                   );
4697             }
4698           }
4699           return TRUE;
4700         }
4701         else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
4702         {
4703           Debug::print(Debug::Classes,0,
4704                        "    New undocumented base class '%s' baseClassName=%s templSpec=%s isArtificial=%d\n",
4705                        qPrint(biName),qPrint(baseClassName),qPrint(templSpec),isArtificial
4706                       );
4707           baseClass=0;
4708           if (isATemplateArgument)
4709           {
4710             baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
4711             if (baseClass==0) // not found (or alias)
4712             {
4713               baseClass= toClassDefMutable(
4714                 Doxygen::hiddenClassLinkedMap->add(baseClassName,
4715                   std::unique_ptr<ClassDef>(
4716                     createClassDef(root->fileName,root->startLine,root->startColumn,
4717                                  baseClassName,
4718                                  ClassDef::Class))));
4719               if (baseClass) // really added (not alias)
4720               {
4721                 if (isArtificial) baseClass->setArtificial(TRUE);
4722                 baseClass->setLanguage(root->lang);
4723               }
4724             }
4725           }
4726           else
4727           {
4728             baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
4729             //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
4730             //    qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
4731             if (baseClass==0) // not found (or alias)
4732             {
4733               baseClass = toClassDefMutable(
4734                   Doxygen::classLinkedMap->add(baseClassName,
4735                     std::unique_ptr<ClassDef>(
4736                       createClassDef(root->fileName,root->startLine,root->startColumn,
4737                         baseClassName,
4738                         ClassDef::Class))));
4739               if (baseClass) // really added (not alias)
4740               {
4741                 if (isArtificial) baseClass->setArtificial(TRUE);
4742                 baseClass->setLanguage(root->lang);
4743                 si = baseClassName.findRev("::");
4744                 if (si!=-1) // class is nested
4745                 {
4746                   Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),0,root->tagInfo());
4747                   if (sd==0 || sd==Doxygen::globalScope) // outer scope not found
4748                   {
4749                     baseClass->setArtificial(TRUE); // see bug678139
4750                   }
4751                 }
4752               }
4753             }
4754           }
4755           if (baseClass)
4756           {
4757             if (biName.right(2)=="-p")
4758             {
4759               biName="<"+biName.left(biName.length()-2)+">";
4760             }
4761             // add base class to this class
4762             cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
4763             // add this class as super class to the base class
4764             baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
4765             // the undocumented base was found in this file
4766             baseClass->insertUsedFile(root->fileDef());
4767 
4768             Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,0);
4769             if (scope!=baseClass)
4770             {
4771               baseClass->setOuterScope(scope);
4772             }
4773 
4774             if (baseClassName.right(2)=="-p")
4775             {
4776               baseClass->setCompoundType(ClassDef::Protocol);
4777             }
4778             return TRUE;
4779           }
4780           else
4781           {
4782             Debug::print(Debug::Classes,0,"    Base class '%s' not created (alias?)\n",qPrint(biName));
4783           }
4784         }
4785         else
4786         {
4787           Debug::print(Debug::Classes,0,"    Base class '%s' not found\n",qPrint(biName));
4788         }
4789       }
4790       else
4791       {
4792         if (mode!=TemplateInstances)
4793         {
4794           warn(root->fileName,root->startLine,
4795               "Detected potential recursive class relation "
4796               "between class %s and base class %s!\n",
4797               qPrint(root->name),qPrint(baseClassName)
4798               );
4799         }
4800         // for mode==TemplateInstance this case is quite common and
4801         // indicates a relation between a template class and a template
4802         // instance with the same name.
4803       }
4804       if (scopeOffset==0)
4805       {
4806         scopeOffset=-1;
4807       }
4808       else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4809       {
4810         scopeOffset=0;
4811       }
4812       //printf("new scopeOffset='%d'",scopeOffset);
4813     } while (scopeOffset>=0);
4814 
4815     if (parentNode==0)
4816     {
4817       lastParent=TRUE;
4818     }
4819     else
4820     {
4821       parentNode=parentNode->parent();
4822     }
4823   } while (lastParent);
4824 
4825   return FALSE;
4826 }
4827 
4828 //----------------------------------------------------------------------
4829 // Computes the base and super classes for each class in the tree
4830 
isClassSection(const Entry * root)4831 static bool isClassSection(const Entry *root)
4832 {
4833   if ( !root->name.isEmpty() )
4834   {
4835     if (root->section & Entry::COMPOUND_MASK)
4836          // is it a compound (class, struct, union, interface ...)
4837     {
4838       return TRUE;
4839     }
4840     else if (root->section & Entry::COMPOUNDDOC_MASK)
4841          // is it a documentation block with inheritance info.
4842     {
4843       bool hasExtends = !root->extends.empty();
4844       if (hasExtends) return TRUE;
4845     }
4846   }
4847   return FALSE;
4848 }
4849 
4850 
4851 /*! Builds a dictionary of all entry nodes in the tree starting with \a root
4852  */
findClassEntries(const Entry * root)4853 static void findClassEntries(const Entry *root)
4854 {
4855   if (isClassSection(root))
4856   {
4857     g_classEntries.insert({root->name.str(),root});
4858   }
4859   for (const auto &e : root->children()) findClassEntries(e.get());
4860 }
4861 
extractClassName(const Entry * root)4862 static QCString extractClassName(const Entry *root)
4863 {
4864   // strip any anonymous scopes first
4865   QCString bName=stripAnonymousNamespaceScope(root->name);
4866   bName=stripTemplateSpecifiersFromScope(bName);
4867   int i;
4868   if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) &&
4869       (i=bName.find('<'))!=-1)
4870   {
4871     // a Java/C# generic class looks like a C++ specialization, so we need to strip the
4872     // template part before looking for matches
4873     bName=bName.left(i);
4874   }
4875   return bName;
4876 }
4877 
4878 /*! Using the dictionary build by findClassEntries(), this
4879  *  function will look for additional template specialization that
4880  *  exists as inheritance relations only. These instances will be
4881  *  added to the template they are derived from.
4882  */
findInheritedTemplateInstances()4883 static void findInheritedTemplateInstances()
4884 {
4885   ClassDefSet visitedClasses;
4886   for (const auto &kv : g_classEntries)
4887   {
4888     const Entry *root = kv.second;
4889     ClassDef *cd;
4890     QCString bName = extractClassName(root);
4891     Debug::print(Debug::Classes,0,"  Inheritance: Class %s : \n",qPrint(bName));
4892     if ((cd=getClass(bName)))
4893     {
4894       ClassDefMutable *cdm = toClassDefMutable(cd);
4895       if (cdm)
4896       {
4897         //printf("Class %s %zu\n",qPrint(cd->name()),root->extends.size());
4898         findBaseClassesForClass(root,cd,cdm,cdm,TemplateInstances,FALSE);
4899       }
4900     }
4901   }
4902 }
4903 
findUsedTemplateInstances()4904 static void findUsedTemplateInstances()
4905 {
4906   for (const auto &kv : g_classEntries)
4907   {
4908     const Entry *root = kv.second;
4909     ClassDef *cd;
4910     QCString bName = extractClassName(root);
4911     Debug::print(Debug::Classes,0,"  Usage: Class %s : \n",qPrint(bName));
4912     if ((cd=getClass(bName)))
4913     {
4914       ClassDefMutable *cdm = toClassDefMutable(cd);
4915       if (cdm)
4916       {
4917         findUsedClassesForClass(root,cd,cdm,cdm,TRUE);
4918         cdm->addTypeConstraints();
4919       }
4920     }
4921   }
4922 }
4923 
computeClassRelations()4924 static void computeClassRelations()
4925 {
4926   for (const auto &kv : g_classEntries)
4927   {
4928     const Entry *root = kv.second;
4929     ClassDefMutable *cd;
4930 
4931     QCString bName = extractClassName(root);
4932     Debug::print(Debug::Classes,0,"  Relations: Class %s : \n",qPrint(bName));
4933     if ((cd=getClassMutable(bName)))
4934     {
4935       findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,FALSE);
4936     }
4937     size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
4938     if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 &&
4939         bName.right(2)!="::")
4940     {
4941       if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
4942           (guessSection(root->fileName)==Entry::HEADER_SEC ||
4943            Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
4944            protectionLevelVisible(root->protection) && // hidden by protection
4945            !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
4946          )
4947         warn_undoc(
4948                    root->fileName,root->startLine,
4949                    "Compound %s is not documented.",
4950                    qPrint(root->name)
4951              );
4952     }
4953   }
4954 }
4955 
computeTemplateClassRelations()4956 static void computeTemplateClassRelations()
4957 {
4958   for (const auto &kv : g_classEntries)
4959   {
4960     const Entry *root = kv.second;
4961     QCString bName=stripAnonymousNamespaceScope(root->name);
4962     bName=stripTemplateSpecifiersFromScope(bName);
4963     ClassDefMutable *cd=getClassMutable(bName);
4964     // strip any anonymous scopes first
4965     if (cd && !cd->getTemplateInstances().empty())
4966     {
4967       Debug::print(Debug::Classes,0,"  Template class %s : \n",qPrint(cd->name()));
4968       for (const auto &ti : cd->getTemplateInstances()) // for each template instance
4969       {
4970         ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
4971         if (tcd)
4972         {
4973           Debug::print(Debug::Classes,0,"    Template instance %s : \n",qPrint(tcd->name()));
4974           QCString templSpec = ti.templSpec;
4975           std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
4976           for (const BaseInfo &bi : root->extends)
4977           {
4978             // check if the base class is a template argument
4979             BaseInfo tbi = bi;
4980             const ArgumentList &tl = cd->templateArguments();
4981             if (!tl.empty())
4982             {
4983               TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
4984               TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
4985               // for each template name that we inherit from we need to
4986               // substitute the formal with the actual arguments
4987               TemplateNameMap actualTemplateNames;
4988               for (const auto &tn_kv : templateNames)
4989               {
4990                 int templIndex = tn_kv.second;
4991                 Argument actArg;
4992                 bool hasActArg=FALSE;
4993                 if (templIndex<(int)templArgs->size())
4994                 {
4995                   actArg=templArgs->at(templIndex);
4996                   hasActArg=TRUE;
4997                 }
4998                 if (hasActArg &&
4999                     baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5000                     actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5001                    )
5002                 {
5003                   actualTemplateNames.insert(std::make_pair(actArg.type.str(),templIndex));
5004                 }
5005               }
5006 
5007               tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs);
5008               // find a documented base class in the correct scope
5009               if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5010               {
5011                 // no documented base class -> try to find an undocumented one
5012                 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5013               }
5014             }
5015           }
5016         }
5017       }
5018     }
5019   }
5020 }
5021 
5022 //-----------------------------------------------------------------------
5023 // compute the references (anchors in HTML) for each function in the file
5024 
computeMemberReferences()5025 static void computeMemberReferences()
5026 {
5027   for (const auto &cd : *Doxygen::classLinkedMap)
5028   {
5029     ClassDefMutable *cdm = toClassDefMutable(cd.get());
5030     if (cdm)
5031     {
5032       cdm->computeAnchors();
5033     }
5034   }
5035   for (const auto &fn : *Doxygen::inputNameLinkedMap)
5036   {
5037     for (const auto &fd : *fn)
5038     {
5039       fd->computeAnchors();
5040     }
5041   }
5042   for (const auto &nd : *Doxygen::namespaceLinkedMap)
5043   {
5044     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
5045     if (ndm)
5046     {
5047       ndm->computeAnchors();
5048     }
5049   }
5050   for (const auto &gd : *Doxygen::groupLinkedMap)
5051   {
5052     gd->computeAnchors();
5053   }
5054 }
5055 
5056 //----------------------------------------------------------------------
5057 
addListReferences()5058 static void addListReferences()
5059 {
5060   for (const auto &cd : *Doxygen::classLinkedMap)
5061   {
5062     ClassDefMutable *cdm = toClassDefMutable(cd.get());
5063     if (cdm)
5064     {
5065       cdm->addListReferences();
5066     }
5067   }
5068 
5069   for (const auto &fn : *Doxygen::inputNameLinkedMap)
5070   {
5071     for (const auto &fd : *fn)
5072     {
5073       fd->addListReferences();
5074     }
5075   }
5076 
5077   for (const auto &nd : *Doxygen::namespaceLinkedMap)
5078   {
5079     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
5080     if (ndm)
5081     {
5082       ndm->addListReferences();
5083     }
5084   }
5085 
5086   for (const auto &gd : *Doxygen::groupLinkedMap)
5087   {
5088     gd->addListReferences();
5089   }
5090 
5091   for (const auto &pd : *Doxygen::pageLinkedMap)
5092   {
5093     QCString name = pd->getOutputFileBase();
5094     if (pd->getGroupDef())
5095     {
5096       name = pd->getGroupDef()->getOutputFileBase();
5097     }
5098     {
5099       const RefItemVector &xrefItems = pd->xrefListItems();
5100       addRefItem(xrefItems,
5101           name,
5102           theTranslator->trPage(TRUE,TRUE),
5103           name,pd->title(),QCString(),0);
5104     }
5105   }
5106 
5107   for (const auto &dd : *Doxygen::dirLinkedMap)
5108   {
5109     QCString name = dd->getOutputFileBase();
5110     //if (dd->getGroupDef())
5111     //{
5112     //  name = dd->getGroupDef()->getOutputFileBase();
5113     //}
5114     const RefItemVector &xrefItems = dd->xrefListItems();
5115     addRefItem(xrefItems,
5116         name,
5117         theTranslator->trDir(TRUE,TRUE),
5118         name,dd->displayName(),QCString(),0);
5119   }
5120 }
5121 
5122 //----------------------------------------------------------------------
5123 
generateXRefPages()5124 static void generateXRefPages()
5125 {
5126   for (RefListManager::Ptr &rl : RefListManager::instance())
5127   {
5128     rl->generatePage();
5129   }
5130 }
5131 
5132 //----------------------------------------------------------------------
5133 // Copy the documentation in entry 'root' to member definition 'md' and
5134 // set the function declaration of the member to 'funcDecl'. If the boolean
5135 // over_load is set the standard overload text is added.
5136 
addMemberDocs(const Entry * root,MemberDefMutable * md,const QCString & funcDecl,const ArgumentList * al,bool over_load,uint64 spec)5137 static void addMemberDocs(const Entry *root,
5138                    MemberDefMutable *md, const QCString &funcDecl,
5139                    const ArgumentList *al,
5140                    bool over_load,
5141                    uint64 spec
5142                   )
5143 {
5144   if (md==0) return;
5145   //printf("addMemberDocs: '%s'::'%s' '%s' funcDecl='%s' mSpec=%lld\n",
5146   //     qPrint(root->parent()->name),qPrint(md->name()),qPrint(md->argsString()),qPrint(funcDecl),spec);
5147   QCString fDecl=funcDecl;
5148   // strip extern specifier
5149   fDecl.stripPrefix("extern ");
5150   md->setDefinition(fDecl);
5151   md->enableCallGraph(root->callGraph);
5152   md->enableCallerGraph(root->callerGraph);
5153   md->enableReferencedByRelation(root->referencedByRelation);
5154   md->enableReferencesRelation(root->referencesRelation);
5155   ClassDefMutable *cd=md->getClassDefMutable();
5156   const NamespaceDef *nd=md->getNamespaceDef();
5157   QCString fullName;
5158   if (cd)
5159     fullName = cd->name();
5160   else if (nd)
5161     fullName = nd->name();
5162 
5163   if (!fullName.isEmpty()) fullName+="::";
5164   fullName+=md->name();
5165   FileDef *rfd=root->fileDef();
5166 
5167   // TODO determine scope based on root not md
5168   Definition *rscope = md->getOuterScope();
5169 
5170   const ArgumentList &mdAl = md->argumentList();
5171   if (al)
5172   {
5173     ArgumentList mergedAl = *al;
5174     //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5175     mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5176   }
5177   else
5178   {
5179     if (
5180           matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5181                            rscope,rfd,&root->argList,
5182                            TRUE
5183                          )
5184        )
5185     {
5186       //printf("merging arguments (2)\n");
5187       ArgumentList mergedArgList = root->argList;
5188       mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5189     }
5190   }
5191   if (over_load)  // the \overload keyword was used
5192   {
5193     QCString doc=getOverloadDocs();
5194     if (!root->doc.isEmpty())
5195     {
5196       doc+="<p>";
5197       doc+=root->doc;
5198     }
5199     md->setDocumentation(doc,root->docFile,root->docLine);
5200     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5201     md->setDocsForDefinition(!root->proto);
5202   }
5203   else
5204   {
5205     //printf("overwrite!\n");
5206     md->setDocumentation(root->doc,root->docFile,root->docLine);
5207     md->setDocsForDefinition(!root->proto);
5208 
5209     //printf("overwrite!\n");
5210     md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5211 
5212     if (
5213         (md->inbodyDocumentation().isEmpty() ||
5214          !root->parent()->name.isEmpty()
5215         ) && !root->inbodyDocs.isEmpty()
5216        )
5217     {
5218       md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5219     }
5220   }
5221 
5222   //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5223   //    qPrint(md->initializer()),md->initializer().isEmpty(),
5224   //    qPrint(root->initializer),root->initializer.isEmpty()
5225   //   );
5226   std::string rootInit = root->initializer.str();
5227   if (md->initializer().isEmpty() && !rootInit.empty())
5228   {
5229     //printf("setInitializer\n");
5230     md->setInitializer(rootInit.c_str());
5231   }
5232   if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5233   {
5234     md->setRequiresClause(root->req);
5235   }
5236 
5237   md->setMaxInitLines(root->initLines);
5238 
5239   if (rfd)
5240   {
5241     if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5242        )
5243     {
5244       //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5245       md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5246       md->setBodyDef(rfd);
5247     }
5248 
5249     md->setRefItems(root->sli);
5250   }
5251 
5252   md->enableCallGraph(md->hasCallGraph() || root->callGraph);
5253   md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
5254   md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
5255   md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
5256 
5257   md->mergeMemberSpecifiers(spec);
5258   md->addSectionsToDefinition(root->anchors);
5259   addMemberToGroups(root,md);
5260   if (cd) cd->insertUsedFile(rfd);
5261   //printf("root->mGrpId=%d\n",root->mGrpId);
5262   if (root->mGrpId!=-1)
5263   {
5264     if (md->getMemberGroupId()!=-1)
5265     {
5266       if (md->getMemberGroupId()!=root->mGrpId)
5267       {
5268         warn(
5269              root->fileName,root->startLine,
5270              "member %s belongs to two different groups. The second "
5271              "one found here will be ignored.",
5272              qPrint(md->name())
5273             );
5274       }
5275     }
5276     else // set group id
5277     {
5278       //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5279       md->setMemberGroupId(root->mGrpId);
5280     }
5281   }
5282 }
5283 
5284 //----------------------------------------------------------------------
5285 // find a class definition given the scope name and (optionally) a
5286 // template list specifier
5287 
findClassDefinition(FileDef * fd,NamespaceDef * nd,const QCString & scopeName)5288 static const ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd,
5289                          const QCString &scopeName)
5290 {
5291   SymbolResolver resolver(fd);
5292   const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5293   return tcd;
5294 }
5295 
5296 
5297 //----------------------------------------------------------------------
5298 // Adds the documentation contained in 'root' to a global function
5299 // with name 'name' and argument list 'args' (for overloading) and
5300 // function declaration 'decl' to the corresponding member definition.
5301 
findGlobalMember(const Entry * root,const QCString & namespaceName,const QCString & type,const QCString & name,const QCString & tempArg,const QCString &,const QCString & decl,uint64 spec)5302 static bool findGlobalMember(const Entry *root,
5303                            const QCString &namespaceName,
5304                            const QCString &type,
5305                            const QCString &name,
5306                            const QCString &tempArg,
5307                            const QCString &,
5308                            const QCString &decl,
5309                            uint64 spec)
5310 {
5311   Debug::print(Debug::FindMembers,0,
5312        "2. findGlobalMember(namespace=%s,type=%s,name=%s,tempArg=%s,decl=%s)\n",
5313           qPrint(namespaceName),qPrint(type),qPrint(name),qPrint(tempArg),qPrint(decl));
5314   QCString n=name;
5315   if (n.isEmpty()) return FALSE;
5316   if (n.find("::")!=-1) return FALSE; // skip undefined class members
5317   MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5318   if (mn==0)
5319   {
5320     mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5321   }
5322   if (mn) // function name defined
5323   {
5324     Debug::print(Debug::FindMembers,0,"3. Found symbol scope\n");
5325     //int count=0;
5326     bool found=FALSE;
5327     for (const auto &md : *mn)
5328     {
5329       const NamespaceDef *nd=0;
5330       if (md->isAlias() && md->getOuterScope() &&
5331           md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5332       {
5333         nd = toNamespaceDef(md->getOuterScope());
5334       }
5335       else
5336       {
5337         nd = md->getNamespaceDef();
5338       }
5339 
5340       // special case for strong enums
5341       int enumNamePos=0;
5342       if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5343       { // md part of a strong enum in a namespace?
5344         QCString enumName = namespaceName.mid(enumNamePos+2);
5345         if (namespaceName.left(enumNamePos)==nd->name())
5346         {
5347           MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5348           if (enumMn)
5349           {
5350             for (const auto &emd : *enumMn)
5351             {
5352               found = emd->isStrong() && md->getEnumScope()==emd.get();
5353               if (found)
5354               {
5355                 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5356                 break;
5357               }
5358             }
5359           }
5360         }
5361         if (found)
5362         {
5363           break;
5364         }
5365       }
5366       else if (nd==0 && md->isEnumValue()) // md part of global strong enum?
5367       {
5368         MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5369         if (enumMn)
5370         {
5371           for (const auto &emd : *enumMn)
5372           {
5373             found = emd->isStrong() && md->getEnumScope()==emd.get();
5374             if (found)
5375             {
5376               addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5377               break;
5378             }
5379           }
5380         }
5381       }
5382 
5383       const FileDef *fd=root->fileDef();
5384       //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5385       LinkedRefMap<const NamespaceDef> nl;
5386       if (fd)
5387       {
5388         nl = fd->getUsedNamespaces();
5389       }
5390       //printf("NamespaceList %p\n",nl);
5391 
5392       // search in the list of namespaces that are imported via a
5393       // using declaration
5394       bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=0;
5395 
5396       if ((namespaceName.isEmpty() && nd==0) ||  // not in a namespace
5397           (nd && nd->name()==namespaceName) ||   // or in the same namespace
5398           viaUsingDirective                      // member in 'using' namespace
5399          )
5400       {
5401         Debug::print(Debug::FindMembers,0,"4. Try to add member '%s' to scope '%s'\n",
5402             qPrint(md->name()),qPrint(namespaceName));
5403 
5404         NamespaceDef *rnd = 0;
5405         if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5406 
5407         const ArgumentList &mdAl = const_cast<const MemberDef *>(md.get())->argumentList();
5408         bool matching=
5409           (mdAl.empty() && root->argList.empty()) ||
5410           md->isVariable() || md->isTypedef() || /* in case of function pointers */
5411           matchArguments2(md->getOuterScope(),const_cast<const MemberDef *>(md.get())->getFileDef(),&mdAl,
5412                           rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5413                           FALSE);
5414 
5415         // for template members we need to check if the number of
5416         // template arguments is the same, otherwise we are dealing with
5417         // different functions.
5418         if (matching && !root->tArgLists.empty())
5419         {
5420           const ArgumentList &mdTempl = md->templateArguments();
5421           if (root->tArgLists.back().size()!=mdTempl.size())
5422           {
5423             matching=FALSE;
5424           }
5425         }
5426 
5427         //printf("%s<->%s\n",
5428         //    qPrint(argListToString(md->argumentList())),
5429         //    qPrint(argListToString(root->argList)));
5430 
5431         // for static members we also check if the comment block was found in
5432         // the same file. This is needed because static members with the same
5433         // name can be in different files. Thus it would be wrong to just
5434         // put the comment block at the first syntactically matching member.
5435         if (matching && md->isStatic() &&
5436             md->getDefFileName()!=root->fileName &&
5437             mn->size()>1)
5438         {
5439           matching = FALSE;
5440         }
5441 
5442         // for template member we also need to check the return type and requires
5443         if (!md->templateArguments().empty() && !root->tArgLists.empty())
5444         {
5445           //printf("Comparing return types '%s'<->'%s'\n",
5446           //    md->typeString(),type);
5447           if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5448               md->typeString()!=type ||
5449               md->requiresClause()!=root->req)
5450           {
5451             //printf(" ---> no matching\n");
5452             matching = FALSE;
5453           }
5454         }
5455 
5456         if (matching) // add docs to the member
5457         {
5458           Debug::print(Debug::FindMembers,0,"5. Match found\n");
5459           addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5460           found=TRUE;
5461           break;
5462         }
5463       }
5464     }
5465     if (!found && root->relatesType != Duplicate && root->section==Entry::FUNCTION_SEC) // no match
5466     {
5467       QCString fullFuncDecl=decl;
5468       if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5469       QCString warnMsg =
5470          QCString("no matching file member found for \n")+substitute(fullFuncDecl,"%","%%");
5471       if (mn->size()>0)
5472       {
5473         warnMsg+="\nPossible candidates:\n";
5474         for (const auto &md : *mn)
5475         {
5476           warnMsg+=" '";
5477           warnMsg+=substitute(md->declaration(),"%","%%");
5478           warnMsg+="' at line "+QCString().setNum(md->getDefLine())+
5479                    " of file "+md->getDefFileName()+"\n";
5480         }
5481       }
5482       warn(root->fileName,root->startLine, "%s", qPrint(warnMsg));
5483     }
5484   }
5485   else // got docs for an undefined member!
5486   {
5487     if (root->type!="friend class" &&
5488         root->type!="friend struct" &&
5489         root->type!="friend union" &&
5490         root->type!="friend" &&
5491         (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5492          root->type.find("typedef ")==-1)
5493        )
5494     {
5495       warn(root->fileName,root->startLine,
5496            "documented symbol '%s' was not declared or defined.",qPrint(decl)
5497           );
5498     }
5499   }
5500   return TRUE;
5501 }
5502 
isSpecialization(const ArgumentLists & srcTempArgLists,const ArgumentLists & dstTempArgLists)5503 static bool isSpecialization(
5504                   const ArgumentLists &srcTempArgLists,
5505                   const ArgumentLists &dstTempArgLists
5506     )
5507 {
5508     auto srcIt = srcTempArgLists.begin();
5509     auto dstIt = dstTempArgLists.begin();
5510     while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5511     {
5512       if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5513       ++srcIt;
5514       ++dstIt;
5515     }
5516     return FALSE;
5517 }
5518 
scopeIsTemplate(const Definition * d)5519 static bool scopeIsTemplate(const Definition *d)
5520 {
5521   bool result=FALSE;
5522   if (d && d->definitionType()==Definition::TypeClass)
5523   {
5524     result = !(toClassDef(d))->templateArguments().empty() ||
5525              scopeIsTemplate(d->getOuterScope());
5526   }
5527   return result;
5528 }
5529 
substituteTemplatesInString(const ArgumentLists & srcTempArgLists,const ArgumentLists & dstTempArgLists,const std::string & src)5530 static QCString substituteTemplatesInString(
5531     const ArgumentLists &srcTempArgLists,
5532     const ArgumentLists &dstTempArgLists,
5533     const std::string &src
5534     )
5535 {
5536   std::string dst;
5537   static const reg::Ex re(R"(\a\w*)");
5538   reg::Iterator it(src,re);
5539   reg::Iterator end;
5540   //printf("type=%s\n",qPrint(sa->type));
5541   size_t p=0;
5542   for (; it!=end ; ++it) // for each word in srcType
5543   {
5544     const auto &match = *it;
5545     size_t i = match.position();
5546     size_t l = match.length();
5547     bool found=FALSE;
5548     dst+=src.substr(p,i-p);
5549     std::string name=match.str();
5550 
5551     auto srcIt = srcTempArgLists.begin();
5552     auto dstIt = dstTempArgLists.begin();
5553     while (srcIt!=srcTempArgLists.end() && !found)
5554     {
5555       const ArgumentList *tdAli = 0;
5556       std::vector<Argument>::const_iterator tdaIt;
5557       if (dstIt!=dstTempArgLists.end())
5558       {
5559         tdAli = &(*dstIt);
5560         tdaIt = tdAli->begin();
5561         ++dstIt;
5562       }
5563 
5564       const ArgumentList &tsaLi = *srcIt;
5565       for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5566       {
5567         Argument tsa = *tsaIt;
5568         const Argument *tda = 0;
5569         if (tdAli && tdaIt!=tdAli->end())
5570         {
5571           tda = &(*tdaIt);
5572           ++tdaIt;
5573         }
5574         //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5575         //    qPrint(tsa.type),qPrint(tsa.name),
5576         //    qPrint(tda->type),qPrint(tda->name));
5577         if (name==tsa.name.str())
5578         {
5579           if (tda && tda->name.isEmpty())
5580           {
5581             QCString tdaName = tda->name;
5582             QCString tdaType = tda->type;
5583             int vc=0;
5584             if (tdaType.left(6)=="class ") vc=6;
5585             else if (tdaType.left(9)=="typename ") vc=9;
5586             if (vc>0) // convert type=="class T" to type=="class" name=="T"
5587             {
5588               tdaName = tdaType.mid(vc);
5589             }
5590             if (!tdaName.isEmpty())
5591             {
5592               name=tdaName.str(); // substitute
5593               found=TRUE;
5594             }
5595           }
5596         }
5597       }
5598 
5599       //printf("   srcList='%s' dstList='%s faList='%s'\n",
5600       //  qPrint(argListToString(srclali.current())),
5601       //  qPrint(argListToString(dstlali.current())),
5602       //  funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
5603       ++srcIt;
5604     }
5605     dst+=name;
5606     p=i+l;
5607   }
5608   dst+=src.substr(p);
5609   //printf("  substituteTemplatesInString(%s)=%s\n",
5610   //    qPrint(src),qPrint(dst));
5611   return QCString(dst);
5612 }
5613 
substituteTemplatesInArgList(const ArgumentLists & srcTempArgLists,const ArgumentLists & dstTempArgLists,const ArgumentList & src,ArgumentList & dst)5614 static void substituteTemplatesInArgList(
5615                   const ArgumentLists &srcTempArgLists,
5616                   const ArgumentLists &dstTempArgLists,
5617                   const ArgumentList &src,
5618                   ArgumentList &dst
5619                  )
5620 {
5621   auto dstIt = dst.begin();
5622   for (const Argument &sa : src)
5623   {
5624     QCString dstType =  substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
5625     QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
5626     if (dstIt == dst.end())
5627     {
5628       Argument da = sa;
5629       da.type  = dstType;
5630       da.array = dstArray;
5631       dst.push_back(da);
5632       dstIt = dst.end();
5633     }
5634     else
5635     {
5636       Argument da = *dstIt;
5637       da.type  = dstType;
5638       da.array = dstArray;
5639       ++dstIt;
5640     }
5641   }
5642   dst.setConstSpecifier(src.constSpecifier());
5643   dst.setVolatileSpecifier(src.volatileSpecifier());
5644   dst.setPureSpecifier(src.pureSpecifier());
5645   dst.setTrailingReturnType(substituteTemplatesInString(
5646                              srcTempArgLists,dstTempArgLists,
5647                              src.trailingReturnType().str()));
5648   //printf("substituteTemplatesInArgList: replacing %s with %s\n",
5649   //    qPrint(argListToString(src)),qPrint(argListToString(dst))
5650   //    );
5651 }
5652 
5653 //-------------------------------------------------------------------------------------------
5654 
addLocalObjCMethod(const Entry * root,const QCString & scopeName,const QCString & funcType,const QCString & funcName,const QCString & funcArgs,const QCString & exceptions,const QCString & funcDecl,uint64 spec)5655 static void addLocalObjCMethod(const Entry *root,
5656                         const QCString &scopeName,
5657                         const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
5658                         const QCString &exceptions,const QCString &funcDecl,
5659                         uint64 spec)
5660 {
5661   //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
5662   ClassDefMutable *cd=0;
5663   if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
5664   {
5665     Debug::print(Debug::FindMembers,0,"4. Local objective C method %s\n"
5666         "  scopeName=%s\n",qPrint(root->name),qPrint(scopeName));
5667     //printf("Local objective C method '%s' of class '%s' found\n",qPrint(root->name),qPrint(cd->name()));
5668     std::unique_ptr<MemberDefMutable> md { createMemberDef(
5669         root->fileName,root->startLine,root->startColumn,
5670         funcType,funcName,funcArgs,exceptions,
5671         root->protection,root->virt,root->stat,Member,
5672         MemberType_Function,ArgumentList(),root->argList,root->metaData) };
5673     md->setTagInfo(root->tagInfo());
5674     md->setLanguage(root->lang);
5675     md->setId(root->id);
5676     md->makeImplementationDetail();
5677     md->setMemberClass(cd);
5678     md->setDefinition(funcDecl);
5679     md->enableCallGraph(root->callGraph);
5680     md->enableCallerGraph(root->callerGraph);
5681     md->enableReferencedByRelation(root->referencedByRelation);
5682     md->enableReferencesRelation(root->referencesRelation);
5683     md->setDocumentation(root->doc,root->docFile,root->docLine);
5684     md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5685     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5686     md->setDocsForDefinition(!root->proto);
5687     md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
5688     md->addSectionsToDefinition(root->anchors);
5689     md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5690     FileDef *fd=root->fileDef();
5691     md->setBodyDef(fd);
5692     md->setMemberSpecifiers(spec);
5693     md->setMemberGroupId(root->mGrpId);
5694     cd->insertMember(md.get());
5695     cd->insertUsedFile(fd);
5696     md->setRefItems(root->sli);
5697 
5698     MemberName *mn = Doxygen::memberNameLinkedMap->add(root->name);
5699     mn->push_back(std::move(md));
5700   }
5701   else
5702   {
5703     // local objective C method found for class without interface
5704   }
5705 }
5706 
5707 //-------------------------------------------------------------------------------------------
5708 
addMemberFunction(const Entry * root,MemberName * mn,const QCString & scopeName,const QCString & namespaceName,const QCString & className,const QCString & funcTyp,const QCString & funcName,const QCString & funcArgs,const QCString & funcTempList,const QCString & exceptions,const QCString & type,const QCString & args,bool isFriend,uint64 spec,const QCString & relates,const QCString & funcDecl,bool overloaded,bool isFunc)5709 static void addMemberFunction(const Entry *root,
5710                        MemberName *mn,
5711                        const QCString &scopeName,
5712                        const QCString &namespaceName,
5713                        const QCString &className,
5714                        const QCString &funcTyp,
5715                        const QCString &funcName,
5716                        const QCString &funcArgs,
5717                        const QCString &funcTempList,
5718                        const QCString &exceptions,
5719                        const QCString &type,
5720                        const QCString &args,
5721                        bool isFriend,
5722                        uint64 spec,
5723                        const QCString &relates,
5724                        const QCString &funcDecl,
5725                        bool overloaded,
5726                        bool isFunc)
5727 {
5728   QCString funcType = funcTyp;
5729   int count=0;
5730   int noMatchCount=0;
5731   bool memFound=FALSE;
5732   for (const auto &imd : *mn)
5733   {
5734     MemberDefMutable *md = toMemberDefMutable(imd.get());
5735     if (md==0) continue;
5736     ClassDefMutable *cd=md->getClassDefMutable();
5737     if (cd==0) continue;
5738     Debug::print(Debug::FindMembers,0,
5739         "3. member definition found, "
5740         "scope needed='%s' scope='%s' args='%s' fileName=%s\n",
5741         qPrint(scopeName),qPrint(cd->name()),
5742         qPrint(md->argsString()),
5743         qPrint(root->fileName));
5744     //printf("Member %s (member scopeName=%s) (this scopeName=%s) isEnumValue()=%d\n",
5745     //    qPrint(md->name()),qPrint(cd->name()),qPrint(scopeName),md->isEnumValue());
5746     FileDef *fd=root->fileDef();
5747     NamespaceDef *nd=0;
5748     if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
5749 
5750     //printf("scopeName %s->%s\n",qPrint(scopeName),
5751     //       qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
5752 
5753     // if the member we are searching for is an enum value that is part of
5754     // a "strong" enum, we need to look into the fields of the enum for a match
5755     int enumNamePos=0;
5756     if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
5757     {
5758       QCString enumName = className.mid(enumNamePos+2);
5759       QCString fullScope = className.left(enumNamePos);
5760       if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
5761       if (fullScope==cd->name())
5762       {
5763         MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
5764         //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
5765         if (enumMn)
5766         {
5767           for (const auto &emd : *enumMn)
5768           {
5769             memFound = emd->isStrong() && md->getEnumScope()==emd.get();
5770             if (memFound)
5771             {
5772               addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5773               count++;
5774             }
5775             if (memFound) break;
5776           }
5777         }
5778       }
5779     }
5780     if (memFound) break;
5781 
5782     const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
5783     if (tcd==0 && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
5784     {
5785       // don't be fooled by anonymous scopes
5786       tcd=cd;
5787     }
5788     //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
5789     //    qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd,tcd?qPrint(tcd->name()):"",cd);
5790 
5791     if (cd && tcd==cd) // member's classes match
5792     {
5793       Debug::print(Debug::FindMembers,0,
5794           "4. class definition %s found\n",qPrint(cd->name()));
5795 
5796       // get the template parameter lists found at the member declaration
5797       ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
5798       const ArgumentList &templAl = md->templateArguments();
5799       if (!templAl.empty())
5800       {
5801         declTemplArgs.push_back(templAl);
5802       }
5803 
5804       // get the template parameter lists found at the member definition
5805       const ArgumentLists &defTemplArgs = root->tArgLists;
5806       //printf("defTemplArgs=%p\n",defTemplArgs);
5807 
5808       // do we replace the decl argument lists with the def argument lists?
5809       bool substDone=FALSE;
5810       ArgumentList argList;
5811 
5812       /* substitute the occurrences of class template names in the
5813        * argument list before matching
5814        */
5815       const ArgumentList &mdAl = md->argumentList();
5816       if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
5817       {
5818         /* the function definition has template arguments
5819          * and the class definition also has template arguments, so
5820          * we must substitute the template names of the class by that
5821          * of the function definition before matching.
5822          */
5823         substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
5824 
5825         substDone=TRUE;
5826       }
5827       else /* no template arguments, compare argument lists directly */
5828       {
5829         argList = mdAl;
5830       }
5831 
5832       Debug::print(Debug::FindMembers,0,
5833           "5. matching '%s'<=>'%s' className=%s namespaceName=%s\n",
5834           qPrint(argListToString(argList,TRUE)),qPrint(argListToString(root->argList,TRUE)),
5835           qPrint(className),qPrint(namespaceName)
5836           );
5837 
5838       bool matching=
5839         md->isVariable() || md->isTypedef() || // needed for function pointers
5840         matchArguments2(
5841             md->getClassDef(),md->getFileDef(),&argList,
5842             cd,fd,&root->argList,
5843             TRUE);
5844 
5845       if (md->getLanguage()==SrcLangExt_ObjC && md->isVariable() && (root->section&Entry::FUNCTION_SEC))
5846       {
5847         matching = FALSE; // don't match methods and attributes with the same name
5848       }
5849 
5850       // for template member we also need to check the return type
5851       if (!md->templateArguments().empty() && !root->tArgLists.empty())
5852       {
5853         QCString memType = md->typeString();
5854         memType.stripPrefix("static "); // see bug700696
5855         funcType=substitute(stripTemplateSpecifiersFromScope(funcType,TRUE),
5856             className+"::",""); // see bug700693 & bug732594
5857         memType=substitute(stripTemplateSpecifiersFromScope(memType,TRUE),
5858             className+"::",""); // see bug758900
5859         Debug::print(Debug::FindMembers,0,
5860             "5b. Comparing return types '%s'<->'%s' #args %d<->%d\n",
5861             qPrint(md->typeString()),qPrint(funcType),
5862             md->templateArguments().size(),root->tArgLists.back().size());
5863         if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
5864         {
5865           //printf(" ---> no matching\n");
5866           matching = FALSE;
5867         }
5868       }
5869       bool rootIsUserDoc = (root->section&Entry::MEMBERDOC_SEC)!=0;
5870       bool classIsTemplate = scopeIsTemplate(md->getClassDef());
5871       bool mdIsTemplate    = md->templateArguments().hasParameters();
5872       bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
5873       bool rootIsTemplate  = !root->tArgLists.empty();
5874       //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
5875       if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
5876           (mdIsTemplate || rootIsTemplate) && // either md or root is a template
5877           ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
5878          )
5879       {
5880         // Method with template return type does not match method without return type
5881         // even if the parameters are the same. See also bug709052
5882         Debug::print(Debug::FindMembers,0,
5883             "5b. Comparing return types: template v.s. non-template\n");
5884         matching = FALSE;
5885       }
5886 
5887 
5888       Debug::print(Debug::FindMembers,0,
5889           "6. match results of matchArguments2 = %d substDone=%d\n",matching,substDone);
5890 
5891       if (substDone) // found a new argument list
5892       {
5893         if (matching) // replace member's argument list
5894         {
5895           md->setDefinitionTemplateParameterLists(root->tArgLists);
5896           md->moveArgumentList(std::make_unique<ArgumentList>(argList));
5897         }
5898         else // no match
5899         {
5900           if (!funcTempList.isEmpty() &&
5901               isSpecialization(declTemplArgs,defTemplArgs))
5902           {
5903             // check if we are dealing with a partial template
5904             // specialization. In this case we add it to the class
5905             // even though the member arguments do not match.
5906 
5907             addMethodToClass(root,cd,type,md->name(),args,isFriend,
5908                 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
5909             return;
5910           }
5911         }
5912       }
5913       if (matching)
5914       {
5915         addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5916         count++;
5917         memFound=TRUE;
5918       }
5919     }
5920     else if (cd && cd!=tcd) // we did find a class with the same name as cd
5921       // but in a different namespace
5922     {
5923       noMatchCount++;
5924     }
5925 
5926     if (memFound) break;
5927   }
5928   if (count==0 && root->parent() &&
5929       root->parent()->section==Entry::OBJCIMPL_SEC)
5930   {
5931     addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
5932     return;
5933   }
5934   if (count==0 && !(isFriend && funcType=="class"))
5935   {
5936     int candidates=0;
5937     const ClassDef *ecd = 0, *ucd = 0;
5938     MemberDef *emd = 0, *umd = 0;
5939     //printf("Assume template class\n");
5940     for (const auto &md : *mn)
5941     {
5942       ClassDefMutable *ccd=md->getClassDefMutable();
5943       MemberDef *cmd=md.get();
5944       //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
5945       if (ccd!=0 && rightScopeMatch(ccd->name(),className))
5946       {
5947         const ArgumentList &templAl = md->templateArguments();
5948         if (!root->tArgLists.empty() && !templAl.empty() &&
5949             root->tArgLists.back().size()<=templAl.size())
5950         {
5951           Debug::print(Debug::FindMembers,0,"7. add template specialization\n");
5952           addMethodToClass(root,ccd,type,md->name(),args,isFriend,
5953               root->protection,root->stat,root->virt,spec,relates);
5954           return;
5955         }
5956         if (md->argsString()==argListToString(root->argList,FALSE,FALSE))
5957         { // exact argument list match -> remember
5958           ucd = ecd = ccd;
5959           umd = emd = cmd;
5960           Debug::print(Debug::FindMembers,0,
5961               "7. new candidate className=%s scope=%s args=%s exact match\n",
5962               qPrint(className),qPrint(ccd->name()),qPrint(md->argsString()));
5963         }
5964         else // arguments do not match, but member name and scope do -> remember
5965         {
5966           ucd = ccd;
5967           umd = cmd;
5968           Debug::print(Debug::FindMembers,0,
5969               "7. new candidate className=%s scope=%s args=%s no match\n",
5970               qPrint(className),qPrint(ccd->name()),qPrint(md->argsString()));
5971         }
5972         candidates++;
5973       }
5974     }
5975     static bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
5976     if (!strictProtoMatching)
5977     {
5978       if (candidates==1 && ucd && umd)
5979       {
5980         // we didn't find an actual match on argument lists, but there is only 1 member with this
5981         // name in the same scope, so that has to be the one.
5982         addMemberDocs(root,toMemberDefMutable(umd),funcDecl,0,overloaded,spec);
5983         return;
5984       }
5985       else if (candidates>1 && ecd && emd)
5986       {
5987         // we didn't find a unique match using type resolution,
5988         // but one of the matches has the exact same signature so
5989         // we take that one.
5990         addMemberDocs(root,toMemberDefMutable(emd),funcDecl,0,overloaded,spec);
5991         return;
5992       }
5993     }
5994 
5995     QCString warnMsg = "no ";
5996     if (noMatchCount>1) warnMsg+="uniquely ";
5997     warnMsg+="matching class member found for \n";
5998 
5999     for (const ArgumentList &al : root->tArgLists)
6000     {
6001       warnMsg+="  template ";
6002       warnMsg+=tempArgListToString(al,root->lang);
6003       warnMsg+='\n';
6004     }
6005 
6006     QCString fullFuncDecl=funcDecl;
6007     if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6008 
6009     warnMsg+="  ";
6010     warnMsg+=fullFuncDecl;
6011     warnMsg+='\n';
6012 
6013     if (candidates>0)
6014     {
6015       warnMsg+="Possible candidates:\n";
6016       for (const auto &md : *mn)
6017       {
6018         const ClassDef *cd=md->getClassDef();
6019         if (cd!=0 && rightScopeMatch(cd->name(),className))
6020         {
6021           const ArgumentList &templAl = md->templateArguments();
6022           warnMsg+="  '";
6023           if (templAl.hasParameters())
6024           {
6025             warnMsg+="template ";
6026             warnMsg+=tempArgListToString(templAl,root->lang);
6027             warnMsg+='\n';
6028             warnMsg+="  ";
6029           }
6030           if (!md->typeString().isEmpty())
6031           {
6032             warnMsg+=md->typeString();
6033             warnMsg+=' ';
6034           }
6035           QCString qScope = cd->qualifiedNameWithTemplateParameters();
6036           if (!qScope.isEmpty())
6037             warnMsg+=qScope+"::"+md->name();
6038           warnMsg+=md->argsString();
6039           if (noMatchCount>1)
6040           {
6041             warnMsg+="' at line "+QCString().setNum(md->getDefLine()) +
6042               " of file "+md->getDefFileName();
6043           }
6044           else
6045             warnMsg += "'";
6046 
6047           warnMsg+='\n';
6048         }
6049       }
6050     }
6051     warn_simple(root->fileName,root->startLine,qPrint(warnMsg));
6052   }
6053 }
6054 
6055 //-------------------------------------------------------------------------------------------
6056 
addMemberSpecialization(const Entry * root,MemberName * mn,ClassDefMutable * cd,const QCString & funcType,const QCString & funcName,const QCString & funcArgs,const QCString & funcDecl,const QCString & exceptions,uint64 spec)6057 static void addMemberSpecialization(const Entry *root,
6058                              MemberName *mn,
6059                              ClassDefMutable *cd,
6060                              const QCString &funcType,
6061                              const QCString &funcName,
6062                              const QCString &funcArgs,
6063                              const QCString &funcDecl,
6064                              const QCString &exceptions,
6065                              uint64 spec
6066                             )
6067 {
6068   MemberDef *declMd=0;
6069   for (const auto &md : *mn)
6070   {
6071     if (md->getClassDef()==cd)
6072     {
6073       // TODO: we should probably also check for matching arguments
6074       declMd = md.get();
6075       break;
6076     }
6077   }
6078   MemberType mtype=MemberType_Function;
6079   ArgumentList tArgList;
6080   //  getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6081   std::unique_ptr<MemberDefMutable> md { createMemberDef(
6082       root->fileName,root->startLine,root->startColumn,
6083       funcType,funcName,funcArgs,exceptions,
6084       declMd ? declMd->protection() : root->protection,
6085       root->virt,root->stat,Member,
6086       mtype,tArgList,root->argList,root->metaData) };
6087   //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6088   md->setTagInfo(root->tagInfo());
6089   md->setLanguage(root->lang);
6090   md->setId(root->id);
6091   md->setMemberClass(cd);
6092   md->setTemplateSpecialization(TRUE);
6093   md->setTypeConstraints(root->typeConstr);
6094   md->setDefinition(funcDecl);
6095   md->enableCallGraph(root->callGraph);
6096   md->enableCallerGraph(root->callerGraph);
6097   md->enableReferencedByRelation(root->referencedByRelation);
6098   md->enableReferencesRelation(root->referencesRelation);
6099   md->setDocumentation(root->doc,root->docFile,root->docLine);
6100   md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6101   md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6102   md->setDocsForDefinition(!root->proto);
6103   md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6104   md->addSectionsToDefinition(root->anchors);
6105   md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6106   FileDef *fd=root->fileDef();
6107   md->setBodyDef(fd);
6108   md->setMemberSpecifiers(spec);
6109   md->setMemberGroupId(root->mGrpId);
6110   cd->insertMember(md.get());
6111   md->setRefItems(root->sli);
6112 
6113   mn->push_back(std::move(md));
6114 }
6115 
6116 //-------------------------------------------------------------------------------------------
6117 
addOverloaded(const Entry * root,MemberName * mn,const QCString & funcType,const QCString & funcName,const QCString & funcArgs,const QCString & funcDecl,const QCString & exceptions,uint64 spec)6118 static void addOverloaded(const Entry *root,MemberName *mn,
6119                           const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6120                           const QCString &funcDecl,const QCString &exceptions,uint64 spec)
6121 {
6122   // for unique overloaded member we allow the class to be
6123   // omitted, this is to be Qt compatible. Using this should
6124   // however be avoided, because it is error prone
6125   bool sameClass=false;
6126   if (mn->size()>0)
6127   {
6128     // check if all members with the same name are also in the same class
6129     sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6130                   [](const auto &md1,const auto &md2)
6131                   { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6132   }
6133   if (sameClass)
6134   {
6135     ClassDefMutable *cd = mn->front()->getClassDefMutable();
6136     MemberType mtype;
6137     if      (root->mtype==Signal)  mtype=MemberType_Signal;
6138     else if (root->mtype==Slot)    mtype=MemberType_Slot;
6139     else if (root->mtype==DCOP)    mtype=MemberType_DCOP;
6140     else                           mtype=MemberType_Function;
6141 
6142     // new overloaded member function
6143     std::unique_ptr<ArgumentList> tArgList =
6144       getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6145     //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6146     std::unique_ptr<MemberDefMutable> md { createMemberDef(
6147         root->fileName,root->startLine,root->startColumn,
6148         funcType,funcName,funcArgs,exceptions,
6149         root->protection,root->virt,root->stat,Related,
6150         mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData) };
6151     md->setTagInfo(root->tagInfo());
6152     md->setLanguage(root->lang);
6153     md->setId(root->id);
6154     md->setTypeConstraints(root->typeConstr);
6155     md->setMemberClass(cd);
6156     md->setDefinition(funcDecl);
6157     md->enableCallGraph(root->callGraph);
6158     md->enableCallerGraph(root->callerGraph);
6159     md->enableReferencedByRelation(root->referencedByRelation);
6160     md->enableReferencesRelation(root->referencesRelation);
6161     QCString doc=getOverloadDocs();
6162     doc+="<p>";
6163     doc+=root->doc;
6164     md->setDocumentation(doc,root->docFile,root->docLine);
6165     md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6166     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6167     md->setDocsForDefinition(!root->proto);
6168     md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6169     md->addSectionsToDefinition(root->anchors);
6170     md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6171     FileDef *fd=root->fileDef();
6172     md->setBodyDef(fd);
6173     md->setMemberSpecifiers(spec);
6174     md->setMemberGroupId(root->mGrpId);
6175     cd->insertMember(md.get());
6176     cd->insertUsedFile(fd);
6177     md->setRefItems(root->sli);
6178 
6179     mn->push_back(std::move(md));
6180   }
6181 }
6182 
6183 //-------------------------------------------------------------------------------------------
6184 
6185 /*! This function tries to find a member (in a documented class/file/namespace)
6186  * that corresponds to the function/variable declaration given in \a funcDecl.
6187  *
6188  * The boolean \a overloaded is used to specify whether or not a standard
6189  * overload documentation line should be generated.
6190  *
6191  * The boolean \a isFunc is a hint that indicates that this is a function
6192  * instead of a variable or typedef.
6193  */
findMember(const Entry * root,const QCString & relates,const QCString & type,const QCString & args,QCString funcDecl,bool overloaded,bool isFunc)6194 static void findMember(const Entry *root,
6195                        const QCString &relates,
6196                        const QCString &type,
6197                        const QCString &args,
6198                        QCString funcDecl,
6199                        bool overloaded,
6200                        bool isFunc
6201                       )
6202 {
6203   Debug::print(Debug::FindMembers,0,
6204                "findMember(root=%p,funcDecl='%s',related='%s',overload=%d,"
6205                "isFunc=%d mGrpId=%d #tArgList=%d "
6206                "spec=%lld lang=%x\n",
6207                root,qPrint(funcDecl),qPrint(relates),overloaded,isFunc,root->mGrpId,
6208                root->tArgLists.size(),
6209                root->spec,root->lang
6210               );
6211 
6212   QCString scopeName;
6213   QCString className;
6214   QCString namespaceName;
6215   QCString funcType;
6216   QCString funcName;
6217   QCString funcArgs;
6218   QCString funcTempList;
6219   QCString exceptions;
6220   QCString funcSpec;
6221   bool isRelated=FALSE;
6222   bool isMemberOf=FALSE;
6223   bool isFriend=FALSE;
6224   bool done;
6225   uint64 spec = root->spec;
6226   do
6227   {
6228     done=TRUE;
6229     if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6230     {
6231       isFriend=TRUE;
6232       done=FALSE;
6233     }
6234     if (funcDecl.stripPrefix("inline "))
6235     {
6236       spec|=Entry::Inline;
6237       done=FALSE;
6238     }
6239     if (funcDecl.stripPrefix("explicit "))
6240     {
6241       spec|=Entry::Explicit;
6242       done=FALSE;
6243     }
6244     if (funcDecl.stripPrefix("mutable "))
6245     {
6246       spec|=Entry::Mutable;
6247       done=FALSE;
6248     }
6249     if (funcDecl.stripPrefix("virtual "))
6250     {
6251       done=FALSE;
6252     }
6253   } while (!done);
6254 
6255   // delete any ; from the function declaration
6256   int sep;
6257   while ((sep=funcDecl.find(';'))!=-1)
6258   {
6259     funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6260   }
6261 
6262   // make sure the first character is a space to simplify searching.
6263   if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6264 
6265   // remove some superfluous spaces
6266   funcDecl= substitute(
6267               substitute(
6268                 substitute(funcDecl,"~ ","~"),
6269                 ":: ","::"
6270               ),
6271               " ::","::"
6272             ).stripWhiteSpace();
6273 
6274   //printf("funcDecl='%s'\n",qPrint(funcDecl));
6275   if (isFriend && funcDecl.left(6)=="class ")
6276   {
6277     //printf("friend class\n");
6278     funcDecl=funcDecl.right(funcDecl.length()-6);
6279     funcName = funcDecl;
6280   }
6281   else if (isFriend && funcDecl.left(7)=="struct ")
6282   {
6283     funcDecl=funcDecl.right(funcDecl.length()-7);
6284     funcName = funcDecl;
6285   }
6286   else
6287   {
6288     // extract information from the declarations
6289     parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6290                 funcArgs,funcTempList,exceptions
6291                );
6292   }
6293   //printf("scopeName='%s' funcType='%s' funcName='%s' funcArgs='%s'\n",
6294   //    qPrint(scopeName),qPrint(funcType),qPrint(funcName),qPrint(funcArgs));
6295 
6296   // the class name can also be a namespace name, we decide this later.
6297   // if a related class name is specified and the class name could
6298   // not be derived from the function declaration, then use the
6299   // related field.
6300   //printf("scopeName='%s' className='%s' namespaceName='%s'\n",
6301   //    qPrint(scopeName),qPrint(className),qPrint(namespaceName));
6302   if (!relates.isEmpty())
6303   {                             // related member, prefix user specified scope
6304     isRelated=TRUE;
6305     isMemberOf=(root->relatesType == MemberOf);
6306     if (getClass(relates)==0 && !scopeName.isEmpty())
6307     {
6308       scopeName= mergeScopes(scopeName,relates);
6309     }
6310     else
6311     {
6312       scopeName = relates;
6313     }
6314   }
6315 
6316   if (relates.isEmpty() && root->parent() &&
6317       ((root->parent()->section&Entry::SCOPE_MASK) ||
6318        (root->parent()->section==Entry::OBJCIMPL_SEC)
6319       ) &&
6320       !root->parent()->name.isEmpty()) // see if we can combine scopeName
6321                                      // with the scope in which it was found
6322   {
6323     QCString joinedName = root->parent()->name+"::"+scopeName;
6324     if (!scopeName.isEmpty() &&
6325         (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6326     {
6327       scopeName = joinedName;
6328     }
6329     else
6330     {
6331       scopeName = mergeScopes(root->parent()->name,scopeName);
6332     }
6333   }
6334   else // see if we can prefix a namespace or class that is used from the file
6335   {
6336      FileDef *fd=root->fileDef();
6337      if (fd)
6338      {
6339        for (const auto &fnd : fd->getUsedNamespaces())
6340        {
6341          QCString joinedName = fnd->name()+"::"+scopeName;
6342          if (Doxygen::namespaceLinkedMap->find(joinedName))
6343          {
6344            scopeName=joinedName;
6345            break;
6346          }
6347        }
6348      }
6349   }
6350   scopeName=stripTemplateSpecifiersFromScope(
6351       removeRedundantWhiteSpace(scopeName),FALSE,&funcSpec);
6352 
6353   // funcSpec contains the last template specifiers of the given scope.
6354   // If this method does not have any template arguments or they are
6355   // empty while funcSpec is not empty we assume this is a
6356   // specialization of a method. If not, we clear the funcSpec and treat
6357   // this as a normal method of a template class.
6358   if (!(root->tArgLists.size()>0 &&
6359         root->tArgLists.front().size()==0
6360        )
6361      )
6362   {
6363     funcSpec.resize(0);
6364   }
6365 
6366   // split scope into a namespace and a class part
6367   extractNamespaceName(scopeName,className,namespaceName,TRUE);
6368   //printf("scopeName='%s' className='%s' namespaceName='%s'\n",
6369   //       qPrint(scopeName),qPrint(className),qPrint(namespaceName));
6370 
6371   //namespaceName=removeAnonymousScopes(namespaceName);
6372   if (namespaceName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6373 
6374   //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6375   // merge class and namespace scopes again
6376   scopeName.resize(0);
6377   if (!namespaceName.isEmpty())
6378   {
6379     if (className.isEmpty())
6380     {
6381       scopeName=namespaceName;
6382     }
6383     else if (!relates.isEmpty() || // relates command with explicit scope
6384              !getClass(className)) // class name only exists in a namespace
6385     {
6386       scopeName=namespaceName+"::"+className;
6387     }
6388     else
6389     {
6390       scopeName=className;
6391     }
6392   }
6393   else if (!className.isEmpty())
6394   {
6395     scopeName=className;
6396   }
6397   //printf("new scope='%s'\n",qPrint(scopeName));
6398 
6399   QCString tempScopeName=scopeName;
6400   ClassDefMutable *cd=getClassMutable(scopeName);
6401   if (cd)
6402   {
6403     if (funcSpec.isEmpty())
6404     {
6405       uint argListIndex=0;
6406       tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6407     }
6408     else
6409     {
6410       tempScopeName=scopeName+funcSpec;
6411     }
6412   }
6413   //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6414   //    qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6415 
6416   //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6417   // rebuild the function declaration (needed to get the scope right).
6418   if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6419   {
6420     if (!funcType.isEmpty())
6421     {
6422       if (isFunc) // a function -> we use argList for the arguments
6423       {
6424         funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6425       }
6426       else
6427       {
6428         funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6429       }
6430     }
6431     else
6432     {
6433       if (isFunc) // a function => we use argList for the arguments
6434       {
6435         funcDecl=tempScopeName+"::"+funcName+funcTempList;
6436       }
6437       else // variable => add 'argument' list
6438       {
6439         funcDecl=tempScopeName+"::"+funcName+funcArgs;
6440       }
6441     }
6442   }
6443   else // build declaration without scope
6444   {
6445     if (!funcType.isEmpty()) // but with a type
6446     {
6447       if (isFunc) // function => omit argument list
6448       {
6449         funcDecl=funcType+" "+funcName+funcTempList;
6450       }
6451       else // variable => add 'argument' list
6452       {
6453         funcDecl=funcType+" "+funcName+funcArgs;
6454       }
6455     }
6456     else // no type
6457     {
6458       if (isFunc)
6459       {
6460         funcDecl=funcName+funcTempList;
6461       }
6462       else
6463       {
6464         funcDecl=funcName+funcArgs;
6465       }
6466     }
6467   }
6468 
6469   if (funcType=="template class" && !funcTempList.isEmpty())
6470     return;   // ignore explicit template instantiations
6471 
6472   Debug::print(Debug::FindMembers,0,
6473            "findMember() Parse results:\n"
6474            "  namespaceName='%s'\n"
6475            "  className=`%s`\n"
6476            "  funcType='%s'\n"
6477            "  funcSpec='%s'\n"
6478            "  funcName='%s'\n"
6479            "  funcArgs='%s'\n"
6480            "  funcTempList='%s'\n"
6481            "  funcDecl='%s'\n"
6482            "  related='%s'\n"
6483            "  exceptions='%s'\n"
6484            "  isRelated=%d\n"
6485            "  isMemberOf=%d\n"
6486            "  isFriend=%d\n"
6487            "  isFunc=%d\n\n",
6488            qPrint(namespaceName),qPrint(className),
6489            qPrint(funcType),qPrint(funcSpec),qPrint(funcName),qPrint(funcArgs),qPrint(funcTempList),
6490            qPrint(funcDecl),qPrint(relates),qPrint(exceptions),isRelated,isMemberOf,isFriend,
6491            isFunc
6492           );
6493 
6494   if (!funcName.isEmpty()) // function name is valid
6495   {
6496     Debug::print(Debug::FindMembers,0,
6497                  "1. funcName='%s'\n",qPrint(funcName));
6498 
6499     // check if 'className' is actually a scoped enum, in which case we need to
6500     // process it as a global, see issue #6471
6501     bool strongEnum = false;
6502     MemberName *mn=0;
6503     if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6504     {
6505       for (const auto &imd : *mn)
6506       {
6507         MemberDefMutable *md = toMemberDefMutable(imd.get());
6508         if (md && md->isEnumerate() && md->isStrong())
6509         {
6510           Debug::print(Debug::FindMembers,0,"%s is a strong enum!\n",qPrint(md->name()));
6511           strongEnum = true;
6512           // pass the scope name name as a 'namespace' to the findGlobalMember function
6513           if (!namespaceName.isEmpty())
6514           {
6515             namespaceName+="::"+className;
6516           }
6517           else
6518           {
6519             namespaceName=className;
6520           }
6521         }
6522       }
6523     }
6524 
6525     if (funcName.left(9)=="operator ") // strip class scope from cast operator
6526     {
6527       funcName = substitute(funcName,className+"::","");
6528     }
6529     mn = 0;
6530     if (!funcTempList.isEmpty()) // try with member specialization
6531     {
6532       mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6533     }
6534     if (mn==0) // try without specialization
6535     {
6536       mn=Doxygen::memberNameLinkedMap->find(funcName);
6537     }
6538     if (!isRelated && !strongEnum && mn) // function name already found
6539     {
6540       Debug::print(Debug::FindMembers,0,
6541                    "2. member name exists (%d members with this name)\n",mn->size());
6542       if (!className.isEmpty()) // class name is valid
6543       {
6544         if (funcSpec.isEmpty()) // not a member specialization
6545         {
6546           addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6547                             funcArgs,funcTempList,exceptions,
6548                             type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6549         }
6550         else if (cd) // member specialization
6551         {
6552           addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6553         }
6554         else
6555         {
6556           //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6557           //        qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6558         }
6559       }
6560       else if (overloaded) // check if the function belongs to only one class
6561       {
6562         addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6563       }
6564       else // unrelated function with the same name as a member
6565       {
6566         if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6567         {
6568           QCString fullFuncDecl=funcDecl;
6569           if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6570           warn(root->fileName,root->startLine,
6571                "Cannot determine class for function\n%s",
6572                qPrint(fullFuncDecl)
6573               );
6574         }
6575       }
6576     }
6577     else if (isRelated && !relates.isEmpty())
6578     {
6579       Debug::print(Debug::FindMembers,0,"2. related function\n"
6580               "  scopeName=%s className=%s\n",qPrint(scopeName),qPrint(className));
6581       if (className.isEmpty()) className=relates;
6582       //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6583       if ((cd=getClassMutable(scopeName)))
6584       {
6585         bool newMember=TRUE; // assume we have a new member
6586         MemberDefMutable *mdDefine=0;
6587         {
6588           mn = Doxygen::functionNameLinkedMap->find(funcName);
6589           if (mn)
6590           {
6591             for (const auto &imd : *mn)
6592             {
6593               MemberDefMutable *md = toMemberDefMutable(imd.get());
6594               if (md && md->isDefine())
6595               {
6596                 mdDefine = md;
6597                 break;
6598               }
6599             }
6600           }
6601         }
6602 
6603         FileDef *fd=root->fileDef();
6604 
6605         if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==0)
6606         {
6607           mn=Doxygen::memberNameLinkedMap->add(funcName);
6608         }
6609         else
6610         {
6611           // see if we got another member with matching arguments
6612           MemberDefMutable *rmd_found = 0;
6613           for (const auto &irmd : *mn)
6614           {
6615             MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6616             if (rmd)
6617             {
6618               const ArgumentList &rmdAl = rmd->argumentList();
6619 
6620               newMember=
6621                 className!=rmd->getOuterScope()->name() ||
6622                 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6623                     cd,fd,&root->argList,
6624                     TRUE);
6625               if (!newMember)
6626               {
6627                 rmd_found = rmd;
6628               }
6629             }
6630           }
6631           if (rmd_found) // member already exists as rmd -> add docs
6632           {
6633             //printf("addMemberDocs for related member %s\n",qPrint(root->name));
6634             //rmd->setMemberDefTemplateArguments(root->mtArgList);
6635             addMemberDocs(root,rmd_found,funcDecl,0,overloaded,spec);
6636           }
6637         }
6638 
6639         if (newMember) // need to create a new member
6640         {
6641           MemberType mtype;
6642           if (mdDefine)
6643             mtype=MemberType_Define;
6644           else if (root->mtype==Signal)
6645             mtype=MemberType_Signal;
6646           else if (root->mtype==Slot)
6647             mtype=MemberType_Slot;
6648           else if (root->mtype==DCOP)
6649             mtype=MemberType_DCOP;
6650           else
6651             mtype=MemberType_Function;
6652 
6653           if (mdDefine)
6654           {
6655             mdDefine->setHidden(TRUE);
6656             funcType="#define";
6657             funcArgs=mdDefine->argsString();
6658             funcDecl=funcType + " " + funcName;
6659           }
6660 
6661           //printf("New related name '%s' '%d'\n",qPrint(funcName),
6662           //    root->argList ? (int)root->argList->count() : -1);
6663 
6664           // first note that we pass:
6665           //   (root->tArgLists ? root->tArgLists->last() : 0)
6666           // for the template arguments for the new "member."
6667           // this accurately reflects the template arguments of
6668           // the related function, which don't have to do with
6669           // those of the related class.
6670           std::unique_ptr<MemberDefMutable> md { createMemberDef(
6671               root->fileName,root->startLine,root->startColumn,
6672               funcType,funcName,funcArgs,exceptions,
6673               root->protection,root->virt,
6674               root->stat,
6675               isMemberOf ? Foreign : Related,
6676               mtype,
6677               (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
6678               funcArgs.isEmpty() ? ArgumentList() : root->argList,
6679               root->metaData) };
6680 
6681           if (mdDefine)
6682           {
6683             md->setInitializer(mdDefine->initializer());
6684           }
6685 
6686           //
6687           // we still have the problem that
6688           // MemberDef::writeDocumentation() in memberdef.cpp
6689           // writes the template argument list for the class,
6690           // as if this member is a member of the class.
6691           // fortunately, MemberDef::writeDocumentation() has
6692           // a special mechanism that allows us to totally
6693           // override the set of template argument lists that
6694           // are printed.  We use that and set it to the
6695           // template argument lists of the related function.
6696           //
6697           md->setDefinitionTemplateParameterLists(root->tArgLists);
6698 
6699           md->setTagInfo(root->tagInfo());
6700 
6701           //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
6702           //       qPrint(funcName),qPrint(funcDecl),root->bodyLine);
6703 
6704           // try to find the matching line number of the body from the
6705           // global function list
6706           bool found=FALSE;
6707           if (root->bodyLine==-1)
6708           {
6709             MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
6710             if (rmn)
6711             {
6712               const MemberDefMutable *rmd_found=0;
6713               for (const auto &irmd : *rmn)
6714               {
6715                 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6716                 if (rmd)
6717                 {
6718                   const ArgumentList &rmdAl = rmd->argumentList();
6719                   // check for matching argument lists
6720                   if (
6721                       matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6722                         cd,fd,&root->argList,
6723                         TRUE)
6724                      )
6725                   {
6726                     found=TRUE;
6727                     rmd_found = rmd;
6728                     break;
6729                   }
6730                 }
6731               }
6732               if (rmd_found) // member found -> copy line number info
6733               {
6734                 md->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
6735                 md->setBodyDef(rmd_found->getBodyDef());
6736                 //md->setBodyMember(rmd);
6737               }
6738             }
6739           }
6740           if (!found) // line number could not be found or is available in this
6741                       // entry
6742           {
6743             md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6744             md->setBodyDef(fd);
6745           }
6746 
6747           //if (root->mGrpId!=-1)
6748           //{
6749           //  md->setMemberGroup(memberGroupDict[root->mGrpId]);
6750           //}
6751           md->setMemberClass(cd);
6752           md->setMemberSpecifiers(spec);
6753           md->setDefinition(funcDecl);
6754           md->enableCallGraph(root->callGraph);
6755           md->enableCallerGraph(root->callerGraph);
6756           md->enableReferencedByRelation(root->referencedByRelation);
6757           md->enableReferencesRelation(root->referencesRelation);
6758           md->setDocumentation(root->doc,root->docFile,root->docLine);
6759           md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6760           md->setDocsForDefinition(!root->proto);
6761           md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6762           md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6763           md->addSectionsToDefinition(root->anchors);
6764           md->setMemberGroupId(root->mGrpId);
6765           md->setLanguage(root->lang);
6766           md->setId(root->id);
6767           //md->setMemberDefTemplateArguments(root->mtArgList);
6768           cd->insertMember(md.get());
6769           cd->insertUsedFile(fd);
6770           md->setRefItems(root->sli);
6771           if (root->relatesType == Duplicate) md->setRelatedAlso(cd);
6772           if (!mdDefine)
6773           {
6774             addMemberToGroups(root,md.get());
6775           }
6776           //printf("Adding member=%s\n",qPrint(md->name()));
6777           mn->push_back(std::move(md));
6778         }
6779         if (root->relatesType == Duplicate)
6780         {
6781           if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6782           {
6783             QCString fullFuncDecl=funcDecl;
6784             if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6785             warn(root->fileName,root->startLine,
6786                "Cannot determine file/namespace for relatedalso function\n%s",
6787                qPrint(fullFuncDecl)
6788               );
6789           }
6790         }
6791       }
6792       else
6793       {
6794         warn_undoc(root->fileName,root->startLine,
6795                    "class '%s' for related function '%s' is not "
6796                    "documented.",
6797                    qPrint(className),qPrint(funcName)
6798                   );
6799       }
6800     }
6801     else if (root->parent() && root->parent()->section==Entry::OBJCIMPL_SEC)
6802     {
6803       addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6804     }
6805     else // unrelated not overloaded member found
6806     {
6807       bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
6808       if (className.isEmpty() && !globMem)
6809       {
6810         warn(root->fileName,root->startLine,
6811              "class for member '%s' cannot "
6812              "be found.", qPrint(funcName)
6813             );
6814       }
6815       else if (!className.isEmpty() && !globMem)
6816       {
6817         warn(root->fileName,root->startLine,
6818              "member '%s' of class '%s' cannot be found",
6819              qPrint(funcName),qPrint(className));
6820       }
6821     }
6822   }
6823   else
6824   {
6825     // this should not be called
6826     warn(root->fileName,root->startLine,
6827          "member with no name found.");
6828   }
6829   return;
6830 }
6831 
6832 //----------------------------------------------------------------------
6833 // find the members corresponding to the different documentation blocks
6834 // that are extracted from the sources.
6835 
filterMemberDocumentation(const Entry * root,const QCString & relates)6836 static void filterMemberDocumentation(const Entry *root,const QCString &relates)
6837 {
6838   int i=-1,l;
6839   Debug::print(Debug::FindMembers,0,
6840       "findMemberDocumentation(): root->type='%s' root->inside='%s' root->name='%s' root->args='%s' section=%x root->spec=%lld root->mGrpId=%d\n",
6841       qPrint(root->type),qPrint(root->inside),qPrint(root->name),qPrint(root->args),root->section,root->spec,root->mGrpId
6842       );
6843   //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
6844   bool isFunc=TRUE;
6845 
6846   QCString type = root->type;
6847   QCString args = root->args;
6848   if ( // detect func variable/typedef to func ptr
6849       (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
6850      )
6851   {
6852     //printf("Fixing function pointer!\n");
6853     // fix type and argument
6854     args.prepend(type.right(type.length()-i-l));
6855     type=type.left(i+l);
6856     //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
6857     isFunc=FALSE;
6858   }
6859   else if ((type.left(8)=="typedef " && args.find('(')!=-1))
6860     // detect function types marked as functions
6861   {
6862     isFunc=FALSE;
6863   }
6864 
6865   //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
6866   if (root->section==Entry::MEMBERDOC_SEC)
6867   {
6868     //printf("Documentation for inline member '%s' found args='%s'\n",
6869     //    qPrint(root->name),qPrint(args));
6870     //if (relates.length()) printf("  Relates %s\n",qPrint(relates));
6871     if (type.isEmpty())
6872     {
6873       findMember(root,
6874                  relates,
6875                  type,
6876                  args,
6877                  root->name + args + root->exception,
6878                  FALSE,
6879                  isFunc);
6880     }
6881     else
6882     {
6883       findMember(root,
6884                  relates,
6885                  type,
6886                  args,
6887                  type + " " + root->name + args + root->exception,
6888                  FALSE,
6889                  isFunc);
6890     }
6891   }
6892   else if (root->section==Entry::OVERLOADDOC_SEC)
6893   {
6894     //printf("Overloaded member %s found\n",qPrint(root->name));
6895     findMember(root,
6896                relates,
6897                type,
6898                args,
6899                root->name,
6900                TRUE,
6901                isFunc);
6902   }
6903   else if
6904     ((root->section==Entry::FUNCTION_SEC      // function
6905       ||
6906       (root->section==Entry::VARIABLE_SEC &&  // variable
6907        !type.isEmpty() &&                // with a type
6908        g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
6909        // (to skip forward declaration of class etc.)
6910       )
6911      )
6912     )
6913     {
6914       //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
6915       //    qPrint(root->name),qPrint(args),qPrint(root->exception));
6916       //if (relates.length()) printf("  Relates %s\n",qPrint(relates));
6917       //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
6918       if (type=="friend class" || type=="friend struct" ||
6919           type=="friend union")
6920       {
6921         findMember(root,
6922             relates,
6923             type,
6924             args,
6925             type+" "+root->name,
6926             FALSE,FALSE);
6927 
6928       }
6929       else if (!type.isEmpty())
6930       {
6931         findMember(root,
6932             relates,
6933             type,
6934             args,
6935             type+" "+ root->inside + root->name + args + root->exception,
6936             FALSE,isFunc);
6937       }
6938       else
6939       {
6940         findMember(root,
6941             relates,
6942             type,
6943             args,
6944             root->inside + root->name + args + root->exception,
6945             FALSE,isFunc);
6946       }
6947     }
6948   else if (root->section==Entry::DEFINE_SEC && !relates.isEmpty())
6949   {
6950     findMember(root,
6951                relates,
6952                type,
6953                args,
6954                root->name + args,
6955                FALSE,
6956                !args.isEmpty());
6957   }
6958   else if (root->section==Entry::VARIABLEDOC_SEC)
6959   {
6960     //printf("Documentation for variable %s found\n",qPrint(root->name));
6961     //if (!relates.isEmpty()) printf("  Relates %s\n",qPrint(relates));
6962     findMember(root,
6963                relates,
6964                type,
6965                args,
6966                root->name,
6967                FALSE,
6968                FALSE);
6969   }
6970   else if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
6971            root->section==Entry::INCLUDED_SERVICE_SEC)
6972   {
6973     findMember(root,
6974                relates,
6975                type,
6976                args,
6977                type + " " + root->name,
6978                FALSE,
6979                FALSE);
6980   }
6981   else
6982   {
6983     // skip section
6984     //printf("skip section\n");
6985   }
6986 }
6987 
findMemberDocumentation(const Entry * root)6988 static void findMemberDocumentation(const Entry *root)
6989 {
6990   if (root->section==Entry::MEMBERDOC_SEC ||
6991       root->section==Entry::OVERLOADDOC_SEC ||
6992       root->section==Entry::FUNCTION_SEC ||
6993       root->section==Entry::VARIABLE_SEC ||
6994       root->section==Entry::VARIABLEDOC_SEC ||
6995       root->section==Entry::DEFINE_SEC ||
6996       root->section==Entry::INCLUDED_SERVICE_SEC ||
6997       root->section==Entry::EXPORTED_INTERFACE_SEC
6998      )
6999   {
7000     if (root->relatesType == Duplicate && !root->relates.isEmpty())
7001     {
7002       filterMemberDocumentation(root,"");
7003     }
7004     filterMemberDocumentation(root,root->relates);
7005   }
7006   for (const auto &e : root->children())
7007   {
7008     if (e->section!=Entry::ENUM_SEC)
7009     {
7010       findMemberDocumentation(e.get());
7011     }
7012   }
7013 }
7014 
7015 //----------------------------------------------------------------------
7016 
findObjCMethodDefinitions(const Entry * root)7017 static void findObjCMethodDefinitions(const Entry *root)
7018 {
7019   for (const auto &objCImpl : root->children())
7020   {
7021     if (objCImpl->section==Entry::OBJCIMPL_SEC)
7022     {
7023       for (const auto &objCMethod : objCImpl->children())
7024       {
7025         if (objCMethod->section==Entry::FUNCTION_SEC)
7026         {
7027           //Printf("  Found ObjC method definition %s\n",qPrint(objCMethod->name));
7028           findMember(objCMethod.get(),
7029                      objCMethod->relates,
7030                      objCMethod->type,
7031                      objCMethod->args,
7032                      objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7033                      FALSE,TRUE);
7034           objCMethod->section=Entry::EMPTY_SEC;
7035         }
7036       }
7037     }
7038   }
7039 }
7040 
7041 //----------------------------------------------------------------------
7042 // find and add the enumeration to their classes, namespaces or files
7043 
findEnums(const Entry * root)7044 static void findEnums(const Entry *root)
7045 {
7046   if (root->section==Entry::ENUM_SEC)
7047   {
7048     ClassDefMutable *cd=0;
7049     FileDef         *fd=0;
7050     NamespaceDefMutable *nd=0;
7051     MemberNameLinkedMap *mnsd=0;
7052     bool isGlobal;
7053     bool isRelated=FALSE;
7054     bool isMemberOf=FALSE;
7055     //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7056     int i;
7057 
7058     QCString name;
7059     QCString scope;
7060 
7061     if ((i=root->name.findRev("::"))!=-1) // scope is specified
7062     {
7063       scope=root->name.left(i); // extract scope
7064       name=root->name.right(root->name.length()-i-2); // extract name
7065       if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7066     }
7067     else // no scope, check the scope in which the docs where found
7068     {
7069       if (( root->parent()->section & Entry::SCOPE_MASK )
7070           && !root->parent()->name.isEmpty()
7071          ) // found enum docs inside a compound
7072       {
7073         scope=root->parent()->name;
7074         if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7075       }
7076       name=root->name;
7077     }
7078 
7079     if (!root->relates.isEmpty())
7080     {   // related member, prefix user specified scope
7081       isRelated=TRUE;
7082       isMemberOf=(root->relatesType == MemberOf);
7083       if (getClass(root->relates)==0 && !scope.isEmpty())
7084         scope=mergeScopes(scope,root->relates);
7085       else
7086         scope=root->relates;
7087       if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7088     }
7089 
7090     if (cd && !name.isEmpty()) // found a enum inside a compound
7091     {
7092       //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7093       fd=0;
7094       mnsd=Doxygen::memberNameLinkedMap;
7095       isGlobal=FALSE;
7096     }
7097     else if (nd) // found enum inside namespace
7098     {
7099       mnsd=Doxygen::functionNameLinkedMap;
7100       isGlobal=TRUE;
7101     }
7102     else // found a global enum
7103     {
7104       fd=root->fileDef();
7105       mnsd=Doxygen::functionNameLinkedMap;
7106       isGlobal=TRUE;
7107     }
7108 
7109     if (!name.isEmpty())
7110     {
7111       // new enum type
7112       std::unique_ptr<MemberDefMutable> md { createMemberDef(
7113           root->fileName,root->startLine,root->startColumn,
7114           QCString(),name,QCString(),QCString(),
7115           root->protection,Normal,FALSE,
7116           isMemberOf ? Foreign : isRelated ? Related : Member,
7117           MemberType_Enumeration,
7118           ArgumentList(),ArgumentList(),root->metaData) };
7119       md->setTagInfo(root->tagInfo());
7120       md->setLanguage(root->lang);
7121       md->setId(root->id);
7122       if (!isGlobal) md->setMemberClass(cd); else md->setFileDef(fd);
7123       md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7124       md->setBodyDef(root->fileDef());
7125       md->setMemberSpecifiers(root->spec);
7126       md->setEnumBaseType(root->args);
7127       //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7128       //    qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7129       md->addSectionsToDefinition(root->anchors);
7130       md->setMemberGroupId(root->mGrpId);
7131       md->enableCallGraph(root->callGraph);
7132       md->enableCallerGraph(root->callerGraph);
7133       md->enableReferencedByRelation(root->referencedByRelation);
7134       md->enableReferencesRelation(root->referencesRelation);
7135       //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7136       md->setRefItems(root->sli);
7137       //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7138       bool defSet=FALSE;
7139 
7140       QCString baseType = root->args;
7141       if (!baseType.isEmpty())
7142       {
7143         baseType.prepend(" : ");
7144       }
7145 
7146       if (nd)
7147       {
7148         if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7149         {
7150           md->setDefinition(name+baseType);
7151         }
7152         else
7153         {
7154           md->setDefinition(nd->name()+"::"+name+baseType);
7155         }
7156         //printf("definition=%s\n",md->definition());
7157         defSet=TRUE;
7158         md->setNamespace(nd);
7159         nd->insertMember(md.get());
7160       }
7161 
7162       // even if we have already added the enum to a namespace, we still
7163       // also want to add it to other appropriate places such as file
7164       // or class.
7165       if (isGlobal && (nd==0 || !nd->isAnonymous()))
7166       {
7167         if (!defSet) md->setDefinition(name+baseType);
7168         if (fd==0 && root->parent())
7169         {
7170           fd=root->parent()->fileDef();
7171         }
7172         if (fd)
7173         {
7174           md->setFileDef(fd);
7175           fd->insertMember(md.get());
7176         }
7177       }
7178       else if (cd)
7179       {
7180         if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7181         {
7182           md->setDefinition(name+baseType);
7183         }
7184         else
7185         {
7186           md->setDefinition(cd->name()+"::"+name+baseType);
7187         }
7188         cd->insertMember(md.get());
7189         cd->insertUsedFile(fd);
7190       }
7191       md->setDocumentation(root->doc,root->docFile,root->docLine);
7192       md->setDocsForDefinition(!root->proto);
7193       md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7194       md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7195 
7196       //printf("Adding member=%s\n",qPrint(md->name()));
7197       addMemberToGroups(root,md.get());
7198 
7199       MemberName *mn = mnsd->add(name);
7200       mn->push_back(std::move(md));
7201     }
7202   }
7203   else
7204   {
7205     for (const auto &e : root->children()) findEnums(e.get());
7206   }
7207 }
7208 
7209 //----------------------------------------------------------------------
7210 
addEnumValuesToEnums(const Entry * root)7211 static void addEnumValuesToEnums(const Entry *root)
7212 {
7213   if (root->section==Entry::ENUM_SEC)
7214     // non anonymous enumeration
7215   {
7216     ClassDefMutable     *cd=0;
7217     FileDef             *fd=0;
7218     NamespaceDefMutable *nd=0;
7219     MemberNameLinkedMap *mnsd=0;
7220     bool isGlobal;
7221     bool isRelated=FALSE;
7222     //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7223     int i;
7224 
7225     QCString name;
7226     QCString scope;
7227 
7228     if ((i=root->name.findRev("::"))!=-1) // scope is specified
7229     {
7230       scope=root->name.left(i); // extract scope
7231       name=root->name.right(root->name.length()-i-2); // extract name
7232       if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7233     }
7234     else // no scope, check the scope in which the docs where found
7235     {
7236       if (( root->parent()->section & Entry::SCOPE_MASK )
7237           && !root->parent()->name.isEmpty()
7238          ) // found enum docs inside a compound
7239       {
7240         scope=root->parent()->name;
7241         if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7242       }
7243       name=root->name;
7244     }
7245 
7246     if (!root->relates.isEmpty())
7247     {   // related member, prefix user specified scope
7248       isRelated=TRUE;
7249       if (getClassMutable(root->relates)==0 && !scope.isEmpty())
7250         scope=mergeScopes(scope,root->relates);
7251       else
7252         scope=root->relates;
7253       if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7254     }
7255 
7256     if (cd && !name.isEmpty()) // found a enum inside a compound
7257     {
7258       //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7259       fd=0;
7260       mnsd=Doxygen::memberNameLinkedMap;
7261       isGlobal=FALSE;
7262     }
7263     else if (nd && !nd->isAnonymous()) // found enum inside namespace
7264     {
7265       //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7266       mnsd=Doxygen::functionNameLinkedMap;
7267       isGlobal=TRUE;
7268     }
7269     else // found a global enum
7270     {
7271       fd=root->fileDef();
7272       //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7273       mnsd=Doxygen::functionNameLinkedMap;
7274       isGlobal=TRUE;
7275     }
7276 
7277     if (!name.isEmpty())
7278     {
7279       //printf("** name=%s\n",qPrint(name));
7280       MemberName *mn = mnsd->find(name); // for all members with this name
7281       if (mn)
7282       {
7283         struct EnumValueInfo
7284         {
7285           EnumValueInfo(const QCString &n,std::unique_ptr<MemberDefMutable> &md) :
7286             name(n), member(std::move(md)) {}
7287           QCString name;
7288           std::unique_ptr<MemberDefMutable> member;
7289         };
7290         std::vector< EnumValueInfo > extraMembers;
7291         // for each enum in this list
7292         for (const auto &imd : *mn)
7293         {
7294           MemberDefMutable *md = toMemberDefMutable(imd.get());
7295           // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7296           if (md && md->isEnumerate() && !root->children().empty())
7297           {
7298             //printf("   enum with %d children\n",root->children()->count());
7299             for (const auto &e : root->children())
7300             {
7301               SrcLangExt sle;
7302               if (
7303                    (sle=root->lang)==SrcLangExt_CSharp ||
7304                    sle==SrcLangExt_Java ||
7305                    sle==SrcLangExt_XML ||
7306                    (root->spec&Entry::Strong)
7307                  )
7308               {
7309                 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7310                 // values are only visible inside the enum scope, so we must create
7311                 // them here and only add them to the enum
7312                 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7313                 //    qPrint(md->qualifiedName()),qPrint(e->name),e->tagInfo,qPrint(e->name));
7314                 QCString qualifiedName = substitute(root->name,"::",".");
7315                 if (!scope.isEmpty() && root->tagInfo())
7316                 {
7317                   qualifiedName=substitute(scope,"::",".")+"."+qualifiedName;
7318                 }
7319                 if (substitute(md->qualifiedName(),"::",".")== // TODO: add function to get canonical representation
7320                     qualifiedName       // enum value scope matches that of the enum
7321                    )
7322                 {
7323                   QCString fileName = e->fileName;
7324                   if (fileName.isEmpty() && e->tagInfo())
7325                   {
7326                     fileName = e->tagInfo()->tagName;
7327                   }
7328                   std::unique_ptr<MemberDefMutable> fmd { createMemberDef(
7329                       fileName,e->startLine,e->startColumn,
7330                       e->type,e->name,e->args,QCString(),
7331                       e->protection, Normal,e->stat,Member,
7332                       MemberType_EnumValue,ArgumentList(),ArgumentList(),e->metaData) };
7333                   const NamespaceDef *mnd = md->getNamespaceDef();
7334                   if      (md->getClassDef())
7335                     fmd->setMemberClass(md->getClassDef());
7336                   else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7337                     fmd->setNamespace(mnd);
7338                   else if (md->getFileDef())
7339                     fmd->setFileDef(md->getFileDef());
7340                   fmd->setOuterScope(md->getOuterScope());
7341                   fmd->setTagInfo(e->tagInfo());
7342                   fmd->setLanguage(e->lang);
7343                   fmd->setId(e->id);
7344                   fmd->setDocumentation(e->doc,e->docFile,e->docLine);
7345                   fmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7346                   fmd->addSectionsToDefinition(e->anchors);
7347                   std::string init = e->initializer.str();
7348                   fmd->setInitializer(init.c_str());
7349                   fmd->setMaxInitLines(e->initLines);
7350                   fmd->setMemberGroupId(e->mGrpId);
7351                   fmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7352                   fmd->setRefItems(e->sli);
7353                   fmd->setAnchor();
7354                   md->insertEnumField(fmd.get());
7355                   fmd->setEnumScope(md,TRUE);
7356                   extraMembers.push_back(EnumValueInfo(e->name,fmd));
7357                 }
7358               }
7359               else
7360               {
7361                 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7362                 MemberName *fmn=0;
7363                 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7364                 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7365                   // get list of members with the same name as the field
7366                 {
7367                   for (const auto &ifmd : *fmn)
7368                   {
7369                     MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7370                     if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7371                     {
7372                       //printf("found enum value with same name %s in scope %s\n",
7373                       //    qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7374                       if (nd && !nd->isAnonymous())
7375                       {
7376                         const NamespaceDef *fnd=fmd->getNamespaceDef();
7377                         if (fnd==nd) // enum value is inside a namespace
7378                         {
7379                           md->insertEnumField(fmd);
7380                           fmd->setEnumScope(md);
7381                         }
7382                       }
7383                       else if (isGlobal)
7384                       {
7385                         const FileDef *ffd=fmd->getFileDef();
7386                         if (ffd==fd) // enum value has file scope
7387                         {
7388                           md->insertEnumField(fmd);
7389                           fmd->setEnumScope(md);
7390                         }
7391                       }
7392                       else if (isRelated && cd) // reparent enum value to
7393                                                 // match the enum's scope
7394                       {
7395                         md->insertEnumField(fmd);   // add field def to list
7396                         fmd->setEnumScope(md);      // cross ref with enum name
7397                         fmd->setEnumClassScope(cd); // cross ref with enum name
7398                         fmd->setOuterScope(cd);
7399                         fmd->makeRelated();
7400                         cd->insertMember(fmd);
7401                       }
7402                       else
7403                       {
7404                         const ClassDef *fcd=fmd->getClassDef();
7405                         if (fcd==cd) // enum value is inside a class
7406                         {
7407                           //printf("Inserting enum field %s in enum scope %s\n",
7408                           //    qPrint(fmd->name()),qPrint(md->name()));
7409                           md->insertEnumField(fmd); // add field def to list
7410                           fmd->setEnumScope(md);    // cross ref with enum name
7411                         }
7412                       }
7413                     }
7414                   }
7415                 }
7416               }
7417             }
7418           }
7419         }
7420         // move the newly added members into mn
7421         for (auto &e : extraMembers)
7422         {
7423           MemberName *emn=mnsd->add(e.name);
7424           emn->push_back(std::move(e.member));
7425         }
7426       }
7427     }
7428   }
7429   else
7430   {
7431     for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7432   }
7433 }
7434 
7435 //----------------------------------------------------------------------
7436 
addEnumDocs(const Entry * root,MemberDefMutable * md)7437 static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7438 {
7439   // documentation outside a compound overrides the documentation inside it
7440   {
7441     md->setDocumentation(root->doc,root->docFile,root->docLine);
7442     md->setDocsForDefinition(!root->proto);
7443   }
7444 
7445   // brief descriptions inside a compound override the documentation
7446   // outside it
7447   {
7448     md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7449   }
7450 
7451   if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7452   {
7453     md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7454   }
7455 
7456   if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7457   {
7458     md->setMemberGroupId(root->mGrpId);
7459   }
7460 
7461   md->addSectionsToDefinition(root->anchors);
7462   md->setRefItems(root->sli);
7463 
7464   const GroupDef *gd=md->getGroupDef();
7465   if (gd==0 && !root->groups.empty()) // member not grouped but out-of-line documentation is
7466   {
7467     addMemberToGroups(root,md);
7468   }
7469 }
7470 
7471 
7472 //----------------------------------------------------------------------
7473 // find the documentation blocks for the enumerations
7474 
findEnumDocumentation(const Entry * root)7475 static void findEnumDocumentation(const Entry *root)
7476 {
7477   if (root->section==Entry::ENUMDOC_SEC
7478       && !root->name.isEmpty()
7479       && root->name.at(0)!='@'        // skip anonymous enums
7480      )
7481   {
7482     int i;
7483     QCString name;
7484     QCString scope;
7485     if ((i=root->name.findRev("::"))!=-1) // scope is specified as part of the name
7486     {
7487       name=root->name.right(root->name.length()-i-2); // extract name
7488       scope=root->name.left(i); // extract scope
7489       //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7490     }
7491     else // just the name
7492     {
7493       name=root->name;
7494     }
7495     if (( root->parent()->section & Entry::SCOPE_MASK )
7496         && !root->parent()->name.isEmpty()
7497        ) // found enum docs inside a compound
7498     {
7499       if (!scope.isEmpty()) scope.prepend("::");
7500       scope.prepend(root->parent()->name);
7501     }
7502     const ClassDef *cd = getClass(scope);
7503     const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
7504     const FileDef *fd = root->fileDef();
7505     Debug::print(Debug::FindMembers,0,"1. Found docs for enum with name '%s' and scope '%s' in context %s cd=%s, nd=%s fd=%s\n",
7506                  qPrint(name),qPrint(scope),qPrint(root->parent()->name),
7507                  cd?qPrint(cd->name()):"<none>",
7508                  nd?qPrint(nd->name()):"<none>",
7509                  fd?qPrint(fd->name()):"<none>");
7510 
7511     if (!name.isEmpty())
7512     {
7513       bool found=FALSE;
7514       MemberName *mn;
7515       if (cd)
7516       {
7517         mn = Doxygen::memberNameLinkedMap->find(name);
7518       }
7519       else
7520       {
7521         mn = Doxygen::functionNameLinkedMap->find(name);
7522       }
7523       if (mn)
7524       {
7525         for (const auto &imd : *mn)
7526         {
7527           MemberDefMutable *md = toMemberDefMutable(imd.get());
7528           if (md && md->isEnumerate())
7529           {
7530             const ClassDef *mcd = md->getClassDef();
7531             const NamespaceDef *mnd = md->getNamespaceDef();
7532             const FileDef *mfd = md->getFileDef();
7533             if (cd && mcd==cd)
7534             {
7535               Debug::print(Debug::FindMembers,0,"2. Match found for class scope\n");
7536               addEnumDocs(root,md);
7537               found=TRUE;
7538               break;
7539             }
7540             else if (cd==0 && mcd==0 && nd!=0 && mnd==nd)
7541             {
7542               Debug::print(Debug::FindMembers,0,"2. Match found for namespace scope\n");
7543               addEnumDocs(root,md);
7544               found=TRUE;
7545               break;
7546             }
7547             else if (cd==0 && nd==0 && mcd==0 && mnd==0 && fd==mfd)
7548             {
7549               Debug::print(Debug::FindMembers,0,"2. Match found for global scope\n");
7550               addEnumDocs(root,md);
7551               found=TRUE;
7552               break;
7553             }
7554           }
7555         }
7556       }
7557       if (!found)
7558       {
7559         warn(root->fileName,root->startLine,
7560              "Documentation for undefined enum '%s' found.",
7561              qPrint(name)
7562             );
7563       }
7564     }
7565   }
7566   for (const auto &e : root->children()) findEnumDocumentation(e.get());
7567 }
7568 
7569 // search for each enum (member or function) in mnl if it has documented
7570 // enum values.
findDEV(const MemberNameLinkedMap & mnsd)7571 static void findDEV(const MemberNameLinkedMap &mnsd)
7572 {
7573   // for each member name
7574   for (const auto &mn : mnsd)
7575   {
7576     // for each member definition
7577     for (const auto &imd : *mn)
7578     {
7579       MemberDefMutable *md = toMemberDefMutable(imd.get());
7580       if (md && md->isEnumerate()) // member is an enum
7581       {
7582         int documentedEnumValues=0;
7583         // for each enum value
7584         for (const auto &fmd : md->enumFieldList())
7585         {
7586           if (fmd->isLinkableInProject()) documentedEnumValues++;
7587         }
7588         // at least one enum value is documented
7589         if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
7590       }
7591     }
7592   }
7593 }
7594 
7595 // search for each enum (member or function) if it has documented enum
7596 // values.
findDocumentedEnumValues()7597 static void findDocumentedEnumValues()
7598 {
7599   findDEV(*Doxygen::memberNameLinkedMap);
7600   findDEV(*Doxygen::functionNameLinkedMap);
7601 }
7602 
7603 //----------------------------------------------------------------------
7604 
addMembersToIndex()7605 static void addMembersToIndex()
7606 {
7607   // for each class member name
7608   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7609   {
7610     // for each member definition
7611     for (const auto &md : *mn)
7612     {
7613       addClassMemberNameToIndex(md.get());
7614     }
7615   }
7616   // for each file/namespace function name
7617   for (const auto &mn : *Doxygen::functionNameLinkedMap)
7618   {
7619     // for each member definition
7620     for (const auto &md : *mn)
7621     {
7622       if (md->getNamespaceDef())
7623       {
7624         addNamespaceMemberNameToIndex(md.get());
7625       }
7626       else
7627       {
7628         addFileMemberNameToIndex(md.get());
7629       }
7630     }
7631   }
7632   sortMemberIndexLists();
7633 }
7634 
7635 //----------------------------------------------------------------------
7636 
addToIndices()7637 static void addToIndices()
7638 {
7639   for (const auto &cd : *Doxygen::classLinkedMap)
7640   {
7641     if (cd->isLinkableInProject())
7642     {
7643       Doxygen::indexList->addIndexItem(cd.get(),0);
7644       if (Doxygen::searchIndex)
7645       {
7646         Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7647         Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7648       }
7649     }
7650   }
7651 
7652   for (const auto &cd : *Doxygen::conceptLinkedMap)
7653   {
7654     if (cd->isLinkableInProject())
7655     {
7656       Doxygen::indexList->addIndexItem(cd.get(),0);
7657       if (Doxygen::searchIndex)
7658       {
7659         Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7660         Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7661       }
7662     }
7663   }
7664 
7665   for (const auto &nd : *Doxygen::namespaceLinkedMap)
7666   {
7667     if (nd->isLinkableInProject())
7668     {
7669       Doxygen::indexList->addIndexItem(nd.get(),0);
7670       if (Doxygen::searchIndex)
7671       {
7672         Doxygen::searchIndex->setCurrentDoc(nd.get(),nd->anchor(),FALSE);
7673         Doxygen::searchIndex->addWord(nd->localName(),TRUE);
7674       }
7675     }
7676   }
7677 
7678   for (const auto &fn : *Doxygen::inputNameLinkedMap)
7679   {
7680     for (const auto &fd : *fn)
7681     {
7682       if (Doxygen::searchIndex && fd->isLinkableInProject())
7683       {
7684         Doxygen::searchIndex->setCurrentDoc(fd.get(),fd->anchor(),FALSE);
7685         Doxygen::searchIndex->addWord(fd->localName(),TRUE);
7686       }
7687     }
7688   }
7689 
7690   for (const auto &gd : *Doxygen::groupLinkedMap)
7691   {
7692     if (gd->isLinkableInProject())
7693     {
7694       Doxygen::indexList->addIndexItem(gd.get(),0,QCString(),gd->groupTitle());
7695       if (Doxygen::searchIndex)
7696       {
7697         Doxygen::searchIndex->setCurrentDoc(gd.get(),gd->anchor(),FALSE);
7698         std::string title = gd->groupTitle().str();
7699         static const reg::Ex re(R"(\a[\w-]*)");
7700         reg::Iterator it(title,re);
7701         reg::Iterator end;
7702         for (; it!=end ; ++it)
7703         {
7704           const auto &match = *it;
7705           std::string matchStr = match.str();
7706           Doxygen::searchIndex->addWord(matchStr.c_str(),TRUE);
7707         }
7708       }
7709     }
7710   }
7711 
7712   for (const auto &pd : *Doxygen::pageLinkedMap)
7713   {
7714     if (pd->isLinkableInProject())
7715     {
7716       Doxygen::indexList->addIndexItem(pd.get(),0,QCString(),filterTitle(pd->title().str()));
7717     }
7718   }
7719 
7720   auto addMemberToSearchIndex = [](const MemberDef *md)
7721   {
7722     if (Doxygen::searchIndex)
7723     {
7724       Doxygen::searchIndex->setCurrentDoc(md,md->anchor(),FALSE);
7725       QCString ln=md->localName();
7726       QCString qn=md->qualifiedName();
7727       Doxygen::searchIndex->addWord(ln,TRUE);
7728       if (ln!=qn)
7729       {
7730         Doxygen::searchIndex->addWord(qn,TRUE);
7731         if (md->getClassDef())
7732         {
7733           Doxygen::searchIndex->addWord(md->getClassDef()->displayName(),TRUE);
7734         }
7735         if (md->getNamespaceDef())
7736         {
7737           Doxygen::searchIndex->addWord(md->getNamespaceDef()->displayName(),TRUE);
7738         }
7739       }
7740     }
7741   };
7742 
7743   auto getScope = [](const MemberDef *md)
7744   {
7745     const Definition *scope = 0;
7746     if (md->getGroupDef())          scope = md->getGroupDef();
7747     else if (md->getClassDef())     scope = md->getClassDef();
7748     else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
7749     else if (md->getFileDef())      scope = md->getFileDef();
7750     return scope;
7751   };
7752 
7753   auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
7754   {
7755     if (md->isLinkableInProject())
7756     {
7757       if (!(md->isEnumerate() && md->isAnonymous()))
7758       {
7759         Doxygen::indexList->addIndexItem(getScope(md),md);
7760         addMemberToSearchIndex(md);
7761       }
7762       if (md->isEnumerate())
7763       {
7764         for (const auto &fmd : md->enumFieldList())
7765         {
7766           Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
7767           addMemberToSearchIndex(fmd);
7768         }
7769       }
7770     }
7771   };
7772 
7773   // for each class member name
7774   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7775   {
7776     // for each member definition
7777     for (const auto &md : *mn)
7778     {
7779       addMemberToIndices(md.get());
7780     }
7781   }
7782   // for each file/namespace function name
7783   for (const auto &mn : *Doxygen::functionNameLinkedMap)
7784   {
7785     // for each member definition
7786     for (const auto &md : *mn)
7787     {
7788       addMemberToIndices(md.get());
7789     }
7790   }
7791 }
7792 
7793 //----------------------------------------------------------------------
7794 
vhdlCorrectMemberProperties()7795 static void vhdlCorrectMemberProperties()
7796 {
7797   // for each member name
7798   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7799   {
7800     // for each member definition
7801     for (const auto &imd : *mn)
7802     {
7803       MemberDefMutable *md = toMemberDefMutable(imd.get());
7804       if (md)
7805       {
7806         VhdlDocGen::correctMemberProperties(md);
7807       }
7808     }
7809   }
7810   // for each member name
7811   for (const auto &mn : *Doxygen::functionNameLinkedMap)
7812   {
7813     // for each member definition
7814     for (const auto &imd : *mn)
7815     {
7816       MemberDefMutable *md = toMemberDefMutable(imd.get());
7817       if (md)
7818       {
7819         VhdlDocGen::correctMemberProperties(md);
7820       }
7821     }
7822   }
7823 }
7824 
7825 
7826 //----------------------------------------------------------------------
7827 // computes the relation between all members. For each member 'm'
7828 // the members that override the implementation of 'm' are searched and
7829 // the member that 'm' overrides is searched.
7830 
computeMemberRelations()7831 static void computeMemberRelations()
7832 {
7833   for (const auto &mn : *Doxygen::memberNameLinkedMap)
7834   {
7835     // for each member with a specific name
7836     for (const auto &imd : *mn)
7837     {
7838       MemberDefMutable *md = toMemberDefMutable(imd.get());
7839       if (md)
7840       {
7841         // for each other member with the same name
7842         for ( const auto &ibmd : *mn)
7843         {
7844           MemberDefMutable *bmd = toMemberDefMutable(ibmd.get());
7845           if (bmd && md!=bmd)
7846           {
7847             const ClassDef *mcd  = md->getClassDef();
7848             if (mcd && !mcd->baseClasses().empty())
7849             {
7850               const ClassDef *bmcd = bmd->getClassDef();
7851               //printf("Check relation between '%s'::'%s' (%p) and '%s'::'%s' (%p)\n",
7852               //       qPrint(mcd->name()),qPrint(md->name()),md.get(),
7853               //       qPrint(bmcd->name()),qPrint(bmd->name()),bmd.get()
7854               //      );
7855               if (bmcd && mcd && bmcd!=mcd &&
7856                   (bmd->virtualness()!=Normal ||
7857                    bmd->getLanguage()==SrcLangExt_Python || bmd->getLanguage()==SrcLangExt_Java || bmd->getLanguage()==SrcLangExt_PHP ||
7858                    bmcd->compoundType()==ClassDef::Interface || bmcd->compoundType()==ClassDef::Protocol
7859                   ) &&
7860                   (md->isFunction() || md->isCSharpProperty()) &&
7861                   mcd->isLinkable() &&
7862                   bmcd->isLinkable() &&
7863                   mcd->isBaseClass(bmcd,TRUE))
7864               {
7865                 //printf("  derived scope\n");
7866                 const ArgumentList &bmdAl = bmd->argumentList();
7867                 const ArgumentList &mdAl =  md->argumentList();
7868                 //printf(" Base argList='%s'\n Super argList='%s'\n",
7869                 //        qPrint(argListToString(bmdAl)),
7870                 //        qPrint(argListToString(mdAl))
7871                 //      );
7872                 if (
7873                     bmd->getLanguage()==SrcLangExt_Python ||
7874                     matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
7875                       md->getOuterScope(), md->getFileDef(), &mdAl,
7876                       TRUE
7877                       )
7878                    )
7879                 {
7880                   //printf("match!\n");
7881                   const MemberDef *rmd = md->reimplements();
7882                   if (rmd==0 || minClassDistance(mcd,bmcd)<minClassDistance(mcd,rmd->getClassDef()))
7883                   {
7884                     //printf("setting (new) reimplements member\n");
7885                     md->setReimplements(bmd);
7886                   }
7887                   //printf("%s: add reimplementedBy member %s\n",qPrint(bmcd->name()),qPrint(mcd->name()));
7888                   bmd->insertReimplementedBy(md);
7889                 }
7890                 else
7891                 {
7892                   //printf("no match!\n");
7893                 }
7894               }
7895             }
7896           }
7897         }
7898       }
7899     }
7900   }
7901 }
7902 
7903 //----------------------------------------------------------------------------
7904 
createTemplateInstanceMembers()7905 static void createTemplateInstanceMembers()
7906 {
7907   // for each class
7908   for (const auto &cd : *Doxygen::classLinkedMap)
7909   {
7910     // that is a template
7911     for (const auto &ti : cd->getTemplateInstances())
7912     {
7913       ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
7914       if (tcdm)
7915       {
7916         tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
7917       }
7918     }
7919   }
7920 }
7921 
7922 //----------------------------------------------------------------------------
7923 
mergeCategories()7924 static void mergeCategories()
7925 {
7926   // merge members of categories into the class they extend
7927   for (const auto &cd : *Doxygen::classLinkedMap)
7928   {
7929     int i=cd->name().find('(');
7930     if (i!=-1) // it is an Objective-C category
7931     {
7932       QCString baseName=cd->name().left(i);
7933       ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
7934       if (baseClass)
7935       {
7936         //printf("*** merging members of category %s into %s\n",
7937         //    qPrint(cd->name()),qPrint(baseClass->name()));
7938         baseClass->mergeCategory(cd.get());
7939       }
7940     }
7941   }
7942 }
7943 
7944 // builds the list of all members for each class
7945 
buildCompleteMemberLists()7946 static void buildCompleteMemberLists()
7947 {
7948   // merge the member list of base classes into the inherited classes.
7949   for (const auto &cd : *Doxygen::classLinkedMap)
7950   {
7951     if (// !cd->isReference() && // not an external class
7952          cd->subClasses().empty() && // is a root of the hierarchy
7953          !cd->baseClasses().empty()) // and has at least one base class
7954     {
7955       ClassDefMutable *cdm = toClassDefMutable(cd.get());
7956       if (cdm)
7957       {
7958         //printf("*** merging members for %s\n",qPrint(cd->name()));
7959         cdm->mergeMembers();
7960       }
7961     }
7962   }
7963   // now sort the member list of all members for all classes.
7964   for (const auto &cd : *Doxygen::classLinkedMap)
7965   {
7966     ClassDefMutable *cdm = toClassDefMutable(cd.get());
7967     if (cdm)
7968     {
7969       cdm->sortAllMembersList();
7970     }
7971   }
7972 }
7973 
7974 //----------------------------------------------------------------------------
7975 
generateFileSources()7976 static void generateFileSources()
7977 {
7978   if (!Doxygen::inputNameLinkedMap->empty())
7979   {
7980 #if USE_LIBCLANG
7981     if (Doxygen::clangAssistedParsing)
7982     {
7983       StringUnorderedSet processedFiles;
7984 
7985       // create a dictionary with files to process
7986       StringUnorderedSet filesToProcess;
7987 
7988       for (const auto &fn : *Doxygen::inputNameLinkedMap)
7989       {
7990         for (const auto &fd : *fn)
7991         {
7992           filesToProcess.insert(fd->absFilePath().str());
7993         }
7994       }
7995       // process source files (and their include dependencies)
7996       for (const auto &fn : *Doxygen::inputNameLinkedMap)
7997       {
7998         for (const auto &fd : *fn)
7999         {
8000           if (fd->isSource() && !fd->isReference() &&
8001               ((fd->generateSourceFile() && !g_useOutputTemplate) ||
8002                (!fd->isReference() && Doxygen::parseSourcesNeeded)
8003               )
8004              )
8005           {
8006             auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8007             if (fd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output
8008             {
8009               msg("Generating code for file %s...\n",qPrint(fd->docName()));
8010               clangParser->parse();
8011               fd->writeSourceHeader(*g_outputList);
8012               fd->writeSourceBody(*g_outputList,clangParser.get());
8013               fd->writeSourceFooter(*g_outputList);
8014             }
8015             else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
8016               // we needed to parse the sources even if we do not show them
8017             {
8018               msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8019               clangParser->parse();
8020               fd->parseSource(clangParser.get());
8021             }
8022 
8023             for (auto incFile : clangParser->filesInSameTU())
8024             {
8025               if (filesToProcess.find(incFile)!=filesToProcess.end() &&  // part of input
8026                   fd->absFilePath()!=QCString(incFile) &&                // not same file
8027                   processedFiles.find(incFile)==processedFiles.end())    // not yet marked as processed
8028               {
8029                 StringVector moreFiles;
8030                 bool ambig;
8031                 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
8032                 if (ifd && !ifd->isReference())
8033                 {
8034                   if (ifd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output
8035                   {
8036                     msg(" Generating code for file %s...\n",qPrint(ifd->docName()));
8037                     ifd->writeSourceHeader(*g_outputList);
8038                     ifd->writeSourceBody(*g_outputList,clangParser.get());
8039                     ifd->writeSourceFooter(*g_outputList);
8040                   }
8041                   else if (!ifd->isReference() && Doxygen::parseSourcesNeeded)
8042                     // we needed to parse the sources even if we do not show them
8043                   {
8044                     msg(" Parsing code for file %s...\n",qPrint(ifd->docName()));
8045                     ifd->parseSource(clangParser.get());
8046                   }
8047                   processedFiles.insert(incFile);
8048                 }
8049               }
8050             }
8051             processedFiles.insert(fd->absFilePath().str());
8052           }
8053         }
8054       }
8055       // process remaining files
8056       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8057       {
8058         for (const auto &fd : *fn)
8059         {
8060           if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8061           {
8062             if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output
8063             {
8064               auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8065               msg("Generating code for file %s...\n",qPrint(fd->docName()));
8066               clangParser->parse();
8067               fd->writeSourceHeader(*g_outputList);
8068               fd->writeSourceBody(*g_outputList,clangParser.get());
8069               fd->writeSourceFooter(*g_outputList);
8070             }
8071             else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
8072               // we needed to parse the sources even if we do not show them
8073             {
8074               auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8075               msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8076               clangParser->parse();
8077               fd->writeSourceHeader(*g_outputList);
8078               fd->writeSourceBody(*g_outputList,clangParser.get());
8079               fd->writeSourceFooter(*g_outputList);
8080             }
8081           }
8082         }
8083       }
8084     }
8085     else
8086 #endif
8087     {
8088       std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8089       if (numThreads==0)
8090       {
8091         numThreads = std::thread::hardware_concurrency();
8092       }
8093       if (numThreads>1)
8094       {
8095         msg("Generating code files using %zu threads.\n",numThreads);
8096         struct SourceContext
8097         {
8098           SourceContext(FileDef *fd_,bool gen_,OutputList ol_)
8099             : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8100           FileDef *fd;
8101           bool generateSourceFile;
8102           OutputList ol;
8103         };
8104         ThreadPool threadPool(numThreads);
8105         std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8106         for (const auto &fn : *Doxygen::inputNameLinkedMap)
8107         {
8108           for (const auto &fd : *fn)
8109           {
8110             bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate;
8111             auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8112             if (generateSourceFile)
8113             {
8114               fd->writeSourceHeader(ctx->ol);
8115             }
8116             auto processFile = [ctx]() {
8117               if (ctx->generateSourceFile)
8118               {
8119                 msg("Generating code for file %s...\n",qPrint(ctx->fd->docName()));
8120               }
8121               else
8122               {
8123                 msg("Parsing code for file %s...\n",qPrint(ctx->fd->docName()));
8124               }
8125               StringVector filesInSameTu;
8126               ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8127               if (ctx->generateSourceFile) // sources need to be shown in the output
8128               {
8129                 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8130               }
8131               else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8132                 // we needed to parse the sources even if we do not show them
8133               {
8134                 ctx->fd->parseSource(nullptr);
8135               }
8136               return ctx;
8137             };
8138             results.emplace_back(threadPool.queue(processFile));
8139           }
8140         }
8141         for (auto &f : results)
8142         {
8143           auto ctx = f.get();
8144           if (ctx->generateSourceFile)
8145           {
8146             ctx->fd->writeSourceFooter(ctx->ol);
8147           }
8148         }
8149       }
8150       else // single threaded version
8151       {
8152         for (const auto &fn : *Doxygen::inputNameLinkedMap)
8153         {
8154           for (const auto &fd : *fn)
8155           {
8156             StringVector filesInSameTu;
8157             fd->getAllIncludeFilesRecursively(filesInSameTu);
8158             if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output
8159             {
8160               msg("Generating code for file %s...\n",qPrint(fd->docName()));
8161               fd->writeSourceHeader(*g_outputList);
8162               fd->writeSourceBody(*g_outputList,nullptr);
8163               fd->writeSourceFooter(*g_outputList);
8164             }
8165             else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
8166               // we needed to parse the sources even if we do not show them
8167             {
8168               msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8169               fd->parseSource(nullptr);
8170             }
8171           }
8172         }
8173       }
8174     }
8175   }
8176 }
8177 
8178 //----------------------------------------------------------------------------
8179 
generateFileDocs()8180 static void generateFileDocs()
8181 {
8182   if (documentedFiles==0) return;
8183 
8184   if (!Doxygen::inputNameLinkedMap->empty())
8185   {
8186     std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8187     if (numThreads==0)
8188     {
8189       numThreads = std::thread::hardware_concurrency();
8190     }
8191     if (numThreads>1) // multi threaded processing
8192     {
8193       struct DocContext
8194       {
8195         DocContext(FileDef *fd_,OutputList ol_)
8196           : fd(fd_), ol(ol_) {}
8197         FileDef *fd;
8198         OutputList ol;
8199       };
8200       ThreadPool threadPool(numThreads);
8201       std::vector< std::future< std::shared_ptr<DocContext> > > results;
8202       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8203       {
8204         for (const auto &fd : *fn)
8205         {
8206           bool doc = fd->isLinkableInProject();
8207           if (doc)
8208           {
8209             auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8210             auto processFile = [ctx]() {
8211               msg("Generating docs for file %s...\n",qPrint(ctx->fd->docName()));
8212               ctx->fd->writeDocumentation(ctx->ol);
8213               return ctx;
8214             };
8215             results.emplace_back(threadPool.queue(processFile));
8216           }
8217         }
8218       }
8219       for (auto &f : results)
8220       {
8221         auto ctx = f.get();
8222       }
8223     }
8224     else // single threaded processing
8225     {
8226       for (const auto &fn : *Doxygen::inputNameLinkedMap)
8227       {
8228         for (const auto &fd : *fn)
8229         {
8230           bool doc = fd->isLinkableInProject();
8231           if (doc)
8232           {
8233             msg("Generating docs for file %s...\n",qPrint(fd->docName()));
8234             fd->writeDocumentation(*g_outputList);
8235           }
8236         }
8237       }
8238     }
8239   }
8240 }
8241 
8242 //----------------------------------------------------------------------------
8243 
addSourceReferences()8244 static void addSourceReferences()
8245 {
8246   // add source references for class definitions
8247   for (const auto &cd : *Doxygen::classLinkedMap)
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 concept definitions
8256   for (const auto &cd : *Doxygen::conceptLinkedMap)
8257   {
8258     const FileDef *fd=cd->getBodyDef();
8259     if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8260     {
8261       const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8262     }
8263   }
8264   // add source references for namespace definitions
8265   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8266   {
8267     const FileDef *fd=nd->getBodyDef();
8268     if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8269     {
8270       const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),0);
8271     }
8272   }
8273 
8274   // add source references for member names
8275   for (const auto &mn : *Doxygen::memberNameLinkedMap)
8276   {
8277     for (const auto &md : *mn)
8278     {
8279       //printf("class member %s: def=%s body=%d link?=%d\n",
8280       //    qPrint(md->name()),
8281       //    md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8282       //    md->getStartBodyLine(),md->isLinkableInProject());
8283       const FileDef *fd=md->getBodyDef();
8284       if (fd &&
8285           md->getStartDefLine()!=-1 &&
8286           md->isLinkableInProject() &&
8287           (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8288          )
8289       {
8290         //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8291         //    qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8292         const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8293       }
8294     }
8295   }
8296   for (const auto &mn : *Doxygen::functionNameLinkedMap)
8297   {
8298     for (const auto &md : *mn)
8299     {
8300       const FileDef *fd=md->getBodyDef();
8301       //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8302       //    qPrint(md->name()),
8303       //    md->getStartBodyLine(),md->getEndBodyLine(),fd,
8304       //    md->isLinkableInProject(),
8305       //    Doxygen::parseSourcesNeeded);
8306       if (fd &&
8307           md->getStartDefLine()!=-1 &&
8308           md->isLinkableInProject() &&
8309           (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8310          )
8311       {
8312         //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8313         //    qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8314         const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8315       }
8316     }
8317   }
8318 }
8319 
8320 //----------------------------------------------------------------------------
8321 
8322 // add the macro definitions found during preprocessing as file members
buildDefineList()8323 static void buildDefineList()
8324 {
8325   for (const auto &s : g_inputFiles)
8326   {
8327     auto it = Doxygen::macroDefinitions.find(s);
8328     if (it!=Doxygen::macroDefinitions.end())
8329     {
8330       for (const auto &def : it->second)
8331       {
8332         std::unique_ptr<MemberDefMutable> md { createMemberDef(
8333             def.fileName,def.lineNr,def.columnNr,
8334             "#define",def.name,def.args,QCString(),
8335             Public,Normal,FALSE,Member,MemberType_Define,
8336             ArgumentList(),ArgumentList(),"") };
8337 
8338         if (!def.args.isEmpty())
8339         {
8340           md->moveArgumentList(stringToArgumentList(SrcLangExt_Cpp, def.args));
8341         }
8342         md->setInitializer(def.definition);
8343         md->setFileDef(def.fileDef);
8344         md->setDefinition("#define "+def.name);
8345 
8346         MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8347         if (def.fileDef)
8348         {
8349           def.fileDef->insertMember(md.get());
8350         }
8351         mn->push_back(std::move(md));
8352       }
8353     }
8354   }
8355 }
8356 
8357 //----------------------------------------------------------------------------
8358 
sortMemberLists()8359 static void sortMemberLists()
8360 {
8361   // sort class member lists
8362   for (const auto &cd : *Doxygen::classLinkedMap)
8363   {
8364     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8365     if (cdm)
8366     {
8367       cdm->sortMemberLists();
8368     }
8369   }
8370 
8371   // sort namespace member lists
8372   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8373   {
8374     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8375     if (ndm)
8376     {
8377       ndm->sortMemberLists();
8378     }
8379   }
8380 
8381   // sort file member lists
8382   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8383   {
8384     for (const auto &fd : *fn)
8385     {
8386       fd->sortMemberLists();
8387     }
8388   }
8389 
8390   // sort group member lists
8391   for (const auto &gd : *Doxygen::groupLinkedMap)
8392   {
8393     gd->sortMemberLists();
8394   }
8395 }
8396 
8397 //----------------------------------------------------------------------------
8398 
isSymbolHidden(const Definition * d)8399 static bool isSymbolHidden(const Definition *d)
8400 {
8401   bool hidden = d->isHidden();
8402   const Definition *parent = d->getOuterScope();
8403   return parent ? hidden || isSymbolHidden(parent) : hidden;
8404 }
8405 
computeTooltipTexts()8406 static void computeTooltipTexts()
8407 {
8408   for (const auto &kv : *Doxygen::symbolMap)
8409   {
8410     DefinitionMutable *dm = toDefinitionMutable(kv.second);
8411     if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
8412     {
8413       dm->computeTooltip();
8414     }
8415   }
8416 }
8417 
8418 //----------------------------------------------------------------------------
8419 
setAnonymousEnumType()8420 static void setAnonymousEnumType()
8421 {
8422   for (const auto &cd : *Doxygen::classLinkedMap)
8423   {
8424     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8425     if (cdm)
8426     {
8427       cdm->setAnonymousEnumType();
8428     }
8429   }
8430 }
8431 
8432 //----------------------------------------------------------------------------
8433 
countMembers()8434 static void countMembers()
8435 {
8436   for (const auto &cd : *Doxygen::classLinkedMap)
8437   {
8438     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8439     if (cdm)
8440     {
8441       cdm->countMembers();
8442     }
8443   }
8444 
8445   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8446   {
8447     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8448     if (ndm)
8449     {
8450       ndm->countMembers();
8451     }
8452   }
8453 
8454   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8455   {
8456     for (const auto &fd : *fn)
8457     {
8458       fd->countMembers();
8459     }
8460   }
8461 
8462   for (const auto &gd : *Doxygen::groupLinkedMap)
8463   {
8464     gd->countMembers();
8465   }
8466 }
8467 
8468 
8469 //----------------------------------------------------------------------------
8470 // generate the documentation of all classes
8471 
generateClassList(const ClassLinkedMap & classList)8472 static void generateClassList(const ClassLinkedMap &classList)
8473 {
8474   std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8475   if (numThreads==0)
8476   {
8477     numThreads = std::thread::hardware_concurrency();
8478   }
8479   if (numThreads>1) // multi threaded processing
8480   {
8481     struct DocContext
8482     {
8483       DocContext(ClassDefMutable *cd_,OutputList ol_)
8484         : cd(cd_), ol(ol_) {}
8485       ClassDefMutable *cd;
8486       OutputList ol;
8487     };
8488     ThreadPool threadPool(numThreads);
8489     std::vector< std::future< std::shared_ptr<DocContext> > > results;
8490     for (const auto &cdi : classList)
8491     {
8492       ClassDefMutable *cd=toClassDefMutable(cdi.get());
8493 
8494       //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8495       if (cd &&
8496           (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8497            cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8498           ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8499          )
8500       {
8501         auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
8502         auto processFile = [ctx]()
8503         {
8504           msg("Generating docs for compound %s...\n",qPrint(ctx->cd->name()));
8505 
8506           // skip external references, anonymous compounds and
8507           // template instances
8508           if ( ctx->cd->isLinkableInProject() && ctx->cd->templateMaster()==0)
8509           {
8510             ctx->cd->writeDocumentation(ctx->ol);
8511             ctx->cd->writeMemberList(ctx->ol);
8512           }
8513 
8514           // even for undocumented classes, the inner classes can be documented.
8515           ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
8516           return ctx;
8517         };
8518         results.emplace_back(threadPool.queue(processFile));
8519       }
8520     }
8521     for (auto &f : results)
8522     {
8523       auto ctx = f.get();
8524     }
8525   }
8526   else // single threaded processing
8527   {
8528     for (const auto &cdi : classList)
8529     {
8530       ClassDefMutable *cd=toClassDefMutable(cdi.get());
8531 
8532       //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8533       if (cd &&
8534           (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8535            cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8536           ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8537          )
8538       {
8539         // skip external references, anonymous compounds and
8540         // template instances
8541         if ( cd->isLinkableInProject() && cd->templateMaster()==0)
8542         {
8543           msg("Generating docs for compound %s...\n",qPrint(cd->name()));
8544 
8545           cd->writeDocumentation(*g_outputList);
8546           cd->writeMemberList(*g_outputList);
8547         }
8548         // even for undocumented classes, the inner classes can be documented.
8549         cd->writeDocumentationForInnerClasses(*g_outputList);
8550       }
8551     }
8552   }
8553 }
8554 
generateClassDocs()8555 static void generateClassDocs()
8556 {
8557   generateClassList(*Doxygen::classLinkedMap);
8558   generateClassList(*Doxygen::hiddenClassLinkedMap);
8559 }
8560 
8561 //----------------------------------------------------------------------------
8562 
generateConceptDocs()8563 static void generateConceptDocs()
8564 {
8565   for (const auto &cdi : *Doxygen::conceptLinkedMap)
8566   {
8567     ConceptDefMutable *cd=toConceptDefMutable(cdi.get());
8568 
8569     //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8570     if (cd &&
8571         (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8572          cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
8573         ) && !cd->isHidden() && cd->isLinkableInProject()
8574        )
8575     {
8576       msg("Generating docs for concept %s...\n",qPrint(cd->name()));
8577       cd->writeDocumentation(*g_outputList);
8578     }
8579   }
8580 }
8581 
8582 //----------------------------------------------------------------------------
8583 
inheritDocumentation()8584 static void inheritDocumentation()
8585 {
8586   for (const auto &mn : *Doxygen::memberNameLinkedMap)
8587   {
8588     for (const auto &imd : *mn)
8589     {
8590       MemberDefMutable *md = toMemberDefMutable(imd.get());
8591       //static int count=0;
8592       //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
8593       if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
8594       { // no documentation yet
8595         const MemberDef *bmd = md->reimplements();
8596         while (bmd && bmd->documentation().isEmpty() &&
8597                       bmd->briefDescription().isEmpty()
8598               )
8599         { // search up the inheritance tree for a documentation member
8600           //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
8601           bmd = bmd->reimplements();
8602         }
8603         if (bmd) // copy the documentation from the reimplemented member
8604         {
8605           md->setInheritsDocsFrom(bmd);
8606           md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
8607           md->setDocsForDefinition(bmd->isDocsForDefinition());
8608           md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
8609           md->copyArgumentNames(bmd);
8610           md->setInbodyDocumentation(bmd->inbodyDocumentation(),bmd->inbodyFile(),bmd->inbodyLine());
8611         }
8612       }
8613     }
8614   }
8615 }
8616 
8617 //----------------------------------------------------------------------------
8618 
combineUsingRelations()8619 static void combineUsingRelations()
8620 {
8621   // for each file
8622   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8623   {
8624     for (const auto &fd : *fn)
8625     {
8626       fd->combineUsingRelations();
8627     }
8628   }
8629 
8630   // for each namespace
8631   NamespaceDefSet visitedNamespaces;
8632   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8633   {
8634     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8635     if (ndm)
8636     {
8637       ndm->combineUsingRelations(visitedNamespaces);
8638     }
8639   }
8640 }
8641 
8642 //----------------------------------------------------------------------------
8643 
addMembersToMemberGroup()8644 static void addMembersToMemberGroup()
8645 {
8646   // for each class
8647   for (const auto &cd : *Doxygen::classLinkedMap)
8648   {
8649     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8650     if (cdm)
8651     {
8652       cdm->addMembersToMemberGroup();
8653     }
8654   }
8655   // for each file
8656   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8657   {
8658     for (const auto &fd : *fn)
8659     {
8660       fd->addMembersToMemberGroup();
8661     }
8662   }
8663   // for each namespace
8664   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8665   {
8666     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8667     if (ndm)
8668     {
8669       ndm->addMembersToMemberGroup();
8670     }
8671   }
8672   // for each group
8673   for (const auto &gd : *Doxygen::groupLinkedMap)
8674   {
8675     gd->addMembersToMemberGroup();
8676   }
8677 }
8678 
8679 //----------------------------------------------------------------------------
8680 
distributeMemberGroupDocumentation()8681 static void distributeMemberGroupDocumentation()
8682 {
8683   // for each class
8684   for (const auto &cd : *Doxygen::classLinkedMap)
8685   {
8686     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8687     if (cdm)
8688     {
8689       cdm->distributeMemberGroupDocumentation();
8690     }
8691   }
8692   // for each file
8693   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8694   {
8695     for (const auto &fd : *fn)
8696     {
8697       fd->distributeMemberGroupDocumentation();
8698     }
8699   }
8700   // for each namespace
8701   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8702   {
8703     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8704     if (ndm)
8705     {
8706       ndm->distributeMemberGroupDocumentation();
8707     }
8708   }
8709   // for each group
8710   for (const auto &gd : *Doxygen::groupLinkedMap)
8711   {
8712     gd->distributeMemberGroupDocumentation();
8713   }
8714 }
8715 
8716 //----------------------------------------------------------------------------
8717 
findSectionsInDocumentation()8718 static void findSectionsInDocumentation()
8719 {
8720   // for each class
8721   for (const auto &cd : *Doxygen::classLinkedMap)
8722   {
8723     ClassDefMutable *cdm = toClassDefMutable(cd.get());
8724     if (cdm)
8725     {
8726       cdm->findSectionsInDocumentation();
8727     }
8728   }
8729   // for each concept
8730   for (const auto &cd : *Doxygen::conceptLinkedMap)
8731   {
8732     ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
8733     if (cdm)
8734     {
8735       cdm->findSectionsInDocumentation();
8736     }
8737   }
8738   // for each file
8739   for (const auto &fn : *Doxygen::inputNameLinkedMap)
8740   {
8741     for (const auto &fd : *fn)
8742     {
8743       fd->findSectionsInDocumentation();
8744     }
8745   }
8746   // for each namespace
8747   for (const auto &nd : *Doxygen::namespaceLinkedMap)
8748   {
8749     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8750     if (ndm)
8751     {
8752       ndm->findSectionsInDocumentation();
8753     }
8754   }
8755   // for each group
8756   for (const auto &gd : *Doxygen::groupLinkedMap)
8757   {
8758     gd->findSectionsInDocumentation();
8759   }
8760   // for each page
8761   for (const auto &pd : *Doxygen::pageLinkedMap)
8762   {
8763     pd->findSectionsInDocumentation();
8764   }
8765   if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
8766 }
8767 
8768 //----------------------------------------------------------------------
8769 
8770 
flushCachedTemplateRelations()8771 static void flushCachedTemplateRelations()
8772 {
8773   // remove all references to classes from the cache
8774   // as there can be new template instances in the inheritance path
8775   // to this class. Optimization: only remove those classes that
8776   // have inheritance instances as direct or indirect sub classes.
8777   StringVector elementsToRemove;
8778   for (const auto &ci : *Doxygen::lookupCache)
8779   {
8780     const LookupInfo &li = ci.second;
8781     if (li.classDef)
8782     {
8783       elementsToRemove.push_back(ci.first);
8784     }
8785   }
8786   for (const auto &k : elementsToRemove)
8787   {
8788     Doxygen::lookupCache->remove(k);
8789   }
8790 
8791   // remove all cached typedef resolutions whose target is a
8792   // template class as this may now be a template instance
8793   // for each global function name
8794   for (const auto &fn : *Doxygen::functionNameLinkedMap)
8795   {
8796     // for each function with that name
8797     for (const auto &ifmd : *fn)
8798     {
8799       MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8800       if (fmd && fmd->isTypedefValCached())
8801       {
8802         const ClassDef *cd = fmd->getCachedTypedefVal();
8803         if (cd->isTemplate()) fmd->invalidateTypedefValCache();
8804       }
8805     }
8806   }
8807   // for each class method name
8808   for (const auto &nm : *Doxygen::memberNameLinkedMap)
8809   {
8810     // for each function with that name
8811     for (const auto &imd : *nm)
8812     {
8813       MemberDefMutable *md = toMemberDefMutable(imd.get());
8814       if (md && md->isTypedefValCached())
8815       {
8816         const ClassDef *cd = md->getCachedTypedefVal();
8817         if (cd->isTemplate()) md->invalidateTypedefValCache();
8818       }
8819     }
8820   }
8821 }
8822 
8823 //----------------------------------------------------------------------------
8824 
flushUnresolvedRelations()8825 static void flushUnresolvedRelations()
8826 {
8827   // Remove all unresolved references to classes from the cache.
8828   // This is needed before resolving the inheritance relations, since
8829   // it would otherwise not find the inheritance relation
8830   // for C in the example below, as B::I was already found to be unresolvable
8831   // (which is correct if you ignore the inheritance relation between A and B).
8832   //
8833   // class A { class I {} };
8834   // class B : public A {};
8835   // class C : public B::I {};
8836 
8837   StringVector elementsToRemove;
8838   for (const auto &ci : *Doxygen::lookupCache)
8839   {
8840     const LookupInfo &li = ci.second;
8841     if (li.classDef==0 && li.typeDef==0)
8842     {
8843       elementsToRemove.push_back(ci.first);
8844     }
8845   }
8846   for (const auto &k : elementsToRemove)
8847   {
8848     Doxygen::lookupCache->remove(k);
8849   }
8850 
8851   // for each global function name
8852   for (const auto &fn : *Doxygen::functionNameLinkedMap)
8853   {
8854     // for each function with that name
8855     for (const auto &ifmd : *fn)
8856     {
8857       MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8858       if (fmd)
8859       {
8860         fmd->invalidateCachedArgumentTypes();
8861       }
8862     }
8863   }
8864   // for each class method name
8865   for (const auto &nm : *Doxygen::memberNameLinkedMap)
8866   {
8867     // for each function with that name
8868     for (const auto &imd : *nm)
8869     {
8870       MemberDefMutable *md = toMemberDefMutable(imd.get());
8871       if (md)
8872       {
8873         md->invalidateCachedArgumentTypes();
8874       }
8875     }
8876   }
8877 
8878 }
8879 
8880 //----------------------------------------------------------------------------
8881 
findDefineDocumentation(Entry * root)8882 static void findDefineDocumentation(Entry *root)
8883 {
8884   if ((root->section==Entry::DEFINEDOC_SEC ||
8885        root->section==Entry::DEFINE_SEC) && !root->name.isEmpty()
8886      )
8887   {
8888     //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
8889     //       qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
8890 
8891     if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
8892     {
8893       std::unique_ptr<MemberDefMutable> md { createMemberDef(root->tagInfo()->tagName,1,1,
8894                     "#define",root->name,root->args,QCString(),
8895                     Public,Normal,FALSE,Member,MemberType_Define,
8896                     ArgumentList(),ArgumentList(),"") };
8897       md->setTagInfo(root->tagInfo());
8898       md->setLanguage(root->lang);
8899       //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
8900       md->setFileDef(root->parent()->fileDef());
8901       //printf("Adding member=%s\n",qPrint(md->name()));
8902       MemberName *mn = Doxygen::functionNameLinkedMap->add(root->name);
8903       mn->push_back(std::move(md));
8904     }
8905     MemberName *mn=Doxygen::functionNameLinkedMap->find(root->name);
8906     if (mn)
8907     {
8908       int count=0;
8909       for (const auto &md : *mn)
8910       {
8911         if (md->memberType()==MemberType_Define) count++;
8912       }
8913       if (count==1)
8914       {
8915         for (const auto &imd : *mn)
8916         {
8917           MemberDefMutable *md = toMemberDefMutable(imd.get());
8918           if (md && md->memberType()==MemberType_Define)
8919           {
8920             md->setDocumentation(root->doc,root->docFile,root->docLine);
8921             md->setDocsForDefinition(!root->proto);
8922             md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8923             if (md->inbodyDocumentation().isEmpty())
8924             {
8925               md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
8926             }
8927             md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
8928             md->setBodyDef(root->fileDef());
8929             md->addSectionsToDefinition(root->anchors);
8930             md->setMaxInitLines(root->initLines);
8931             md->setRefItems(root->sli);
8932             if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
8933             addMemberToGroups(root,md);
8934           }
8935         }
8936       }
8937       else if (count>1 &&
8938                (!root->doc.isEmpty() ||
8939                 !root->brief.isEmpty() ||
8940                 root->bodyLine!=-1
8941                )
8942               )
8943         // multiple defines don't know where to add docs
8944         // but maybe they are in different files together with their documentation
8945       {
8946         for (const auto &imd : *mn)
8947         {
8948           MemberDefMutable *md = toMemberDefMutable(imd.get());
8949           if (md && md->memberType()==MemberType_Define)
8950           {
8951             const FileDef *fd=md->getFileDef();
8952             if (fd && fd->absFilePath()==root->fileName)
8953               // doc and define in the same file assume they belong together.
8954             {
8955               md->setDocumentation(root->doc,root->docFile,root->docLine);
8956               md->setDocsForDefinition(!root->proto);
8957               md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8958               if (md->inbodyDocumentation().isEmpty())
8959               {
8960                 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
8961               }
8962               md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
8963               md->setBodyDef(root->fileDef());
8964               md->addSectionsToDefinition(root->anchors);
8965               md->setRefItems(root->sli);
8966               md->setLanguage(root->lang);
8967               if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
8968               addMemberToGroups(root,md);
8969             }
8970           }
8971         }
8972         //warn("define %s found in the following files:\n",qPrint(root->name));
8973         //warn("Cannot determine where to add the documentation found "
8974         //     "at line %d of file %s. \n",
8975         //     root->startLine,qPrint(root->fileName));
8976       }
8977     }
8978     else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
8979     {
8980       static bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
8981       if (preEnabled)
8982       {
8983         warn(root->fileName,root->startLine,
8984              "documentation for unknown define %s found.\n",
8985              qPrint(root->name)
8986             );
8987       }
8988       else
8989       {
8990         warn(root->fileName,root->startLine,
8991              "found documented #define %s but ignoring it because "
8992              "ENABLE_PREPROCESSING is NO.\n",
8993              qPrint(root->name)
8994             );
8995       }
8996     }
8997   }
8998   for (const auto &e : root->children()) findDefineDocumentation(e.get());
8999 }
9000 
9001 //----------------------------------------------------------------------------
9002 
findDirDocumentation(const Entry * root)9003 static void findDirDocumentation(const Entry *root)
9004 {
9005   if (root->section == Entry::DIRDOC_SEC)
9006   {
9007     QCString normalizedName = root->name;
9008     normalizedName = substitute(normalizedName,"\\","/");
9009     //printf("root->docFile=%s normalizedName=%s\n",
9010     //    qPrint(root->docFile),qPrint(normalizedName));
9011     if (root->docFile==normalizedName) // current dir?
9012     {
9013       int lastSlashPos=normalizedName.findRev('/');
9014       if (lastSlashPos!=-1) // strip file name
9015       {
9016         normalizedName=normalizedName.left(lastSlashPos);
9017       }
9018     }
9019     if (normalizedName.at(normalizedName.length()-1)!='/')
9020     {
9021       normalizedName+='/';
9022     }
9023     DirDef *matchingDir=0;
9024     for (const auto &dir : *Doxygen::dirLinkedMap)
9025     {
9026       //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9027       if (dir->name().right(normalizedName.length())==normalizedName)
9028       {
9029         if (matchingDir)
9030         {
9031            warn(root->fileName,root->startLine,
9032              "\\dir command matches multiple directories.\n"
9033              "  Applying the command for directory %s\n"
9034              "  Ignoring the command for directory %s\n",
9035              qPrint(matchingDir->name()),qPrint(dir->name())
9036            );
9037         }
9038         else
9039         {
9040           matchingDir=dir.get();
9041         }
9042       }
9043     }
9044     if (matchingDir)
9045     {
9046       //printf("Match for with dir %s\n",qPrint(matchingDir->name()));
9047       matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9048       matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9049       matchingDir->setRefItems(root->sli);
9050       addDirToGroups(root,matchingDir);
9051     }
9052     else
9053     {
9054       warn(root->fileName,root->startLine,"No matching "
9055           "directory found for command \\dir %s\n",qPrint(normalizedName));
9056     }
9057   }
9058   for (const auto &e : root->children()) findDirDocumentation(e.get());
9059 }
9060 
9061 
9062 //----------------------------------------------------------------------------
9063 // create a (sorted) list of separate documentation pages
9064 
buildPageList(Entry * root)9065 static void buildPageList(Entry *root)
9066 {
9067   if (root->section == Entry::PAGEDOC_SEC)
9068   {
9069     if (!root->name.isEmpty())
9070     {
9071       addRelatedPage(root);
9072     }
9073   }
9074   else if (root->section == Entry::MAINPAGEDOC_SEC)
9075   {
9076     QCString title=root->args.stripWhiteSpace();
9077     if (title.isEmpty()) title=theTranslator->trMainPage();
9078     //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9079     QCString name = "index";
9080     addRefItem(root->sli,
9081                name,
9082                "page",
9083                name,
9084                title,
9085                QCString(),0
9086                );
9087   }
9088   for (const auto &e : root->children()) buildPageList(e.get());
9089 }
9090 
9091 // search for the main page defined in this project
findMainPage(Entry * root)9092 static void findMainPage(Entry *root)
9093 {
9094   if (root->section == Entry::MAINPAGEDOC_SEC)
9095   {
9096     if (Doxygen::mainPage==0 && root->tagInfo()==0)
9097     {
9098       //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9099       //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9100       QCString title=root->args.stripWhiteSpace();
9101       if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9102       //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9103       QCString indexName="index";
9104       Doxygen::mainPage.reset(createPageDef(root->docFile,root->docLine,
9105                               indexName, root->brief+root->doc+root->inbodyDocs,title));
9106       //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9107       Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9108       Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9109       Doxygen::mainPage->setFileName(indexName);
9110       Doxygen::mainPage->setLocalToc(root->localToc);
9111       addPageToContext(Doxygen::mainPage.get(),root);
9112 
9113       const SectionInfo *si = SectionManager::instance().find(Doxygen::mainPage->name());
9114       if (si)
9115       {
9116         if (!si->ref().isEmpty()) // we are from a tag file
9117         {
9118           // a page name is a label as well! but should no be double either
9119           SectionManager::instance().replace(
9120             Doxygen::mainPage->name(),
9121             indexName,
9122             root->startLine,
9123             Doxygen::mainPage->title(),
9124             SectionType::Page,
9125             0); // level 0
9126         }
9127         else if (si->lineNr() != -1)
9128         {
9129           warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s, line %d)",
9130                qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()),si->lineNr());
9131         }
9132         else
9133         {
9134           warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s)",
9135                qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()));
9136         }
9137       }
9138       else
9139       {
9140         // a page name is a label as well! but should no be double either
9141         SectionManager::instance().add(
9142           Doxygen::mainPage->name(),
9143           indexName,
9144           root->startLine,
9145           Doxygen::mainPage->title(),
9146           SectionType::Page,
9147           0); // level 0
9148       }
9149       Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9150     }
9151     else if (root->tagInfo()==0)
9152     {
9153       warn(root->fileName,root->startLine,
9154            "found more than one \\mainpage comment block! (first occurrence: %s, line %d), Skipping current block!",
9155            qPrint(Doxygen::mainPage->docFile()),Doxygen::mainPage->getStartBodyLine());
9156     }
9157   }
9158   for (const auto &e : root->children()) findMainPage(e.get());
9159 }
9160 
9161 // search for the main page imported via tag files and add only the section labels
findMainPageTagFiles(Entry * root)9162 static void findMainPageTagFiles(Entry *root)
9163 {
9164   if (root->section == Entry::MAINPAGEDOC_SEC)
9165   {
9166     if (Doxygen::mainPage && root->tagInfo())
9167     {
9168       Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9169     }
9170   }
9171   for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9172 }
9173 
computePageRelations(Entry * root)9174 static void computePageRelations(Entry *root)
9175 {
9176   if ((root->section==Entry::PAGEDOC_SEC ||
9177        root->section==Entry::MAINPAGEDOC_SEC
9178       )
9179       && !root->name.isEmpty()
9180      )
9181   {
9182     PageDef *pd = root->section==Entry::PAGEDOC_SEC ?
9183                     Doxygen::pageLinkedMap->find(root->name) :
9184                     Doxygen::mainPage.get();
9185     if (pd)
9186     {
9187       for (const BaseInfo &bi : root->extends)
9188       {
9189         PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9190         if (pd==subPd)
9191         {
9192          term("page defined at line %d of file %s with label %s is a direct "
9193              "subpage of itself! Please remove this cyclic dependency.\n",
9194               pd->docLine(),qPrint(pd->docFile()),qPrint(pd->name()));
9195         }
9196         else if (subPd)
9197         {
9198           pd->addInnerCompound(subPd);
9199           //printf("*** Added subpage relation: %s->%s\n",
9200           //    qPrint(pd->name()),qPrint(subPd->name()));
9201         }
9202       }
9203     }
9204   }
9205   for (const auto &e : root->children()) computePageRelations(e.get());
9206 }
9207 
checkPageRelations()9208 static void checkPageRelations()
9209 {
9210   for (const auto &pd : *Doxygen::pageLinkedMap)
9211   {
9212     Definition *ppd = pd->getOuterScope();
9213     while (ppd)
9214     {
9215       if (ppd==pd.get())
9216       {
9217         term("page defined at line %d of file %s with label %s is a subpage "
9218             "of itself! Please remove this cyclic dependency.\n",
9219             pd->docLine(),qPrint(pd->docFile()),qPrint(pd->name()));
9220       }
9221       ppd=ppd->getOuterScope();
9222     }
9223   }
9224 }
9225 
9226 //----------------------------------------------------------------------------
9227 
resolveUserReferences()9228 static void resolveUserReferences()
9229 {
9230   for (const auto &si : SectionManager::instance())
9231   {
9232     //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9233     //        qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9234     //        qPrint(si->fileName));
9235     PageDef *pd=0;
9236 
9237     // hack: the items of a todo/test/bug/deprecated list are all fragments from
9238     // different files, so the resulting section's all have the wrong file
9239     // name (not from the todo/test/bug/deprecated list, but from the file in
9240     // which they are defined). We correct this here by looking at the
9241     // generated section labels!
9242     for (const RefListManager::Ptr &rl : RefListManager::instance())
9243     {
9244       QCString label="_"+rl->listName(); // "_todo", "_test", ...
9245       if (si->label().left(label.length())==label)
9246       {
9247         si->setFileName(rl->listName());
9248         si->setGenerated(TRUE);
9249         break;
9250       }
9251     }
9252 
9253     //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9254     if (!si->generated())
9255     {
9256       // if this section is in a page and the page is in a group, then we
9257       // have to adjust the link file name to point to the group.
9258       if (!si->fileName().isEmpty() &&
9259           (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9260           pd->getGroupDef())
9261       {
9262         si->setFileName(pd->getGroupDef()->getOutputFileBase());
9263       }
9264 
9265       if (si->definition())
9266       {
9267         // TODO: there should be one function in Definition that returns
9268         // the file to link to, so we can avoid the following tests.
9269         const GroupDef *gd=0;
9270         if (si->definition()->definitionType()==Definition::TypeMember)
9271         {
9272           gd = (toMemberDef(si->definition()))->getGroupDef();
9273         }
9274 
9275         if (gd)
9276         {
9277           si->setFileName(gd->getOutputFileBase());
9278         }
9279         else
9280         {
9281           //si->fileName=si->definition->getOutputFileBase();
9282           //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9283         }
9284       }
9285     }
9286     //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9287   }
9288 }
9289 
9290 
9291 
9292 //----------------------------------------------------------------------------
9293 // generate all separate documentation pages
9294 
9295 
generatePageDocs()9296 static void generatePageDocs()
9297 {
9298   //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9299   if (documentedPages==0) return;
9300   for (const auto &pd : *Doxygen::pageLinkedMap)
9301   {
9302     if (!pd->getGroupDef() && !pd->isReference())
9303     {
9304       msg("Generating docs for page %s...\n",qPrint(pd->name()));
9305       Doxygen::insideMainPage=TRUE;
9306       pd->writeDocumentation(*g_outputList);
9307       Doxygen::insideMainPage=FALSE;
9308     }
9309   }
9310 }
9311 
9312 //----------------------------------------------------------------------------
9313 // create a (sorted) list & dictionary of example pages
9314 
buildExampleList(Entry * root)9315 static void buildExampleList(Entry *root)
9316 {
9317   if ((root->section==Entry::EXAMPLE_SEC || root->section==Entry::EXAMPLE_LINENO_SEC) && !root->name.isEmpty())
9318   {
9319     if (Doxygen::exampleLinkedMap->find(root->name))
9320     {
9321       warn(root->fileName,root->startLine,
9322           "Example %s was already documented. Ignoring "
9323           "documentation found here.",
9324           qPrint(root->name)
9325           );
9326     }
9327     else
9328     {
9329       PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9330              std::unique_ptr<PageDef>(
9331                createPageDef(root->fileName,root->startLine,
9332                  root->name,root->brief+root->doc+root->inbodyDocs,root->args)));
9333       pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9334       pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9335       pd->addSectionsToDefinition(root->anchors);
9336       pd->setLanguage(root->lang);
9337       pd->setShowLineNo(root->section==Entry::EXAMPLE_LINENO_SEC);
9338 
9339       //we don't add example to groups
9340       //addExampleToGroups(root,pd);
9341     }
9342   }
9343   for (const auto &e : root->children()) buildExampleList(e.get());
9344 }
9345 
9346 //----------------------------------------------------------------------------
9347 // prints the Entry tree (for debugging)
9348 
printNavTree(Entry * root,int indent)9349 void printNavTree(Entry *root,int indent)
9350 {
9351   QCString indentStr;
9352   indentStr.fill(' ',indent);
9353   msg("%s%s (sec=0x%x)\n",
9354       indentStr.isEmpty()?"":qPrint(indentStr),
9355       root->name.isEmpty()?"<empty>":qPrint(root->name),
9356       root->section);
9357   for (const auto &e : root->children())
9358   {
9359     printNavTree(e.get(),indent+2);
9360   }
9361 }
9362 
9363 
9364 //----------------------------------------------------------------------------
9365 // generate the example documentation
9366 
generateExampleDocs()9367 static void generateExampleDocs()
9368 {
9369   g_outputList->disable(OutputGenerator::Man);
9370   for (const auto &pd : *Doxygen::exampleLinkedMap)
9371   {
9372     msg("Generating docs for example %s...\n",qPrint(pd->name()));
9373     auto intf = Doxygen::parserManager->getCodeParser(".c"); // TODO: do this on code type
9374     intf->resetCodeParserState();
9375     QCString n=pd->getOutputFileBase();
9376     startFile(*g_outputList,n,n,pd->name());
9377     startTitle(*g_outputList,n);
9378     g_outputList->docify(pd->name());
9379     endTitle(*g_outputList,n,QCString());
9380     g_outputList->startContents();
9381     QCString lineNoOptStr;
9382     if (pd->showLineNo())
9383     {
9384       lineNoOptStr="{lineno}";
9385     }
9386     g_outputList->generateDoc(pd->docFile(),                       // file
9387                          pd->docLine(),                            // startLine
9388                          pd.get(),                                 // context
9389                          0,                                        // memberDef
9390                          (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9391                          pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9392                          TRUE,                                     // index words
9393                          TRUE,                                     // is example
9394                          pd->name(),
9395                          FALSE,
9396                          FALSE,
9397                          Config_getBool(MARKDOWN_SUPPORT)
9398                         );
9399     endFile(*g_outputList); // contains g_outputList->endContents()
9400   }
9401   g_outputList->enable(OutputGenerator::Man);
9402 }
9403 
9404 //----------------------------------------------------------------------------
9405 // generate module pages
9406 
generateGroupDocs()9407 static void generateGroupDocs()
9408 {
9409   for (const auto &gd : *Doxygen::groupLinkedMap)
9410   {
9411     if (!gd->isReference())
9412     {
9413       gd->writeDocumentation(*g_outputList);
9414     }
9415   }
9416 }
9417 
9418 //----------------------------------------------------------------------------
9419 // generate module pages
9420 
generateNamespaceClassDocs(const ClassLinkedRefMap & classList)9421 static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
9422 {
9423   std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9424   if (numThreads==0)
9425   {
9426     numThreads = std::thread::hardware_concurrency();
9427   }
9428   if (numThreads>1) // multi threaded processing
9429   {
9430     struct DocContext
9431     {
9432       DocContext(ClassDefMutable *cdm_,OutputList ol_)
9433         : cdm(cdm_), ol(ol_) {}
9434       ClassDefMutable *cdm;
9435       OutputList ol;
9436     };
9437     ThreadPool threadPool(numThreads);
9438     std::vector< std::future< std::shared_ptr<DocContext> > > results;
9439     // for each class in the namespace...
9440     for (const auto &cd : classList)
9441     {
9442       ClassDefMutable *cdm = toClassDefMutable(cd);
9443       if (cdm)
9444       {
9445         auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
9446         auto processFile = [ctx]()
9447         {
9448           if ( ( ctx->cdm->isLinkableInProject() &&
9449                 ctx->cdm->templateMaster()==0
9450                ) // skip external references, anonymous compounds and
9451               // template instances and nested classes
9452               && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
9453              )
9454           {
9455             msg("Generating docs for compound %s...\n",qPrint(ctx->cdm->name()));
9456             ctx->cdm->writeDocumentation(ctx->ol);
9457             ctx->cdm->writeMemberList(ctx->ol);
9458           }
9459           ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
9460           return ctx;
9461         };
9462         results.emplace_back(threadPool.queue(processFile));
9463       }
9464     }
9465     // wait for the results
9466     for (auto &f : results)
9467     {
9468       auto ctx = f.get();
9469     }
9470   }
9471   else // single threaded processing
9472   {
9473     // for each class in the namespace...
9474     for (const auto &cd : classList)
9475     {
9476       ClassDefMutable *cdm = toClassDefMutable(cd);
9477       if (cdm)
9478       {
9479         if ( ( cd->isLinkableInProject() &&
9480               cd->templateMaster()==0
9481              ) // skip external references, anonymous compounds and
9482             // template instances and nested classes
9483             && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
9484            )
9485         {
9486           msg("Generating docs for compound %s...\n",qPrint(cd->name()));
9487 
9488           cdm->writeDocumentation(*g_outputList);
9489           cdm->writeMemberList(*g_outputList);
9490         }
9491         cdm->writeDocumentationForInnerClasses(*g_outputList);
9492       }
9493     }
9494   }
9495 }
9496 
generateNamespaceConceptDocs(const ConceptLinkedRefMap & conceptList)9497 static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
9498 {
9499   // for each concept in the namespace...
9500   for (const auto &cd : conceptList)
9501   {
9502     ConceptDefMutable *cdm = toConceptDefMutable(cd);
9503     if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
9504     {
9505       msg("Generating docs for concept %s...\n",qPrint(cd->name()));
9506       cdm->writeDocumentation(*g_outputList);
9507     }
9508   }
9509 }
9510 
generateNamespaceDocs()9511 static void generateNamespaceDocs()
9512 {
9513   static bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
9514 
9515   //writeNamespaceIndex(*g_outputList);
9516 
9517   // for each namespace...
9518   for (const auto &nd : *Doxygen::namespaceLinkedMap)
9519   {
9520     if (nd->isLinkableInProject())
9521     {
9522       NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
9523       if (ndm)
9524       {
9525         msg("Generating docs for namespace %s\n",qPrint(nd->name()));
9526         ndm->writeDocumentation(*g_outputList);
9527       }
9528     }
9529 
9530     generateNamespaceClassDocs(nd->getClasses());
9531     if (sliceOpt)
9532     {
9533       generateNamespaceClassDocs(nd->getInterfaces());
9534       generateNamespaceClassDocs(nd->getStructs());
9535       generateNamespaceClassDocs(nd->getExceptions());
9536     }
9537     generateNamespaceConceptDocs(nd->getConcepts());
9538   }
9539 }
9540 
9541 #if defined(_WIN32)
fixSlashes(QCString & s)9542 static QCString fixSlashes(QCString &s)
9543 {
9544   QCString result;
9545   uint i;
9546   for (i=0;i<s.length();i++)
9547   {
9548     switch(s.at(i))
9549     {
9550       case '/':
9551       case '\\':
9552         result+="\\\\";
9553         break;
9554       default:
9555         result+=s.at(i);
9556     }
9557   }
9558   return result;
9559 }
9560 #endif
9561 
9562 
9563 //----------------------------------------------------------------------------
9564 
9565 /*! Generate a template version of the configuration file.
9566  *  If the \a shortList parameter is TRUE a configuration file without
9567  *  comments will be generated.
9568  */
generateConfigFile(const QCString & configFile,bool shortList,bool updateOnly=FALSE)9569 static void generateConfigFile(const QCString &configFile,bool shortList,
9570                                bool updateOnly=FALSE)
9571 {
9572   std::ofstream f;
9573   bool fileOpened=openOutputFile(configFile,f);
9574   bool writeToStdout=configFile=="-";
9575   if (fileOpened)
9576   {
9577     TextStream t(&f);
9578     Config::writeTemplate(t,shortList,updateOnly);
9579     if (!writeToStdout)
9580     {
9581       if (!updateOnly)
9582       {
9583         msg("\n\nConfiguration file '%s' created.\n\n",qPrint(configFile));
9584         msg("Now edit the configuration file and enter\n\n");
9585         if (configFile!="Doxyfile" && configFile!="doxyfile")
9586           msg("  doxygen %s\n\n",qPrint(configFile));
9587         else
9588           msg("  doxygen\n\n");
9589         msg("to generate the documentation for your project\n\n");
9590       }
9591       else
9592       {
9593         msg("\n\nConfiguration file '%s' updated.\n\n",qPrint(configFile));
9594       }
9595     }
9596   }
9597   else
9598   {
9599     term("Cannot open file %s for writing\n",qPrint(configFile));
9600   }
9601 }
9602 
compareDoxyfile()9603 static void compareDoxyfile()
9604 {
9605   std::ofstream f;
9606   bool fileOpened=openOutputFile("-",f);
9607   if (fileOpened)
9608   {
9609     TextStream t(&f);
9610     Config::compareDoxyfile(t);
9611   }
9612   else
9613   {
9614     term("Cannot open stdout for writing\n");
9615   }
9616 }
9617 
9618 //----------------------------------------------------------------------------
9619 // read and parse a tag file
9620 
readTagFile(const std::shared_ptr<Entry> & root,const QCString & tagLine)9621 static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
9622 {
9623   QCString fileName;
9624   QCString destName;
9625   int eqPos = tagLine.find('=');
9626   if (eqPos!=-1) // tag command contains a destination
9627   {
9628     fileName = tagLine.left(eqPos).stripWhiteSpace();
9629     destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
9630     if (fileName.isEmpty() || destName.isEmpty()) return;
9631     FileInfo fi(fileName.str());
9632     Doxygen::tagDestinationMap.insert(
9633         std::make_pair(fi.absFilePath(), destName.str()));
9634     //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
9635   }
9636   else
9637   {
9638     fileName = tagLine;
9639   }
9640 
9641   FileInfo fi(fileName.str());
9642   if (!fi.exists() || !fi.isFile())
9643   {
9644     err("Tag file '%s' does not exist or is not a file. Skipping it...\n",
9645         qPrint(fileName));
9646     return;
9647   }
9648 
9649   if (!destName.isEmpty())
9650     msg("Reading tag file '%s', location '%s'...\n",qPrint(fileName),qPrint(destName));
9651   else
9652     msg("Reading tag file '%s'...\n",qPrint(fileName));
9653 
9654   parseTagFile(root,fi.absFilePath().c_str());
9655 }
9656 
9657 //----------------------------------------------------------------------------
copyLatexStyleSheet()9658 static void copyLatexStyleSheet()
9659 {
9660   const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
9661   for (const auto &sheet : latexExtraStyleSheet)
9662   {
9663     std::string fileName = sheet;
9664     if (!fileName.empty())
9665     {
9666       FileInfo fi(fileName);
9667       if (!fi.exists())
9668       {
9669         err("Style sheet '%s' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName));
9670       }
9671       else
9672       {
9673         QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
9674         if (!checkExtension(fi.fileName().c_str(), LATEX_STYLE_EXTENSION))
9675         {
9676           destFileName += LATEX_STYLE_EXTENSION;
9677         }
9678         copyFile(QCString(fileName), destFileName);
9679       }
9680     }
9681   }
9682 }
9683 
9684 //----------------------------------------------------------------------------
copyStyleSheet()9685 static void copyStyleSheet()
9686 {
9687   QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
9688   if (!htmlStyleSheet.isEmpty())
9689   {
9690     FileInfo fi(htmlStyleSheet.str());
9691     if (!fi.exists())
9692     {
9693       err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",qPrint(htmlStyleSheet));
9694       htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
9695     }
9696     else
9697     {
9698       QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9699       copyFile(htmlStyleSheet,destFileName);
9700     }
9701   }
9702   const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
9703   for (const auto &sheet : htmlExtraStyleSheet)
9704   {
9705     std::string fileName = sheet;
9706     if (!fileName.empty())
9707     {
9708       FileInfo fi(fileName);
9709       if (!fi.exists())
9710       {
9711         err("Style sheet '%s' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName.c_str());
9712       }
9713       else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
9714       {
9715         err("Style sheet %s specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",qPrint(fi.fileName()));
9716       }
9717       else
9718       {
9719         QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9720         copyFile(QCString(fileName), destFileName);
9721       }
9722     }
9723   }
9724 }
9725 
copyLogo(const QCString & outputOption)9726 static void copyLogo(const QCString &outputOption)
9727 {
9728   QCString projectLogo = Config_getString(PROJECT_LOGO);
9729   if (!projectLogo.isEmpty())
9730   {
9731     FileInfo fi(projectLogo.str());
9732     if (!fi.exists())
9733     {
9734       err("Project logo '%s' specified by PROJECT_LOGO does not exist!\n",qPrint(projectLogo));
9735       projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
9736     }
9737     else
9738     {
9739       QCString destFileName = outputOption+"/"+fi.fileName();
9740       copyFile(projectLogo,destFileName);
9741       Doxygen::indexList->addImageFile(fi.fileName().c_str());
9742     }
9743   }
9744 }
9745 
copyExtraFiles(const StringVector & files,const QCString & filesOption,const QCString & outputOption)9746 static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
9747 {
9748   for (const auto &fileName : files)
9749   {
9750     if (!fileName.empty())
9751     {
9752       FileInfo fi(fileName);
9753       if (!fi.exists())
9754       {
9755         err("Extra file '%s' specified in %s does not exist!\n", fileName.c_str(),qPrint(filesOption));
9756       }
9757       else
9758       {
9759         QCString destFileName = outputOption+"/"+fi.fileName();
9760         Doxygen::indexList->addImageFile(fi.fileName().c_str());
9761         copyFile(QCString(fileName), destFileName);
9762       }
9763     }
9764   }
9765 }
9766 
9767 //----------------------------------------------------------------------------
9768 
generateDiskNames()9769 static void generateDiskNames()
9770 {
9771   for (const auto &fn : *Doxygen::inputNameLinkedMap)
9772   {
9773     struct FileEntry
9774     {
9775       FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
9776       QCString path;
9777       FileDef *fileDef;
9778     };
9779 
9780     // collect the entry for which to compute the longest common prefix (LCP) of the path
9781     std::vector<FileEntry> fileEntries;
9782     for (const auto &fd : *fn)
9783     {
9784       if (!fd->isReference()) // skip external references
9785       {
9786         fileEntries.emplace_back(fd->getPath(),fd.get());
9787       }
9788     }
9789 
9790     size_t size = fileEntries.size();
9791 
9792     if (size==1) // name if unique, so diskname is simply the name
9793     {
9794       FileDef *fd = fileEntries[0].fileDef;
9795       fd->setDiskName(fn->fileName());
9796     }
9797     else if (size>1) // multiple occurrences of the same file name
9798     {
9799       // sort the array
9800       std::sort(fileEntries.begin(),
9801                 fileEntries.end(),
9802                 [](const FileEntry &fe1,const FileEntry &fe2)
9803                 { return fe1.path < fe2.path; }
9804                );
9805 
9806       // since the entries are sorted, the common prefix of the whole array is same
9807       // as the common prefix between the first and last entry
9808       const FileEntry &first = fileEntries[0];
9809       const FileEntry &last =  fileEntries[size-1];
9810       int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
9811       int last_path_size  = static_cast<int>(last.path.size())-1;  // -1 to skip trailing slash
9812       int j=0;
9813       int i=0;
9814       for (i=0;i<first_path_size && i<last_path_size;i++)
9815       {
9816         if (first.path[i]=='/') j=i;
9817         if (first.path[i]!=last.path[i]) break;
9818       }
9819       if (i==first_path_size && i<last_path_size && last.path[i]=='/')
9820       {
9821         // case first='some/path' and last='some/path/more' => match is 'some/path'
9822         j=first_path_size;
9823       }
9824       else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
9825       {
9826         // case first='some/path/more' and last='some/path' => match is 'some/path'
9827         j=last_path_size;
9828       }
9829 
9830       // add non-common part of the path to the name
9831       for (auto &fileEntry : fileEntries)
9832       {
9833          QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
9834          fileEntry.fileDef->setName(prefix+fn->fileName());
9835          //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
9836          fileEntry.fileDef->setDiskName(prefix+fn->fileName());
9837       }
9838     }
9839   }
9840 }
9841 
9842 
9843 
9844 //----------------------------------------------------------------------------
9845 
getParserForFile(const QCString & fn)9846 static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
9847 {
9848   QCString fileName=fn;
9849   QCString extension;
9850   int sep = fileName.findRev('/');
9851   int ei = fileName.findRev('.');
9852   if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
9853   {
9854     extension=fileName.right(fileName.length()-ei);
9855   }
9856   else
9857   {
9858     extension = ".no_extension";
9859   }
9860 
9861   return Doxygen::parserManager->getOutlineParser(extension);
9862 }
9863 
parseFile(OutlineParserInterface & parser,FileDef * fd,const QCString & fn,ClangTUParser * clangParser,bool newTU)9864 static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
9865                       FileDef *fd,const QCString &fn,
9866                       ClangTUParser *clangParser,bool newTU)
9867 {
9868   QCString fileName=fn;
9869   QCString extension;
9870   int ei = fileName.findRev('.');
9871   if (ei!=-1)
9872   {
9873     extension=fileName.right(fileName.length()-ei);
9874   }
9875   else
9876   {
9877     extension = ".no_extension";
9878   }
9879 
9880   FileInfo fi(fileName.str());
9881   BufStr preBuf((uint)fi.size()+4096);
9882 
9883   if (Config_getBool(ENABLE_PREPROCESSING) &&
9884       parser.needsPreprocessing(extension))
9885   {
9886     Preprocessor preprocessor;
9887     const StringVector &includePath = Config_getList(INCLUDE_PATH);
9888     for (const auto &s : includePath)
9889     {
9890       std::string absPath = FileInfo(s).absFilePath();
9891       preprocessor.addSearchDir(absPath.c_str());
9892     }
9893     BufStr inBuf((uint)fi.size()+4096);
9894     msg("Preprocessing %s...\n",qPrint(fn));
9895     readInputFile(fileName,inBuf);
9896     preprocessor.processFile(fileName,inBuf,preBuf);
9897   }
9898   else // no preprocessing
9899   {
9900     msg("Reading %s...\n",qPrint(fn));
9901     readInputFile(fileName,preBuf);
9902   }
9903   if (preBuf.data() && preBuf.curPos()>0 && *(preBuf.data()+preBuf.curPos()-1)!='\n')
9904   {
9905     preBuf.addChar('\n'); // add extra newline to help parser
9906   }
9907 
9908   BufStr convBuf(preBuf.curPos()+1024);
9909 
9910   // convert multi-line C++ comments to C style comments
9911   convertCppComments(&preBuf,&convBuf,fileName);
9912 
9913   convBuf.addChar('\0');
9914 
9915   std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
9916   // use language parse to parse the file
9917   if (clangParser)
9918   {
9919     if (newTU) clangParser->parse();
9920     clangParser->switchToFile(fd);
9921   }
9922   parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
9923   fileRoot->setFileDef(fd);
9924   return fileRoot;
9925 }
9926 
9927 //! parse the list of input files
parseFilesMultiThreading(const std::shared_ptr<Entry> & root)9928 static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
9929 {
9930 #if USE_LIBCLANG
9931   if (Doxygen::clangAssistedParsing)
9932   {
9933     StringUnorderedSet processedFiles;
9934 
9935     // create a dictionary with files to process
9936     StringUnorderedSet filesToProcess;
9937     for (const auto &s : g_inputFiles)
9938     {
9939       filesToProcess.insert(s);
9940     }
9941 
9942     std::mutex processedFilesLock;
9943     // process source files (and their include dependencies)
9944     std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9945     if (numThreads==0)
9946     {
9947       numThreads = std::thread::hardware_concurrency();
9948     }
9949     msg("Processing input using %zu threads.\n",numThreads);
9950     ThreadPool threadPool(numThreads);
9951     using FutureType = std::vector< std::shared_ptr<Entry> >;
9952     std::vector< std::future< FutureType > > results;
9953     for (const auto &s : g_inputFiles)
9954     {
9955       bool ambig;
9956       FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
9957       ASSERT(fd!=0);
9958       if (fd->isSource() && !fd->isReference()) // this is a source file
9959       {
9960         // lambda representing the work to executed by a thread
9961         auto processFile = [s,&filesToProcess,&processedFilesLock,&processedFiles]() {
9962           bool ambig_l;
9963           std::vector< std::shared_ptr<Entry> > roots;
9964           FileDef *fd_l = findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig_l);
9965           auto clangParser = ClangParser::instance()->createTUParser(fd_l);
9966           auto parser = getParserForFile(s.c_str());
9967           auto fileRoot { parseFile(*parser.get(),fd_l,s.c_str(),clangParser.get(),true) };
9968           roots.push_back(fileRoot);
9969 
9970           // Now process any include files in the same translation unit
9971           // first. When libclang is used this is much more efficient.
9972           for (auto incFile : clangParser->filesInSameTU())
9973           {
9974             if (filesToProcess.find(incFile)!=filesToProcess.end())
9975             {
9976               bool needsToBeProcessed;
9977               {
9978                 std::lock_guard<std::mutex> lock(processedFilesLock);
9979                 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
9980                 if (needsToBeProcessed) processedFiles.insert(incFile);
9981               }
9982               if (incFile!=s && needsToBeProcessed)
9983               {
9984                 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig_l);
9985                 if (ifd && !ifd->isReference())
9986                 {
9987                   //printf("  Processing %s in same translation unit as %s\n",incFile,s->c_str());
9988                   fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
9989                   roots.push_back(fileRoot);
9990                 }
9991               }
9992             }
9993           }
9994           return roots;
9995         };
9996         // dispatch the work and collect the future results
9997         results.emplace_back(threadPool.queue(processFile));
9998       }
9999     }
10000     // synchronise with the Entry result lists produced and add them to the root
10001     for (auto &f : results)
10002     {
10003       auto l = f.get();
10004       for (auto &e : l)
10005       {
10006         root->moveToSubEntryAndKeep(e);
10007       }
10008     }
10009     // process remaining files
10010     results.clear();
10011     for (const auto &s : g_inputFiles)
10012     {
10013       if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10014       {
10015         // lambda representing the work to executed by a thread
10016         auto processFile = [s]() {
10017           bool ambig;
10018           std::vector< std::shared_ptr<Entry> > roots;
10019           FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10020           auto clangParser = ClangParser::instance()->createTUParser(fd);
10021           auto parser { getParserForFile(s.c_str()) };
10022           auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10023           roots.push_back(fileRoot);
10024           return roots;
10025         };
10026         // dispatch the work and collect the future results
10027         results.emplace_back(threadPool.queue(processFile));
10028       }
10029     }
10030     // synchronise with the Entry result lists produced and add them to the root
10031     for (auto &f : results)
10032     {
10033       auto l = f.get();
10034       for (auto &e : l)
10035       {
10036         root->moveToSubEntryAndKeep(e);
10037       }
10038     }
10039   }
10040   else // normal processing
10041 #endif
10042   {
10043     std::size_t numThreads = std::thread::hardware_concurrency();
10044     msg("Processing input using %zu threads.\n",numThreads);
10045     ThreadPool threadPool(numThreads);
10046     using FutureType = std::shared_ptr<Entry>;
10047     std::vector< std::future< FutureType > > results;
10048     for (const auto &s : g_inputFiles)
10049     {
10050       // lambda representing the work to executed by a thread
10051       auto processFile = [s]() {
10052         bool ambig;
10053         FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10054         auto parser = getParserForFile(s.c_str());
10055         auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10056         return fileRoot;
10057       };
10058       // dispatch the work and collect the future results
10059       results.emplace_back(threadPool.queue(processFile));
10060     }
10061     // synchronise with the Entry results produced and add them to the root
10062     for (auto &f : results)
10063     {
10064       root->moveToSubEntryAndKeep(f.get());
10065     }
10066   }
10067 }
10068 
10069 //! parse the list of input files
parseFilesSingleThreading(const std::shared_ptr<Entry> & root)10070 static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10071 {
10072 #if USE_LIBCLANG
10073   if (Doxygen::clangAssistedParsing)
10074   {
10075     StringUnorderedSet processedFiles;
10076 
10077     // create a dictionary with files to process
10078     StringUnorderedSet filesToProcess;
10079     for (const auto &s : g_inputFiles)
10080     {
10081       filesToProcess.insert(s);
10082     }
10083 
10084     // process source files (and their include dependencies)
10085     for (const auto &s : g_inputFiles)
10086     {
10087       bool ambig;
10088       FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10089       ASSERT(fd!=0);
10090       if (fd->isSource() && !fd->isReference()) // this is a source file
10091       {
10092         auto clangParser = ClangParser::instance()->createTUParser(fd);
10093         auto parser { getParserForFile(s.c_str()) };
10094         auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10095         root->moveToSubEntryAndKeep(fileRoot);
10096         processedFiles.insert(s);
10097 
10098         // Now process any include files in the same translation unit
10099         // first. When libclang is used this is much more efficient.
10100         for (auto incFile : clangParser->filesInSameTU())
10101         {
10102           //printf("    file %s\n",incFile.c_str());
10103           if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10104               processedFiles.find(incFile)==processedFiles.end())   // and is not processed already
10105           {
10106             FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
10107             if (ifd && !ifd->isReference())
10108             {
10109               //printf("  Processing %s in same translation unit as %s\n",incFile.c_str(),s.c_str());
10110               fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10111               root->moveToSubEntryAndKeep(fileRoot);
10112               processedFiles.insert(incFile);
10113             }
10114           }
10115         }
10116       }
10117     }
10118     // process remaining files
10119     for (const auto &s : g_inputFiles)
10120     {
10121       if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10122       {
10123         bool ambig;
10124         FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10125         auto clangParser = ClangParser::instance()->createTUParser(fd);
10126         auto parser { getParserForFile(s.c_str()) };
10127         auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10128         root->moveToSubEntryAndKeep(fileRoot);
10129         processedFiles.insert(s);
10130       }
10131     }
10132   }
10133   else // normal processing
10134 #endif
10135   {
10136     for (const auto &s : g_inputFiles)
10137     {
10138       bool ambig;
10139       FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10140       ASSERT(fd!=0);
10141       std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10142       std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10143       root->moveToSubEntryAndKeep(fileRoot);
10144     }
10145   }
10146 }
10147 
10148 // resolves a path that may include symlinks, if a recursive symlink is
10149 // found an empty string is returned.
resolveSymlink(const std::string & path)10150 static std::string resolveSymlink(const std::string &path)
10151 {
10152   int sepPos=0;
10153   int oldPos=0;
10154   StringSet nonSymlinks;
10155   StringSet known;
10156   QCString result(path);
10157   QCString oldPrefix = "/";
10158   do
10159   {
10160 #ifdef WIN32
10161     // UNC path, skip server and share name
10162     if (sepPos==0 && (result.left(2)=="//" || result.left(2)=="\\\\"))
10163       sepPos = result.find('/',2);
10164     if (sepPos!=-1)
10165       sepPos = result.find('/',sepPos+1);
10166 #else
10167     sepPos = result.find('/',sepPos+1);
10168 #endif
10169     QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10170     if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10171     {
10172       FileInfo fi(prefix.str());
10173       if (fi.isSymLink())
10174       {
10175         QCString target = fi.readLink();
10176         bool isRelative = FileInfo(target.str()).isRelative();
10177         if (isRelative)
10178         {
10179           target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10180         }
10181         if (sepPos!=-1)
10182         {
10183           if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10184           {
10185             target+='/';
10186           }
10187           target+=result.mid(sepPos);
10188         }
10189         result = Dir::cleanDirPath(target.str());
10190         sepPos = 0;
10191         if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10192         known.insert(result.str());
10193         if (isRelative)
10194         {
10195           sepPos = oldPos;
10196         }
10197         else // link to absolute path
10198         {
10199           sepPos = 0;
10200           oldPrefix = "/";
10201         }
10202       }
10203       else
10204       {
10205         nonSymlinks.insert(prefix.str());
10206         oldPrefix = prefix;
10207       }
10208       oldPos = sepPos;
10209     }
10210   }
10211   while (sepPos!=-1);
10212   return Dir::cleanDirPath(result.str());
10213 }
10214 
10215 static StringUnorderedSet g_pathsVisited(1009);
10216 
10217 //----------------------------------------------------------------------------
10218 // Read all files matching at least one pattern in 'patList' in the
10219 // directory represented by 'fi'.
10220 // The directory is read iff the recursiveFlag is set.
10221 // The contents of all files is append to the input string
10222 
readDir(FileInfo * fi,FileNameLinkedMap * fnMap,StringUnorderedSet * exclSet,const StringVector * patList,const StringVector * exclPatList,StringVector * resultList,StringUnorderedSet * resultSet,bool errorIfNotExist,bool recursive,StringUnorderedSet * killSet,StringSet * paths)10223 static void readDir(FileInfo *fi,
10224             FileNameLinkedMap *fnMap,
10225             StringUnorderedSet *exclSet,
10226             const StringVector *patList,
10227             const StringVector *exclPatList,
10228             StringVector *resultList,
10229             StringUnorderedSet *resultSet,
10230             bool errorIfNotExist,
10231             bool recursive,
10232             StringUnorderedSet *killSet,
10233             StringSet *paths
10234            )
10235 {
10236   std::string dirName = fi->absFilePath();
10237   if (paths && !dirName.empty())
10238   {
10239     paths->insert(dirName);
10240   }
10241   //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
10242   if (fi->isSymLink())
10243   {
10244     dirName = resolveSymlink(dirName);
10245     if (dirName.empty())
10246     {
10247       //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
10248       return;  // recursive symlink
10249     }
10250   }
10251 
10252   if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
10253   {
10254     //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
10255     return; // already visited path
10256   }
10257   g_pathsVisited.insert(dirName);
10258 
10259   Dir dir(dirName);
10260   msg("Searching for files in directory %s\n", qPrint(fi->absFilePath()));
10261   //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
10262 
10263   StringVector dirResultList;
10264 
10265   for (const auto &dirEntry : dir.iterator())
10266   {
10267     FileInfo cfi(dirEntry.path());
10268     if (exclSet==0 || exclSet->find(cfi.absFilePath())==exclSet->end())
10269     { // file should not be excluded
10270       //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
10271       if (!cfi.exists() || !cfi.isReadable())
10272       {
10273         if (errorIfNotExist)
10274         {
10275           warn_uncond("source '%s' is not a readable file or directory... skipping.\n",cfi.absFilePath().c_str());
10276         }
10277       }
10278       else if (cfi.isFile() &&
10279           (!Config_getBool(EXCLUDE_SYMLINKS) || !cfi.isSymLink()) &&
10280           (patList==0 || patternMatch(cfi,*patList)) &&
10281           (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10282           (killSet==0 || killSet->find(cfi.absFilePath())==killSet->end())
10283           )
10284       {
10285         std::string name=cfi.fileName();
10286         std::string path=cfi.dirPath()+"/";
10287         std::string fullName=path+name;
10288         if (fnMap)
10289         {
10290           std::unique_ptr<FileDef> fd { createFileDef(QCString(path),QCString(name)) };
10291           FileName *fn=0;
10292           if (!name.empty())
10293           {
10294             fn = fnMap->add(QCString(name),QCString(fullName));
10295             fn->push_back(std::move(fd));
10296           }
10297         }
10298         dirResultList.push_back(fullName);
10299         if (resultSet) resultSet->insert(fullName);
10300         if (killSet) killSet->insert(fullName);
10301       }
10302       else if (recursive &&
10303           (!Config_getBool(EXCLUDE_SYMLINKS) || !cfi.isSymLink()) &&
10304           cfi.isDir() &&
10305           (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10306           cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
10307       {
10308         FileInfo acfi(cfi.absFilePath());
10309         readDir(&acfi,fnMap,exclSet,
10310             patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
10311             recursive,killSet,paths);
10312       }
10313     }
10314   }
10315   if (resultList && !dirResultList.empty())
10316   {
10317     // sort the resulting list to make the order platform independent.
10318     std::sort(dirResultList.begin(),
10319               dirResultList.end(),
10320               [](const auto &f1,const auto &f2) { return qstricmp(f1.c_str(),f2.c_str())<0; });
10321 
10322     // append the sorted results to resultList
10323     resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
10324   }
10325 }
10326 
10327 
10328 //----------------------------------------------------------------------------
10329 // read a file or all files in a directory and append their contents to the
10330 // input string. The names of the files are appended to the 'fiList' list.
10331 
readFileOrDirectory(const QCString & s,FileNameLinkedMap * fnMap,StringUnorderedSet * exclSet,const StringVector * patList,const StringVector * exclPatList,StringVector * resultList,StringUnorderedSet * resultSet,bool recursive,bool errorIfNotExist,StringUnorderedSet * killSet,StringSet * paths)10332 void readFileOrDirectory(const QCString &s,
10333                         FileNameLinkedMap *fnMap,
10334                         StringUnorderedSet *exclSet,
10335                         const StringVector *patList,
10336                         const StringVector *exclPatList,
10337                         StringVector *resultList,
10338                         StringUnorderedSet *resultSet,
10339                         bool recursive,
10340                         bool errorIfNotExist,
10341                         StringUnorderedSet *killSet,
10342                         StringSet *paths
10343                        )
10344 {
10345   //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
10346   // strip trailing slashes
10347   if (s.isEmpty()) return;
10348 
10349   g_pathsVisited.clear();
10350 
10351   FileInfo fi(s.str());
10352   //printf("readFileOrDirectory(%s)\n",s);
10353   {
10354     if (exclSet==0 || exclSet->find(fi.absFilePath())==exclSet->end())
10355     {
10356       if (!fi.exists() || !fi.isReadable())
10357       {
10358         if (errorIfNotExist)
10359         {
10360           warn_uncond("source '%s' is not a readable file or directory... skipping.\n",qPrint(s));
10361         }
10362       }
10363       else if (!Config_getBool(EXCLUDE_SYMLINKS) || !fi.isSymLink())
10364       {
10365         if (fi.isFile())
10366         {
10367           std::string dirPath = fi.dirPath(true);
10368           std::string filePath = fi.absFilePath();
10369           if (paths && !dirPath.empty())
10370           {
10371             paths->insert(dirPath);
10372           }
10373           //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
10374           if (killSet==0 || killSet->find(filePath)==killSet->end())
10375           {
10376             std::string name=fi.fileName();
10377             if (fnMap)
10378             {
10379               std::unique_ptr<FileDef> fd { createFileDef(QCString(dirPath+"/"),QCString(name)) };
10380               if (!name.empty())
10381               {
10382                 FileName *fn = fnMap->add(QCString(name),QCString(filePath));
10383                 fn->push_back(std::move(fd));
10384               }
10385             }
10386             if (resultList || resultSet)
10387             {
10388               if (resultList) resultList->push_back(filePath);
10389               if (resultSet) resultSet->insert(filePath);
10390             }
10391 
10392             if (killSet) killSet->insert(fi.absFilePath());
10393           }
10394         }
10395         else if (fi.isDir()) // readable dir
10396         {
10397           readDir(&fi,fnMap,exclSet,patList,
10398               exclPatList,resultList,resultSet,errorIfNotExist,
10399               recursive,killSet,paths);
10400         }
10401       }
10402     }
10403   }
10404 }
10405 
10406 //----------------------------------------------------------------------------
10407 
expandAliases()10408 static void expandAliases()
10409 {
10410   for (auto &kv : Doxygen::aliasMap)
10411   {
10412     kv.second = expandAlias(kv.first,kv.second);
10413   }
10414 }
10415 
10416 //----------------------------------------------------------------------------
10417 
escapeAliases()10418 static void escapeAliases()
10419 {
10420   for (auto &kv : Doxygen::aliasMap)
10421   {
10422     QCString value(kv.second);
10423     QCString newValue;
10424     int in,p=0;
10425     // for each \n in the alias command value
10426     while ((in=value.find("\\n",p))!=-1)
10427     {
10428       newValue+=value.mid(p,in-p);
10429       // expand \n's except if \n is part of a built-in command.
10430       if (value.mid(in,5)!="\\note" &&
10431           value.mid(in,5)!="\\noop" &&
10432           value.mid(in,5)!="\\name" &&
10433           value.mid(in,10)!="\\namespace" &&
10434           value.mid(in,14)!="\\nosubgrouping"
10435          )
10436       {
10437         newValue+="\\ilinebr ";
10438       }
10439       else
10440       {
10441         newValue+="\\n";
10442       }
10443       p=in+2;
10444     }
10445     newValue+=value.mid(p,value.length()-p);
10446     p = 0;
10447     newValue = "";
10448     while ((in=value.find("^^",p))!=-1)
10449     {
10450       newValue+=value.mid(p,in-p);
10451       newValue+="\\ilinebr ";
10452       p=in+2;
10453     }
10454     newValue+=value.mid(p,value.length()-p);
10455     kv.second=newValue.str();
10456     //printf("Alias %s has value %s\n",kv.first.c_str(),qPrint(newValue));
10457   }
10458 }
10459 
10460 //----------------------------------------------------------------------------
10461 
readAliases()10462 void readAliases()
10463 {
10464   // add aliases to a dictionary
10465   const StringVector &aliasList = Config_getList(ALIASES);
10466   for (const auto &al : aliasList)
10467   {
10468     QCString alias(al);
10469     int i=alias.find('=');
10470     if (i>0)
10471     {
10472       QCString name=alias.left(i).stripWhiteSpace();
10473       QCString value=alias.right(alias.length()-i-1);
10474       //printf("Alias: found name='%s' value='%s'\n",qPrint(name),qPrint(value));
10475       if (!name.isEmpty())
10476       {
10477         auto it = Doxygen::aliasMap.find(name.str());
10478         if (it==Doxygen::aliasMap.end()) // insert new alias
10479         {
10480           Doxygen::aliasMap.insert(std::make_pair(name.str(),value.str()));
10481         }
10482         else // overwrite previous alias
10483         {
10484           it->second=value.str();
10485         }
10486       }
10487     }
10488   }
10489   expandAliases();
10490   escapeAliases();
10491 }
10492 
10493 //----------------------------------------------------------------------------
10494 
dumpSymbol(TextStream & t,Definition * d)10495 static void dumpSymbol(TextStream &t,Definition *d)
10496 {
10497   QCString anchor;
10498   if (d->definitionType()==Definition::TypeMember)
10499   {
10500     MemberDef *md = toMemberDef(d);
10501     anchor=":"+md->anchor();
10502   }
10503   QCString scope;
10504   if (d->getOuterScope() && d->getOuterScope()!=Doxygen::globalScope)
10505   {
10506     scope = addHtmlExtensionIfMissing(d->getOuterScope()->getOutputFileBase());
10507   }
10508   t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
10509     << addHtmlExtensionIfMissing(d->getOutputFileBase())+anchor << "','"
10510     << scope << "','"
10511     << d->name() << "','"
10512     << d->getDefFileName() << "','"
10513     << d->getDefLine()
10514     << "');\n";
10515 }
10516 
dumpSymbolMap()10517 static void dumpSymbolMap()
10518 {
10519   std::ofstream f("symbols.sql",std::ofstream::out | std::ofstream::binary);
10520   if (f.is_open())
10521   {
10522     TextStream t(&f);
10523     for (const auto &kv : *Doxygen::symbolMap)
10524     {
10525       dumpSymbol(t,kv.second);
10526     }
10527   }
10528 }
10529 
10530 // print developer options of doxygen
devUsage()10531 static void devUsage()
10532 {
10533   msg("Developer parameters:\n");
10534   msg("  -m          dump symbol map\n");
10535   msg("  -b          making messages output unbuffered\n");
10536   msg("  -T          activates output generation via Django like template\n");
10537   msg("  -d <level>  enable a debug level, such as (multiple invocations of -d are possible):\n");
10538   Debug::printFlags();
10539 }
10540 
10541 
10542 //----------------------------------------------------------------------------
10543 // print the version of doxygen
10544 
version(const bool extended)10545 static void version(const bool extended)
10546 {
10547   QCString versionString = getFullVersion();
10548   msg("%s\n",qPrint(versionString));
10549   if (extended)
10550   {
10551     QCString extVers;
10552 #if USE_SQLITE3
10553     if (!extVers.isEmpty()) extVers+= ", ";
10554     extVers += "sqlite3 ";
10555     extVers += sqlite3_libversion();
10556 #endif
10557 #if USE_LIBCLANG
10558     if (!extVers.isEmpty()) extVers+= ", ";
10559     extVers += "clang support ";
10560     extVers += CLANG_VERSION_STRING;
10561 #endif
10562     if (!extVers.isEmpty())
10563     {
10564       int lastComma = extVers.findRev(',');
10565       if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
10566       msg("    with %s.\n",qPrint(extVers));
10567     }
10568   }
10569 }
10570 
10571 //----------------------------------------------------------------------------
10572 // print the usage of doxygen
10573 
usage(const QCString & name,const QCString & versionString)10574 static void usage(const QCString &name,const QCString &versionString)
10575 {
10576   msg("Doxygen version %s\nCopyright Dimitri van Heesch 1997-2021\n\n",qPrint(versionString));
10577   msg("You can use doxygen in a number of ways:\n\n");
10578   msg("1) Use doxygen to generate a template configuration file:\n");
10579   msg("    %s [-s] -g [configName]\n\n",qPrint(name));
10580   msg("2) Use doxygen to update an old configuration file:\n");
10581   msg("    %s [-s] -u [configName]\n\n",qPrint(name));
10582   msg("3) Use doxygen to generate documentation using an existing ");
10583   msg("configuration file:\n");
10584   msg("    %s [configName]\n\n",qPrint(name));
10585   msg("4) Use doxygen to generate a template file controlling the layout of the\n");
10586   msg("   generated documentation:\n");
10587   msg("    %s -l [layoutFileName]\n\n",qPrint(name));
10588   msg("    In case layoutFileName is omitted layoutFileName.xml will be used as filename.\n");
10589   msg("    If - is used for layoutFileName doxygen will write to standard output.\n\n");
10590   msg("5) Use doxygen to generate a template style sheet file for RTF, HTML or Latex.\n");
10591   msg("    RTF:        %s -w rtf styleSheetFile\n",qPrint(name));
10592   msg("    HTML:       %s -w html headerFile footerFile styleSheetFile [configFile]\n",qPrint(name));
10593   msg("    LaTeX:      %s -w latex headerFile footerFile styleSheetFile [configFile]\n\n",qPrint(name));
10594   msg("6) Use doxygen to generate a rtf extensions file\n");
10595   msg("    %s -e rtf extensionsFile\n\n",qPrint(name));
10596   msg("    If - is used for extensionsFile doxygen will write to standard output.\n\n");
10597   msg("7) Use doxygen to compare the used configuration file with the template configuration file\n");
10598   msg("    %s -x [configFile]\n\n",qPrint(name));
10599   msg("8) Use doxygen to show a list of built-in emojis.\n");
10600   msg("    %s -f emoji outputFileName\n\n",qPrint(name));
10601   msg("    If - is used for outputFileName doxygen will write to standard output.\n\n");
10602   msg("If -s is specified the comments of the configuration items in the config file will be omitted.\n");
10603   msg("If configName is omitted 'Doxyfile' will be used as a default.\n");
10604   msg("If - is used for configFile doxygen will write / read the configuration to /from standard output / input.\n\n");
10605   msg("If -q is used for a doxygen documentation run, doxygen will see this as if QUIET=YES has been set.\n\n");
10606   msg("-v print version string, -V print extended version information\n");
10607 }
10608 
10609 //----------------------------------------------------------------------------
10610 // read the argument of option 'c' from the comment argument list and
10611 // update the option index 'optInd'.
10612 
getArg(int argc,char ** argv,int & optInd)10613 static const char *getArg(int argc,char **argv,int &optInd)
10614 {
10615   char *s=0;
10616   if (qstrlen(&argv[optInd][2])>0)
10617     s=&argv[optInd][2];
10618   else if (optInd+1<argc && argv[optInd+1][0]!='-')
10619     s=argv[++optInd];
10620   return s;
10621 }
10622 
10623 //----------------------------------------------------------------------------
10624 
10625 /** @brief /dev/null outline parser */
10626 class NullOutlineParser : public OutlineParserInterface
10627 {
10628   public:
parseInput(const QCString & file,const char * buf,const std::shared_ptr<Entry> &,ClangTUParser *)10629     void parseInput(const QCString &file, const char *buf,const std::shared_ptr<Entry> &, ClangTUParser*) {}
needsPreprocessing(const QCString &) const10630     bool needsPreprocessing(const QCString &) const { return FALSE; }
parsePrototype(const QCString &)10631     void parsePrototype(const QCString &) {}
10632 };
10633 
10634 
make_parser_factory()10635 template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
10636 {
10637   return []() { return std::make_unique<T>(); };
10638 }
10639 
initDoxygen()10640 void initDoxygen()
10641 {
10642   initResources();
10643   QCString lang = Portable::getenv("LC_ALL");
10644   if (!lang.isEmpty()) Portable::setenv("LANG",lang);
10645   std::setlocale(LC_ALL,"");
10646   std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
10647   std::setlocale(LC_NUMERIC,"C");
10648 
10649   Doxygen::symbolMap = new SymbolMap<Definition>;
10650 
10651   Portable::correct_path();
10652 
10653   Debug::startTimer();
10654   Doxygen::parserManager = new ParserManager(            make_parser_factory<NullOutlineParser>(),
10655                                                          make_parser_factory<FileCodeParser>());
10656   Doxygen::parserManager->registerParser("c",            make_parser_factory<COutlineParser>(),
10657                                                          make_parser_factory<CCodeParser>());
10658   Doxygen::parserManager->registerParser("python",       make_parser_factory<PythonOutlineParser>(),
10659                                                          make_parser_factory<PythonCodeParser>());
10660   Doxygen::parserManager->registerParser("fortran",      make_parser_factory<FortranOutlineParser>(),
10661                                                          make_parser_factory<FortranCodeParser>());
10662   Doxygen::parserManager->registerParser("fortranfree",  make_parser_factory<FortranOutlineParserFree>(),
10663                                                          make_parser_factory<FortranCodeParserFree>());
10664   Doxygen::parserManager->registerParser("fortranfixed", make_parser_factory<FortranOutlineParserFixed>(),
10665                                                          make_parser_factory<FortranCodeParserFixed>());
10666   Doxygen::parserManager->registerParser("vhdl",         make_parser_factory<VHDLOutlineParser>(),
10667                                                          make_parser_factory<VHDLCodeParser>());
10668   Doxygen::parserManager->registerParser("xml",          make_parser_factory<NullOutlineParser>(),
10669                                                          make_parser_factory<XMLCodeParser>());
10670   Doxygen::parserManager->registerParser("sql",          make_parser_factory<NullOutlineParser>(),
10671                                                          make_parser_factory<SQLCodeParser>());
10672   Doxygen::parserManager->registerParser("md",           make_parser_factory<MarkdownOutlineParser>(),
10673                                                          make_parser_factory<FileCodeParser>());
10674   Doxygen::parserManager->registerParser("lex",          make_parser_factory<LexOutlineParser>(),
10675                                                          make_parser_factory<LexCodeParser>());
10676 
10677   // register any additional parsers here...
10678 
10679   initDefaultExtensionMapping();
10680   initClassMemberIndices();
10681   initNamespaceMemberIndices();
10682   initFileMemberIndices();
10683 
10684 #ifdef USE_LIBCLANG
10685   Doxygen::clangUsrMap   = new ClangUsrMap;
10686 #endif
10687   Doxygen::memberNameLinkedMap = new MemberNameLinkedMap;
10688   Doxygen::functionNameLinkedMap = new MemberNameLinkedMap;
10689   Doxygen::groupLinkedMap = new GroupLinkedMap;
10690   Doxygen::namespaceLinkedMap = new NamespaceLinkedMap;
10691   Doxygen::classLinkedMap = new ClassLinkedMap;
10692   Doxygen::hiddenClassLinkedMap = new ClassLinkedMap;
10693   Doxygen::conceptLinkedMap = new ConceptLinkedMap;
10694   Doxygen::dirLinkedMap = new DirLinkedMap;
10695   Doxygen::pageLinkedMap = new PageLinkedMap;          // all doc pages
10696   Doxygen::exampleLinkedMap = new PageLinkedMap;       // all examples
10697   //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
10698   Doxygen::indexList = new IndexList;
10699 
10700   // initialisation of these globals depends on
10701   // configuration switches so we need to postpone these
10702   Doxygen::globalScope     = 0;
10703   Doxygen::inputNameLinkedMap   = 0;
10704   Doxygen::includeNameLinkedMap = 0;
10705   Doxygen::exampleNameLinkedMap = 0;
10706   Doxygen::imageNameLinkedMap   = 0;
10707   Doxygen::dotFileNameLinkedMap = 0;
10708   Doxygen::mscFileNameLinkedMap = 0;
10709   Doxygen::diaFileNameLinkedMap = 0;
10710 
10711   /**************************************************************************
10712    *            Initialize some global constants
10713    **************************************************************************/
10714 
10715   g_compoundKeywords.insert("template class");
10716   g_compoundKeywords.insert("template struct");
10717   g_compoundKeywords.insert("class");
10718   g_compoundKeywords.insert("struct");
10719   g_compoundKeywords.insert("union");
10720   g_compoundKeywords.insert("interface");
10721   g_compoundKeywords.insert("exception");
10722 }
10723 
cleanUpDoxygen()10724 void cleanUpDoxygen()
10725 {
10726   FormulaManager::instance().clear();
10727   SectionManager::instance().clear();
10728 
10729   delete Doxygen::indexList;
10730   delete Doxygen::inputNameLinkedMap;
10731   delete Doxygen::includeNameLinkedMap;
10732   delete Doxygen::exampleNameLinkedMap;
10733   delete Doxygen::imageNameLinkedMap;
10734   delete Doxygen::dotFileNameLinkedMap;
10735   delete Doxygen::mscFileNameLinkedMap;
10736   delete Doxygen::diaFileNameLinkedMap;
10737   Doxygen::mainPage.reset();
10738   delete Doxygen::pageLinkedMap;
10739   delete Doxygen::exampleLinkedMap;
10740   delete Doxygen::globalScope;
10741   delete Doxygen::parserManager;
10742   delete theTranslator;
10743   delete g_outputList;
10744   Mappers::freeMappers();
10745 
10746   delete Doxygen::memberNameLinkedMap;
10747   delete Doxygen::functionNameLinkedMap;
10748   delete Doxygen::groupLinkedMap;
10749   delete Doxygen::namespaceLinkedMap;
10750   delete Doxygen::dirLinkedMap;
10751   delete Doxygen::symbolMap;
10752 
10753   DotManager::deleteInstance();
10754 }
10755 
computeIdealCacheParam(size_t v)10756 static int computeIdealCacheParam(size_t v)
10757 {
10758   //printf("computeIdealCacheParam(v=%u)\n",v);
10759 
10760   int r=0;
10761   while (v!=0) v>>=1,r++;
10762   // r = log2(v)
10763 
10764   // convert to a valid cache size value
10765   return std::max(0,std::min(r-16,9));
10766 }
10767 
readConfiguration(int argc,char ** argv)10768 void readConfiguration(int argc, char **argv)
10769 {
10770   QCString versionString = getFullVersion();
10771 
10772   /**************************************************************************
10773    *             Handle arguments                                           *
10774    **************************************************************************/
10775 
10776   int optInd=1;
10777   QCString configName;
10778   QCString layoutName;
10779   QCString debugLabel;
10780   QCString formatName;
10781   QCString listName;
10782   bool genConfig=FALSE;
10783   bool shortList=FALSE;
10784   bool diffList=FALSE;
10785   bool updateConfig=FALSE;
10786   int retVal;
10787   bool quiet = false;
10788   while (optInd<argc && argv[optInd][0]=='-' &&
10789                (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
10790                 argv[optInd][1]=='-')
10791         )
10792   {
10793     switch(argv[optInd][1])
10794     {
10795       case 'g':
10796         genConfig=TRUE;
10797         break;
10798       case 'l':
10799         if (optInd+1>=argc)
10800         {
10801           layoutName="DoxygenLayout.xml";
10802         }
10803         else
10804         {
10805           layoutName=argv[optInd+1];
10806         }
10807         writeDefaultLayoutFile(layoutName);
10808         cleanUpDoxygen();
10809         exit(0);
10810         break;
10811       case 'd':
10812         debugLabel=getArg(argc,argv,optInd);
10813         if (debugLabel.isEmpty())
10814         {
10815           devUsage();
10816           cleanUpDoxygen();
10817           exit(0);
10818         }
10819         retVal = Debug::setFlag(debugLabel);
10820         if (!retVal)
10821         {
10822           err("option \"-d\" has unknown debug specifier: \"%s\".\n",qPrint(debugLabel));
10823           devUsage();
10824           cleanUpDoxygen();
10825           exit(1);
10826         }
10827         break;
10828       case 'x':
10829         diffList=TRUE;
10830         break;
10831       case 's':
10832         shortList=TRUE;
10833         break;
10834       case 'u':
10835         updateConfig=TRUE;
10836         break;
10837       case 'e':
10838         formatName=getArg(argc,argv,optInd);
10839         if (formatName.isEmpty())
10840         {
10841           err("option \"-e\" is missing format specifier rtf.\n");
10842           cleanUpDoxygen();
10843           exit(1);
10844         }
10845         if (qstricmp(formatName.data(),"rtf")==0)
10846         {
10847           if (optInd+1>=argc)
10848           {
10849             err("option \"-e rtf\" is missing an extensions file name\n");
10850             cleanUpDoxygen();
10851             exit(1);
10852           }
10853           std::ofstream f;
10854           if (openOutputFile(argv[optInd+1],f))
10855           {
10856             TextStream t(&f);
10857             RTFGenerator::writeExtensionsFile(t);
10858           }
10859           cleanUpDoxygen();
10860           exit(0);
10861         }
10862         err("option \"-e\" has invalid format specifier.\n");
10863         cleanUpDoxygen();
10864         exit(1);
10865         break;
10866       case 'f':
10867         listName=getArg(argc,argv,optInd);
10868         if (listName.isEmpty())
10869         {
10870           err("option \"-f\" is missing list specifier.\n");
10871           cleanUpDoxygen();
10872           exit(1);
10873         }
10874         if (qstricmp(listName.data(),"emoji")==0)
10875         {
10876           if (optInd+1>=argc)
10877           {
10878             err("option \"-f emoji\" is missing an output file name\n");
10879             cleanUpDoxygen();
10880             exit(1);
10881           }
10882           std::ofstream f;
10883           if (openOutputFile(argv[optInd+1],f))
10884           {
10885             TextStream t(&f);
10886             EmojiEntityMapper::instance()->writeEmojiFile(t);
10887           }
10888           cleanUpDoxygen();
10889           exit(0);
10890         }
10891         err("option \"-f\" has invalid list specifier.\n");
10892         cleanUpDoxygen();
10893         exit(1);
10894         break;
10895       case 'w':
10896         formatName=getArg(argc,argv,optInd);
10897         if (formatName.isEmpty())
10898         {
10899           err("option \"-w\" is missing format specifier rtf, html or latex\n");
10900           cleanUpDoxygen();
10901           exit(1);
10902         }
10903         if (qstricmp(formatName.data(),"rtf")==0)
10904         {
10905           if (optInd+1>=argc)
10906           {
10907             err("option \"-w rtf\" is missing a style sheet file name\n");
10908             cleanUpDoxygen();
10909             exit(1);
10910           }
10911           std::ofstream f;
10912           if (openOutputFile(argv[optInd+1],f))
10913           {
10914             TextStream t(&f);
10915             RTFGenerator::writeStyleSheetFile(t);
10916           }
10917           cleanUpDoxygen();
10918           exit(1);
10919         }
10920         else if (qstricmp(formatName.data(),"html")==0)
10921         {
10922           Config::init();
10923           if (optInd+4<argc || FileInfo("Doxyfile").exists())
10924              // explicit config file mentioned or default found on disk
10925           {
10926             QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
10927             if (!Config::parse(df)) // parse the config file
10928             {
10929               err("error opening or reading configuration file %s!\n",argv[optInd+4]);
10930               cleanUpDoxygen();
10931               exit(1);
10932             }
10933           }
10934           if (optInd+3>=argc)
10935           {
10936             err("option \"-w html\" does not have enough arguments\n");
10937             cleanUpDoxygen();
10938             exit(1);
10939           }
10940           Config::postProcess(TRUE);
10941           Config::updateObsolete();
10942           Config::checkAndCorrect(Config_getBool(QUIET), false);
10943 
10944           setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
10945 
10946           std::ofstream f;
10947           if (openOutputFile(argv[optInd+1],f))
10948           {
10949             TextStream t(&f);
10950             HtmlGenerator::writeHeaderFile(t, argv[optInd+3]);
10951           }
10952           f.close();
10953           if (openOutputFile(argv[optInd+2],f))
10954           {
10955             TextStream t(&f);
10956             HtmlGenerator::writeFooterFile(t);
10957           }
10958           f.close();
10959           if (openOutputFile(argv[optInd+3],f))
10960           {
10961             TextStream t(&f);
10962             HtmlGenerator::writeStyleSheetFile(t);
10963           }
10964           cleanUpDoxygen();
10965           exit(0);
10966         }
10967         else if (qstricmp(formatName.data(),"latex")==0)
10968         {
10969           Config::init();
10970           if (optInd+4<argc || FileInfo("Doxyfile").exists())
10971           {
10972             QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
10973             if (!Config::parse(df))
10974             {
10975               err("error opening or reading configuration file %s!\n",argv[optInd+4]);
10976               cleanUpDoxygen();
10977               exit(1);
10978             }
10979           }
10980           if (optInd+3>=argc)
10981           {
10982             err("option \"-w latex\" does not have enough arguments\n");
10983             cleanUpDoxygen();
10984             exit(1);
10985           }
10986           Config::postProcess(TRUE);
10987           Config::updateObsolete();
10988           Config::checkAndCorrect(Config_getBool(QUIET), false);
10989 
10990           setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
10991 
10992           std::ofstream f;
10993           if (openOutputFile(argv[optInd+1],f))
10994           {
10995             TextStream t(&f);
10996             LatexGenerator::writeHeaderFile(t);
10997           }
10998           f.close();
10999           if (openOutputFile(argv[optInd+2],f))
11000           {
11001             TextStream t(&f);
11002             LatexGenerator::writeFooterFile(t);
11003           }
11004           f.close();
11005           if (openOutputFile(argv[optInd+3],f))
11006           {
11007             TextStream t(&f);
11008             LatexGenerator::writeStyleSheetFile(t);
11009           }
11010           cleanUpDoxygen();
11011           exit(0);
11012         }
11013         else
11014         {
11015           err("Illegal format specifier \"%s\": should be one of rtf, html or latex\n",qPrint(formatName));
11016           cleanUpDoxygen();
11017           exit(1);
11018         }
11019         break;
11020       case 'm':
11021         g_dumpSymbolMap = TRUE;
11022         break;
11023       case 'v':
11024         version(false);
11025         cleanUpDoxygen();
11026         exit(0);
11027         break;
11028       case 'V':
11029         version(true);
11030         cleanUpDoxygen();
11031         exit(0);
11032         break;
11033       case '-':
11034         if (qstrcmp(&argv[optInd][2],"help")==0)
11035         {
11036           usage(argv[0],versionString);
11037           exit(0);
11038         }
11039         else if (qstrcmp(&argv[optInd][2],"version")==0)
11040         {
11041           version(false);
11042           cleanUpDoxygen();
11043           exit(0);
11044         }
11045         else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11046                  (qstrcmp(&argv[optInd][2],"VERSION")==0))
11047         {
11048           version(true);
11049           cleanUpDoxygen();
11050           exit(0);
11051         }
11052         else
11053         {
11054           err("Unknown option \"-%s\"\n",&argv[optInd][1]);
11055           usage(argv[0],versionString);
11056           exit(1);
11057         }
11058         break;
11059       case 'b':
11060         setvbuf(stdout,NULL,_IONBF,0);
11061         break;
11062       case 'q':
11063         quiet = true;
11064         break;
11065       case 'T':
11066         msg("Warning: this option activates output generation via Django like template files. "
11067             "This option is scheduled for doxygen 2.0, is currently incomplete and highly experimental! "
11068             "Only use if you are a doxygen developer\n");
11069         g_useOutputTemplate=TRUE;
11070         break;
11071       case 'h':
11072       case '?':
11073         usage(argv[0],versionString);
11074         exit(0);
11075         break;
11076       default:
11077         err("Unknown option \"-%c\"\n",argv[optInd][1]);
11078         usage(argv[0],versionString);
11079         exit(1);
11080     }
11081     optInd++;
11082   }
11083 
11084   /**************************************************************************
11085    *            Parse or generate the config file                           *
11086    **************************************************************************/
11087 
11088   Config::init();
11089 
11090   FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11091   if (optInd>=argc)
11092   {
11093     if (configFileInfo1.exists())
11094     {
11095       configName="Doxyfile";
11096     }
11097     else if (configFileInfo2.exists())
11098     {
11099       configName="doxyfile";
11100     }
11101     else if (genConfig)
11102     {
11103       configName="Doxyfile";
11104     }
11105     else
11106     {
11107       err("Doxyfile not found and no input file specified!\n");
11108       usage(argv[0],versionString);
11109       exit(1);
11110     }
11111   }
11112   else
11113   {
11114     FileInfo fi(argv[optInd]);
11115     if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11116     {
11117       configName=argv[optInd];
11118     }
11119     else
11120     {
11121       err("configuration file %s not found!\n",argv[optInd]);
11122       usage(argv[0],versionString);
11123       exit(1);
11124     }
11125   }
11126 
11127   if (genConfig && g_useOutputTemplate)
11128   {
11129     generateTemplateFiles("templates");
11130     cleanUpDoxygen();
11131     exit(0);
11132   }
11133 
11134   if (genConfig)
11135   {
11136     generateConfigFile(configName,shortList);
11137     cleanUpDoxygen();
11138     exit(0);
11139   }
11140 
11141   if (!Config::parse(configName,updateConfig))
11142   {
11143     err("could not open or read configuration file %s!\n",qPrint(configName));
11144     cleanUpDoxygen();
11145     exit(1);
11146   }
11147 
11148   if (diffList)
11149   {
11150     Config::updateObsolete();
11151     compareDoxyfile();
11152     cleanUpDoxygen();
11153     exit(0);
11154   }
11155 
11156   if (updateConfig)
11157   {
11158     Config::updateObsolete();
11159     generateConfigFile(configName,shortList,TRUE);
11160     cleanUpDoxygen();
11161     exit(0);
11162   }
11163 
11164   /* Perlmod wants to know the path to the config file.*/
11165   FileInfo configFileInfo(configName.str());
11166   setPerlModDoxyfile(configFileInfo.absFilePath());
11167 
11168   /* handle -q option */
11169   if (quiet) Config_updateBool(QUIET,TRUE);
11170 }
11171 
11172 /** check and resolve config options */
checkConfiguration()11173 void checkConfiguration()
11174 {
11175 
11176   Config::postProcess(FALSE);
11177   Config::updateObsolete();
11178   Config::checkAndCorrect(Config_getBool(QUIET), true);
11179   initWarningFormat();
11180 }
11181 
11182 /** adjust globals that depend on configuration settings. */
adjustConfiguration()11183 void adjustConfiguration()
11184 {
11185   Doxygen::globalScope = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11186   Doxygen::inputNameLinkedMap = new FileNameLinkedMap;
11187   Doxygen::includeNameLinkedMap = new FileNameLinkedMap;
11188   Doxygen::exampleNameLinkedMap = new FileNameLinkedMap;
11189   Doxygen::imageNameLinkedMap = new FileNameLinkedMap;
11190   Doxygen::dotFileNameLinkedMap = new FileNameLinkedMap;
11191   Doxygen::mscFileNameLinkedMap = new FileNameLinkedMap;
11192   Doxygen::diaFileNameLinkedMap = new FileNameLinkedMap;
11193 
11194   setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11195 
11196   /* Set the global html file extension. */
11197   Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11198 
11199 
11200   Doxygen::parseSourcesNeeded = Config_getBool(CALL_GRAPH) ||
11201                                 Config_getBool(CALLER_GRAPH) ||
11202                                 Config_getBool(REFERENCES_RELATION) ||
11203                                 Config_getBool(REFERENCED_BY_RELATION);
11204 
11205   /**************************************************************************
11206    *            Add custom extension mappings
11207    **************************************************************************/
11208 
11209   const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11210   for (const auto &mapping : extMaps)
11211   {
11212     QCString mapStr = mapping.c_str();
11213     int i=mapStr.find('=');
11214     if (i==-1)
11215     {
11216       continue;
11217     }
11218     else
11219     {
11220       QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11221       QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11222       if (ext.isEmpty() || language.isEmpty())
11223       {
11224         continue;
11225       }
11226 
11227       if (!updateLanguageMapping(ext,language))
11228       {
11229         err("Failed to map file extension '%s' to unsupported language '%s'.\n"
11230             "Check the EXTENSION_MAPPING setting in the config file.\n",
11231             qPrint(ext),qPrint(language));
11232       }
11233       else
11234       {
11235         msg("Adding custom extension mapping: '%s' will be treated as language '%s'\n",
11236             qPrint(ext),qPrint(language));
11237       }
11238     }
11239   }
11240 
11241   // add predefined macro name to a dictionary
11242   const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11243   for (const auto &s : expandAsDefinedList)
11244   {
11245     Doxygen::expandAsDefinedSet.insert(s.c_str());
11246   }
11247 
11248   // read aliases and store them in a dictionary
11249   readAliases();
11250 
11251   // store number of spaces in a tab into Doxygen::spaces
11252   int tabSize = Config_getInt(TAB_SIZE);
11253   Doxygen::spaces.resize(tabSize+1);
11254   int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' ';
11255   Doxygen::spaces.at(tabSize)='\0';
11256 }
11257 
11258 #ifdef HAS_SIGNALS
stopDoxygen(int)11259 static void stopDoxygen(int)
11260 {
11261   signal(SIGINT,SIG_DFL);   // Re-register signal handler for default action
11262   Dir thisDir;
11263   msg("Cleaning up...\n");
11264   if (!Doxygen::filterDBFileName.isEmpty())
11265   {
11266     thisDir.remove(Doxygen::filterDBFileName.str());
11267   }
11268   killpg(0,SIGINT);
11269   cleanUpDoxygen();
11270   exit(1);
11271 }
11272 #endif
11273 
writeTagFile()11274 static void writeTagFile()
11275 {
11276   QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
11277   if (generateTagFile.isEmpty()) return;
11278 
11279   std::ofstream f(generateTagFile.str(),std::ofstream::out | std::ofstream::binary);
11280   if (!f.is_open())
11281   {
11282     err("cannot open tag file %s for writing\n",
11283         qPrint(generateTagFile)
11284        );
11285     return;
11286   }
11287   TextStream tagFile(&f);
11288   tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
11289   tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
11290   if (strlen(getGitVersion())>0)
11291   {
11292     tagFile << " doxygen_gitid=\"" << getGitVersion() << "\"";
11293   }
11294   tagFile << ">\n";
11295 
11296   // for each file
11297   for (const auto &fn : *Doxygen::inputNameLinkedMap)
11298   {
11299     for (const auto &fd : *fn)
11300     {
11301       if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
11302     }
11303   }
11304   // for each class
11305   for (const auto &cd : *Doxygen::classLinkedMap)
11306   {
11307     ClassDefMutable *cdm = toClassDefMutable(cd.get());
11308     if (cdm && cdm->isLinkableInProject())
11309     {
11310       cdm->writeTagFile(tagFile);
11311     }
11312   }
11313   // for each concept
11314   for (const auto &cd : *Doxygen::conceptLinkedMap)
11315   {
11316     ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
11317     if (cdm && cdm->isLinkableInProject())
11318     {
11319       cdm->writeTagFile(tagFile);
11320     }
11321   }
11322   // for each namespace
11323   for (const auto &nd : *Doxygen::namespaceLinkedMap)
11324   {
11325     NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
11326     if (ndm && nd->isLinkableInProject())
11327     {
11328       ndm->writeTagFile(tagFile);
11329     }
11330   }
11331   // for each group
11332   for (const auto &gd : *Doxygen::groupLinkedMap)
11333   {
11334     if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
11335   }
11336   // for each page
11337   for (const auto &pd : *Doxygen::pageLinkedMap)
11338   {
11339     if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
11340   }
11341   if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
11342 
11343   tagFile << "</tagfile>\n";
11344 }
11345 
exitDoxygen()11346 static void exitDoxygen()
11347 {
11348   if (!g_successfulRun)  // premature exit
11349   {
11350     Dir thisDir;
11351     msg("Exiting...\n");
11352     if (!Doxygen::filterDBFileName.isEmpty())
11353     {
11354       thisDir.remove(Doxygen::filterDBFileName.str());
11355     }
11356   }
11357 }
11358 
createOutputDirectory(const QCString & baseDirName,const QCString & formatDirName,const char * defaultDirName)11359 static QCString createOutputDirectory(const QCString &baseDirName,
11360                                   const QCString &formatDirName,
11361                                   const char *defaultDirName)
11362 {
11363   QCString result = formatDirName;
11364   if (result.isEmpty())
11365   {
11366     result = baseDirName + defaultDirName;
11367   }
11368   else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
11369   {
11370     result.prepend(baseDirName+"/");
11371   }
11372   Dir formatDir(result.str());
11373   if (!formatDir.exists() && !formatDir.mkdir(result.str()))
11374   {
11375     err("Could not create output directory %s\n", qPrint(result));
11376     cleanUpDoxygen();
11377     exit(1);
11378   }
11379   return result;
11380 }
11381 
getQchFileName()11382 static QCString getQchFileName()
11383 {
11384   QCString const & qchFile = Config_getString(QCH_FILE);
11385   if (!qchFile.isEmpty())
11386   {
11387     return qchFile;
11388   }
11389 
11390   QCString const & projectName = Config_getString(PROJECT_NAME);
11391   QCString const & versionText = Config_getString(PROJECT_NUMBER);
11392 
11393   return QCString("../qch/")
11394       + (projectName.isEmpty() ? QCString("index") : projectName)
11395       + (versionText.isEmpty() ? QCString("") : QCString("-") + versionText)
11396       + QCString(".qch");
11397 }
11398 
searchInputFiles()11399 void searchInputFiles()
11400 {
11401   StringUnorderedSet killSet;
11402 
11403   const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11404   bool alwaysRecursive = Config_getBool(RECURSIVE);
11405   StringUnorderedSet excludeNameSet;
11406 
11407   // gather names of all files in the include path
11408   g_s.begin("Searching for include files...\n");
11409   killSet.clear();
11410   const StringVector &includePathList = Config_getList(INCLUDE_PATH);
11411   for (const auto &s : includePathList)
11412   {
11413     size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
11414     const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
11415                                          Config_getList(INCLUDE_FILE_PATTERNS);
11416     readFileOrDirectory(s.c_str(),                     // s
11417                         Doxygen::includeNameLinkedMap, // fnDict
11418                         0,                             // exclSet
11419                         &pl,                           // patList
11420                         &exclPatterns,                 // exclPatList
11421                         0,                             // resultList
11422                         0,                             // resultSet
11423                         alwaysRecursive,               // recursive
11424                         TRUE,                          // errorIfNotExist
11425                         &killSet);                     // killSet
11426   }
11427   g_s.end();
11428 
11429   g_s.begin("Searching for example files...\n");
11430   killSet.clear();
11431   const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
11432   for (const auto &s : examplePathList)
11433   {
11434     readFileOrDirectory(s.c_str(),                                              // s
11435                         Doxygen::exampleNameLinkedMap,                          // fnDict
11436                         0,                                                      // exclSet
11437                         &Config_getList(EXAMPLE_PATTERNS),                      // patList
11438                         0,                                                      // exclPatList
11439                         0,                                                      // resultList
11440                         0,                                                      // resultSet
11441                         (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
11442                         TRUE,                                                   // errorIfNotExist
11443                         &killSet);                                              // killSet
11444   }
11445   g_s.end();
11446 
11447   g_s.begin("Searching for images...\n");
11448   killSet.clear();
11449   const StringVector &imagePathList=Config_getList(IMAGE_PATH);
11450   for (const auto &s : imagePathList)
11451   {
11452     readFileOrDirectory(s.c_str(),                        // s
11453                         Doxygen::imageNameLinkedMap,      // fnDict
11454                         0,                                // exclSet
11455                         0,                                // patList
11456                         0,                                // exclPatList
11457                         0,                                // resultList
11458                         0,                                // resultSet
11459                         alwaysRecursive,                  // recursive
11460                         TRUE,                             // errorIfNotExist
11461                         &killSet);                        // killSet
11462   }
11463   g_s.end();
11464 
11465   g_s.begin("Searching for dot files...\n");
11466   killSet.clear();
11467   const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
11468   for (const auto &s : dotFileList)
11469   {
11470     readFileOrDirectory(s.c_str(),                      // s
11471                         Doxygen::dotFileNameLinkedMap,  // fnDict
11472                         0,                              // exclSet
11473                         0,                              // patList
11474                         0,                              // exclPatList
11475                         0,                              // resultList
11476                         0,                              // resultSet
11477                         alwaysRecursive,                // recursive
11478                         TRUE,                           // errorIfNotExist
11479                         &killSet);                      // killSet
11480   }
11481   g_s.end();
11482 
11483   g_s.begin("Searching for msc files...\n");
11484   killSet.clear();
11485   const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
11486   for (const auto &s : mscFileList)
11487   {
11488     readFileOrDirectory(s.c_str(),                       // s
11489                         Doxygen::mscFileNameLinkedMap,   // fnDict
11490                         0,                               // exclSet
11491                         0,                               // patList
11492                         0,                               // exclPatList
11493                         0,                               // resultList
11494                         0,                               // resultSet
11495                         alwaysRecursive,                 // recursive
11496                         TRUE,                            // errorIfNotExist
11497                         &killSet);                       // killSet
11498   }
11499   g_s.end();
11500 
11501   g_s.begin("Searching for dia files...\n");
11502   killSet.clear();
11503   const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
11504   for (const auto &s : diaFileList)
11505   {
11506     readFileOrDirectory(s.c_str(),                         // s
11507                         Doxygen::diaFileNameLinkedMap,     // fnDict
11508                         0,                                 // exclSet
11509                         0,                                 // patList
11510                         0,                                 // exclPatList
11511                         0,                                 // resultList
11512                         0,                                 // resultSet
11513                         alwaysRecursive,                   // recursive
11514                         TRUE,                              // errorIfNotExist
11515                         &killSet);                         // killSet
11516   }
11517   g_s.end();
11518 
11519   g_s.begin("Searching for files to exclude\n");
11520   const StringVector &excludeList = Config_getList(EXCLUDE);
11521   for (const auto &s : excludeList)
11522   {
11523     readFileOrDirectory(s.c_str(),                          // s
11524                         0,                                  // fnDict
11525                         0,                                  // exclSet
11526                         &Config_getList(FILE_PATTERNS),     // patList
11527                         0,                                  // exclPatList
11528                         0,                                  // resultList
11529                         &excludeNameSet,                    // resultSet
11530                         alwaysRecursive,                    // recursive
11531                         FALSE);                             // errorIfNotExist
11532   }
11533   g_s.end();
11534 
11535   /**************************************************************************
11536    *             Determine Input Files                                      *
11537    **************************************************************************/
11538 
11539   g_s.begin("Searching INPUT for files to process...\n");
11540   killSet.clear();
11541   Doxygen::inputPaths.clear();
11542   const StringVector &inputList=Config_getList(INPUT);
11543   for (const auto &s : inputList)
11544   {
11545     QCString path=s.c_str();
11546     uint l = path.length();
11547     if (l>0)
11548     {
11549       // strip trailing slashes
11550       if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
11551 
11552       readFileOrDirectory(
11553           path,                               // s
11554           Doxygen::inputNameLinkedMap,        // fnDict
11555           &excludeNameSet,                    // exclSet
11556           &Config_getList(FILE_PATTERNS),     // patList
11557           &exclPatterns,                      // exclPatList
11558           &g_inputFiles,                      // resultList
11559           0,                                  // resultSet
11560           alwaysRecursive,                    // recursive
11561           TRUE,                               // errorIfNotExist
11562           &killSet,                           // killSet
11563           &Doxygen::inputPaths);              // paths
11564     }
11565   }
11566 
11567   // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
11568   std::sort(Doxygen::inputNameLinkedMap->begin(),
11569             Doxygen::inputNameLinkedMap->end(),
11570             [](const auto &f1,const auto &f2)
11571             {
11572               return  qstricmp(f1->fullName(),f2->fullName())<0;
11573             });
11574   for (auto &fileName : *Doxygen::inputNameLinkedMap)
11575   {
11576     if (fileName->size()>1)
11577     {
11578       std::sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
11579         {
11580           return qstricmp(f1->absFilePath(),f2->absFilePath())<0;
11581         });
11582     }
11583   }
11584   g_s.end();
11585 }
11586 
11587 
parseInput()11588 void parseInput()
11589 {
11590   atexit(exitDoxygen);
11591 
11592 #if USE_LIBCLANG
11593   Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
11594 #endif
11595 
11596   // we would like to show the versionString earlier, but we first have to handle the configuration file
11597   // to know the value of the QUIET setting.
11598   QCString versionString = getFullVersion();
11599   msg("Doxygen version used: %s\n",qPrint(versionString));
11600 
11601   /**************************************************************************
11602    *            Make sure the output directory exists
11603    **************************************************************************/
11604   QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
11605   if (outputDirectory.isEmpty())
11606   {
11607     outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath().c_str());
11608   }
11609   else
11610   {
11611     Dir dir(outputDirectory.str());
11612     if (!dir.exists())
11613     {
11614       dir.setPath(Dir::currentDirPath());
11615       if (!dir.mkdir(outputDirectory.str()))
11616       {
11617         err("tag OUTPUT_DIRECTORY: Output directory '%s' does not "
11618             "exist and cannot be created\n",qPrint(outputDirectory));
11619         cleanUpDoxygen();
11620         exit(1);
11621       }
11622       else
11623       {
11624         msg("Notice: Output directory '%s' does not exist. "
11625             "I have created it for you.\n", qPrint(outputDirectory));
11626       }
11627       dir.setPath(outputDirectory.str());
11628     }
11629     outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath().c_str());
11630   }
11631 
11632   /**************************************************************************
11633    *            Initialize global lists and dictionaries
11634    **************************************************************************/
11635 
11636   // also scale lookup cache with SYMBOL_CACHE_SIZE
11637   int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
11638   if (cacheSize<0) cacheSize=0;
11639   if (cacheSize>9) cacheSize=9;
11640   uint lookupSize = 65536 << cacheSize;
11641   Doxygen::lookupCache = new Cache<std::string,LookupInfo>(lookupSize);
11642 
11643 #ifdef HAS_SIGNALS
11644   signal(SIGINT, stopDoxygen);
11645 #endif
11646 
11647   uint pid = Portable::pid();
11648   Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
11649   Doxygen::filterDBFileName.prepend(outputDirectory+"/");
11650 
11651   /**************************************************************************
11652    *            Check/create output directories                             *
11653    **************************************************************************/
11654 
11655   QCString htmlOutput;
11656   bool generateHtml = Config_getBool(GENERATE_HTML);
11657   if (generateHtml || g_useOutputTemplate /* TODO: temp hack */)
11658   {
11659     htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
11660     Config_updateString(HTML_OUTPUT,htmlOutput);
11661 
11662     // add HTML indexers that are enabled
11663     bool generateHtmlHelp    = Config_getBool(GENERATE_HTMLHELP);
11664     bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
11665     bool generateQhp         = Config_getBool(GENERATE_QHP);
11666     bool generateTreeView    = Config_getBool(GENERATE_TREEVIEW);
11667     bool generateDocSet      = Config_getBool(GENERATE_DOCSET);
11668     if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
11669     if (generateHtmlHelp)    Doxygen::indexList->addIndex<HtmlHelp>();
11670     if (generateQhp)         Doxygen::indexList->addIndex<Qhp>();
11671     if (generateTreeView)    Doxygen::indexList->addIndex<FTVHelp>(TRUE);
11672     if (generateDocSet)      Doxygen::indexList->addIndex<DocSets>();
11673     Doxygen::indexList->initialize();
11674   }
11675 
11676   QCString docbookOutput;
11677   bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
11678   if (generateDocbook)
11679   {
11680     docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
11681     Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
11682   }
11683 
11684   QCString xmlOutput;
11685   bool generateXml = Config_getBool(GENERATE_XML);
11686   if (generateXml)
11687   {
11688     xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
11689     Config_updateString(XML_OUTPUT,xmlOutput);
11690   }
11691 
11692   QCString latexOutput;
11693   bool generateLatex = Config_getBool(GENERATE_LATEX);
11694   if (generateLatex)
11695   {
11696     latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
11697     Config_updateString(LATEX_OUTPUT,latexOutput);
11698   }
11699 
11700   QCString rtfOutput;
11701   bool generateRtf = Config_getBool(GENERATE_RTF);
11702   if (generateRtf)
11703   {
11704     rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
11705     Config_updateString(RTF_OUTPUT,rtfOutput);
11706   }
11707 
11708   QCString manOutput;
11709   bool generateMan = Config_getBool(GENERATE_MAN);
11710   if (generateMan)
11711   {
11712     manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
11713     Config_updateString(MAN_OUTPUT,manOutput);
11714   }
11715 
11716 #if USE_SQLITE3
11717   QCString sqlOutput;
11718   bool generateSql = Config_getBool(GENERATE_SQLITE3);
11719   if (generateSql)
11720   {
11721     sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
11722     Config_updateString(SQLITE3_OUTPUT,sqlOutput);
11723   }
11724 #endif
11725 
11726   if (Config_getBool(HAVE_DOT))
11727   {
11728     QCString curFontPath = Config_getString(DOT_FONTPATH);
11729     if (curFontPath.isEmpty())
11730     {
11731       Portable::getenv("DOTFONTPATH");
11732       QCString newFontPath = ".";
11733       if (!curFontPath.isEmpty())
11734       {
11735         newFontPath+=Portable::pathListSeparator();
11736         newFontPath+=curFontPath;
11737       }
11738       Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
11739     }
11740     else
11741     {
11742       Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
11743     }
11744   }
11745 
11746 
11747 
11748   /**************************************************************************
11749    *             Handle layout file                                         *
11750    **************************************************************************/
11751 
11752   LayoutDocManager::instance().init();
11753   QCString layoutFileName = Config_getString(LAYOUT_FILE);
11754   bool defaultLayoutUsed = FALSE;
11755   if (layoutFileName.isEmpty())
11756   {
11757     layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
11758     defaultLayoutUsed = TRUE;
11759   }
11760 
11761   FileInfo fi(layoutFileName.str());
11762   if (fi.exists())
11763   {
11764     msg("Parsing layout file %s...\n",qPrint(layoutFileName));
11765     LayoutDocManager::instance().parse(layoutFileName);
11766   }
11767   else if (!defaultLayoutUsed)
11768   {
11769     warn_uncond("failed to open layout file '%s' for reading!\n",qPrint(layoutFileName));
11770   }
11771 
11772   /**************************************************************************
11773    *             Read and preprocess input                                  *
11774    **************************************************************************/
11775 
11776   // prevent search in the output directories
11777   StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11778   if (generateHtml)    exclPatterns.push_back(htmlOutput.str());
11779   if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
11780   if (generateXml)     exclPatterns.push_back(xmlOutput.str());
11781   if (generateLatex)   exclPatterns.push_back(latexOutput.str());
11782   if (generateRtf)     exclPatterns.push_back(rtfOutput.str());
11783   if (generateMan)     exclPatterns.push_back(manOutput.str());
11784   Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
11785 
11786   searchInputFiles();
11787 
11788   // Notice: the order of the function calls below is very important!
11789 
11790   if (Config_getBool(GENERATE_HTML) && !Config_getBool(USE_MATHJAX))
11791   {
11792     FormulaManager::instance().readFormulas(Config_getString(HTML_OUTPUT));
11793   }
11794   if (Config_getBool(GENERATE_RTF))
11795   {
11796     // in case GENERRATE_HTML is set we just have to compare, both repositories should be identical
11797     FormulaManager::instance().readFormulas(Config_getString(RTF_OUTPUT),
11798                               Config_getBool(GENERATE_HTML) &&
11799                               !Config_getBool(USE_MATHJAX));
11800   }
11801   if (Config_getBool(GENERATE_DOCBOOK))
11802   {
11803     // in case GENERRATE_HTML is set we just have to compare, both repositories should be identical
11804     FormulaManager::instance().readFormulas(Config_getString(DOCBOOK_OUTPUT),
11805                          (Config_getBool(GENERATE_HTML) &&
11806                          !Config_getBool(USE_MATHJAX)) ||
11807                          Config_getBool(GENERATE_RTF));
11808   }
11809 
11810   /**************************************************************************
11811    *             Handle Tag Files                                           *
11812    **************************************************************************/
11813 
11814   std::shared_ptr<Entry> root = std::make_shared<Entry>();
11815   msg("Reading and parsing tag files\n");
11816 
11817   const StringVector &tagFileList = Config_getList(TAGFILES);
11818   for (const auto &s : tagFileList)
11819   {
11820     readTagFile(root,s.c_str());
11821   }
11822 
11823   /**************************************************************************
11824    *             Parse source files                                         *
11825    **************************************************************************/
11826 
11827   addSTLSupport(root);
11828 
11829   g_s.begin("Parsing files\n");
11830   if (Config_getInt(NUM_PROC_THREADS)==1)
11831   {
11832     parseFilesSingleThreading(root);
11833   }
11834   else
11835   {
11836     parseFilesMultiThreading(root);
11837   }
11838   g_s.end();
11839 
11840   /**************************************************************************
11841    *             Gather information                                         *
11842    **************************************************************************/
11843 
11844   g_s.begin("Building macro definition list...\n");
11845   buildDefineList();
11846   g_s.end();
11847 
11848   g_s.begin("Building group list...\n");
11849   buildGroupList(root.get());
11850   organizeSubGroups(root.get());
11851   g_s.end();
11852 
11853   g_s.begin("Building directory list...\n");
11854   buildDirectories();
11855   findDirDocumentation(root.get());
11856   g_s.end();
11857 
11858   g_s.begin("Building namespace list...\n");
11859   buildNamespaceList(root.get());
11860   findUsingDirectives(root.get());
11861   g_s.end();
11862 
11863   g_s.begin("Building file list...\n");
11864   buildFileList(root.get());
11865   g_s.end();
11866 
11867   g_s.begin("Building class list...\n");
11868   buildClassList(root.get());
11869   g_s.end();
11870 
11871   g_s.begin("Building concept list...\n");
11872   buildConceptList(root.get());
11873   g_s.end();
11874 
11875   // build list of using declarations here (global list)
11876   buildListOfUsingDecls(root.get());
11877   g_s.end();
11878 
11879   g_s.begin("Computing nesting relations for classes...\n");
11880   resolveClassNestingRelations();
11881   g_s.end();
11882   // 1.8.2-20121111: no longer add nested classes to the group as well
11883   //distributeClassGroupRelations();
11884 
11885   // calling buildClassList may result in cached relations that
11886   // become invalid after resolveClassNestingRelations(), that's why
11887   // we need to clear the cache here
11888   Doxygen::lookupCache->clear();
11889   // we don't need the list of using declaration anymore
11890   g_usingDeclarations.clear();
11891 
11892   g_s.begin("Associating documentation with classes...\n");
11893   buildClassDocList(root.get());
11894   g_s.end();
11895 
11896   g_s.begin("Associating documentation with concepts...\n");
11897   buildConceptDocList(root.get());
11898   distributeConceptGroups();
11899   g_s.end();
11900 
11901   g_s.begin("Building example list...\n");
11902   buildExampleList(root.get());
11903   g_s.end();
11904 
11905   g_s.begin("Searching for enumerations...\n");
11906   findEnums(root.get());
11907   g_s.end();
11908 
11909   // Since buildVarList calls isVarWithConstructor
11910   // and this calls getResolvedClass we need to process
11911   // typedefs first so the relations between classes via typedefs
11912   // are properly resolved. See bug 536385 for an example.
11913   g_s.begin("Searching for documented typedefs...\n");
11914   buildTypedefList(root.get());
11915   g_s.end();
11916 
11917   if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
11918   {
11919     g_s.begin("Searching for documented sequences...\n");
11920     buildSequenceList(root.get());
11921     g_s.end();
11922 
11923     g_s.begin("Searching for documented dictionaries...\n");
11924     buildDictionaryList(root.get());
11925     g_s.end();
11926   }
11927 
11928   g_s.begin("Searching for members imported via using declarations...\n");
11929   // this should be after buildTypedefList in order to properly import
11930   // used typedefs
11931   findUsingDeclarations(root.get(),TRUE);  // do for python packages first
11932   findUsingDeclarations(root.get(),FALSE); // then the rest
11933   g_s.end();
11934 
11935   g_s.begin("Searching for included using directives...\n");
11936   findIncludedUsingDirectives();
11937   g_s.end();
11938 
11939   g_s.begin("Searching for documented variables...\n");
11940   buildVarList(root.get());
11941   g_s.end();
11942 
11943   g_s.begin("Building interface member list...\n");
11944   buildInterfaceAndServiceList(root.get()); // UNO IDL
11945 
11946   g_s.begin("Building member list...\n"); // using class info only !
11947   buildFunctionList(root.get());
11948   g_s.end();
11949 
11950   g_s.begin("Searching for friends...\n");
11951   findFriends();
11952   g_s.end();
11953 
11954   g_s.begin("Searching for documented defines...\n");
11955   findDefineDocumentation(root.get());
11956   g_s.end();
11957 
11958   g_s.begin("Computing class inheritance relations...\n");
11959   findClassEntries(root.get());
11960   findInheritedTemplateInstances();
11961   g_s.end();
11962 
11963   g_s.begin("Computing class usage relations...\n");
11964   findUsedTemplateInstances();
11965   g_s.end();
11966 
11967   if (Config_getBool(INLINE_SIMPLE_STRUCTS))
11968   {
11969     g_s.begin("Searching for tag less structs...\n");
11970     findTagLessClasses();
11971     g_s.end();
11972   }
11973 
11974   g_s.begin("Flushing cached template relations that have become invalid...\n");
11975   flushCachedTemplateRelations();
11976   g_s.end();
11977 
11978   g_s.begin("Computing class relations...\n");
11979   computeTemplateClassRelations();
11980   flushUnresolvedRelations();
11981   if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
11982   {
11983     VhdlDocGen::computeVhdlComponentRelations();
11984   }
11985   computeClassRelations();
11986   g_classEntries.clear();
11987   g_s.end();
11988 
11989   g_s.begin("Add enum values to enums...\n");
11990   addEnumValuesToEnums(root.get());
11991   findEnumDocumentation(root.get());
11992   g_s.end();
11993 
11994   g_s.begin("Searching for member function documentation...\n");
11995   findObjCMethodDefinitions(root.get());
11996   findMemberDocumentation(root.get()); // may introduce new members !
11997   findUsingDeclImports(root.get()); // may introduce new members !
11998 
11999   transferRelatedFunctionDocumentation();
12000   transferFunctionDocumentation();
12001   g_s.end();
12002 
12003   // moved to after finding and copying documentation,
12004   // as this introduces new members see bug 722654
12005   g_s.begin("Creating members for template instances...\n");
12006   createTemplateInstanceMembers();
12007   g_s.end();
12008 
12009   g_s.begin("Building page list...\n");
12010   buildPageList(root.get());
12011   g_s.end();
12012 
12013   g_s.begin("Search for main page...\n");
12014   findMainPage(root.get());
12015   findMainPageTagFiles(root.get());
12016   g_s.end();
12017 
12018   g_s.begin("Computing page relations...\n");
12019   computePageRelations(root.get());
12020   checkPageRelations();
12021   g_s.end();
12022 
12023   g_s.begin("Determining the scope of groups...\n");
12024   findGroupScope(root.get());
12025   g_s.end();
12026 
12027   auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12028   {
12029     return qstricmp(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12030                     n2->memberName().data()+getPrefixIndex(n2->memberName())
12031                    )<0;
12032   };
12033 
12034   auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12035   {
12036     if (Config_getBool(SORT_BY_SCOPE_NAME))
12037     {
12038       return qstricmp(c1->name(), c2->name())<0;
12039     }
12040     else
12041     {
12042       int i = qstricmp(c1->className(), c2->className());
12043       return i==0 ? qstricmp(c1->name(), c2->name())<0 : i<0;
12044     }
12045   };
12046 
12047   auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12048   {
12049     return qstricmp(n1->name(),n2->name())<0;
12050   };
12051 
12052   auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12053   {
12054     return qstricmp(c1->name(),c2->name())<0;
12055   };
12056 
12057   g_s.begin("Sorting lists...\n");
12058   std::sort(Doxygen::memberNameLinkedMap->begin(),
12059             Doxygen::memberNameLinkedMap->end(),
12060             memberNameComp);
12061   std::sort(Doxygen::functionNameLinkedMap->begin(),
12062             Doxygen::functionNameLinkedMap->end(),
12063             memberNameComp);
12064   std::sort(Doxygen::hiddenClassLinkedMap->begin(),
12065             Doxygen::hiddenClassLinkedMap->end(),
12066             classComp);
12067   std::sort(Doxygen::classLinkedMap->begin(),
12068             Doxygen::classLinkedMap->end(),
12069             classComp);
12070   std::sort(Doxygen::conceptLinkedMap->begin(),
12071             Doxygen::conceptLinkedMap->end(),
12072             conceptComp);
12073   std::sort(Doxygen::namespaceLinkedMap->begin(),
12074             Doxygen::namespaceLinkedMap->end(),
12075             namespaceComp);
12076   g_s.end();
12077 
12078   g_s.begin("Determining which enums are documented\n");
12079   findDocumentedEnumValues();
12080   g_s.end();
12081 
12082   g_s.begin("Computing member relations...\n");
12083   mergeCategories();
12084   computeMemberRelations();
12085   g_s.end();
12086 
12087   g_s.begin("Building full member lists recursively...\n");
12088   buildCompleteMemberLists();
12089   g_s.end();
12090 
12091   g_s.begin("Adding members to member groups.\n");
12092   addMembersToMemberGroup();
12093   g_s.end();
12094 
12095   if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12096   {
12097     g_s.begin("Distributing member group documentation.\n");
12098     distributeMemberGroupDocumentation();
12099     g_s.end();
12100   }
12101 
12102   g_s.begin("Computing member references...\n");
12103   computeMemberReferences();
12104   g_s.end();
12105 
12106   if (Config_getBool(INHERIT_DOCS))
12107   {
12108     g_s.begin("Inheriting documentation...\n");
12109     inheritDocumentation();
12110     g_s.end();
12111   }
12112 
12113   // compute the shortest possible names of all files
12114   // without losing the uniqueness of the file names.
12115   g_s.begin("Generating disk names...\n");
12116   generateDiskNames();
12117   g_s.end();
12118 
12119   g_s.begin("Adding source references...\n");
12120   addSourceReferences();
12121   g_s.end();
12122 
12123   g_s.begin("Adding xrefitems...\n");
12124   addListReferences();
12125   generateXRefPages();
12126   g_s.end();
12127 
12128   g_s.begin("Sorting member lists...\n");
12129   sortMemberLists();
12130   g_s.end();
12131 
12132   g_s.begin("Setting anonymous enum type...\n");
12133   setAnonymousEnumType();
12134   g_s.end();
12135 
12136   if (Config_getBool(DIRECTORY_GRAPH))
12137   {
12138     g_s.begin("Computing dependencies between directories...\n");
12139     computeDirDependencies();
12140     g_s.end();
12141   }
12142 
12143   g_s.begin("Generating citations page...\n");
12144   CitationManager::instance().generatePage();
12145   g_s.end();
12146 
12147   g_s.begin("Counting members...\n");
12148   countMembers();
12149   g_s.end();
12150 
12151   g_s.begin("Counting data structures...\n");
12152   countDataStructures();
12153   g_s.end();
12154 
12155   g_s.begin("Resolving user defined references...\n");
12156   resolveUserReferences();
12157   g_s.end();
12158 
12159   g_s.begin("Finding anchors and sections in the documentation...\n");
12160   findSectionsInDocumentation();
12161   g_s.end();
12162 
12163   g_s.begin("Transferring function references...\n");
12164   transferFunctionReferences();
12165   g_s.end();
12166 
12167   g_s.begin("Combining using relations...\n");
12168   combineUsingRelations();
12169   g_s.end();
12170 
12171   g_s.begin("Adding members to index pages...\n");
12172   addMembersToIndex();
12173   addToIndices();
12174   g_s.end();
12175 
12176   g_s.begin("Correcting members for VHDL...\n");
12177   vhdlCorrectMemberProperties();
12178   g_s.end();
12179 
12180   g_s.begin("Computing tooltip texts...\n");
12181   computeTooltipTexts();
12182   g_s.end();
12183 
12184   if (Config_getBool(SORT_GROUP_NAMES))
12185   {
12186     std::sort(Doxygen::groupLinkedMap->begin(),
12187               Doxygen::groupLinkedMap->end(),
12188               [](const auto &g1,const auto &g2)
12189               { return g1->groupTitle() < g2->groupTitle(); });
12190 
12191     for (const auto &gd : *Doxygen::groupLinkedMap)
12192     {
12193       gd->sortSubGroups();
12194     }
12195   }
12196 
12197 }
12198 
generateOutput()12199 void generateOutput()
12200 {
12201   /**************************************************************************
12202    *            Initialize output generators                                *
12203    **************************************************************************/
12204 
12205   /// add extra languages for which we can only produce syntax highlighted code
12206   addCodeOnlyMappings();
12207 
12208   //// dump all symbols
12209   if (g_dumpSymbolMap)
12210   {
12211     dumpSymbolMap();
12212     exit(0);
12213   }
12214 
12215   initSearchIndexer();
12216 
12217   bool generateHtml  = Config_getBool(GENERATE_HTML);
12218   bool generateLatex = Config_getBool(GENERATE_LATEX);
12219   bool generateMan   = Config_getBool(GENERATE_MAN);
12220   bool generateRtf   = Config_getBool(GENERATE_RTF);
12221   bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12222 
12223 
12224   g_outputList = new OutputList;
12225   if (generateHtml)
12226   {
12227     g_outputList->add<HtmlGenerator>();
12228     HtmlGenerator::init();
12229     HtmlGenerator::writeTabData();
12230   }
12231   if (generateLatex)
12232   {
12233     g_outputList->add<LatexGenerator>();
12234     LatexGenerator::init();
12235   }
12236   if (generateDocbook)
12237   {
12238     g_outputList->add<DocbookGenerator>();
12239     DocbookGenerator::init();
12240   }
12241   if (generateMan)
12242   {
12243     g_outputList->add<ManGenerator>();
12244     ManGenerator::init();
12245   }
12246   if (generateRtf)
12247   {
12248     g_outputList->add<RTFGenerator>();
12249     RTFGenerator::init();
12250   }
12251   if (Config_getBool(USE_HTAGS))
12252   {
12253     Htags::useHtags = TRUE;
12254     QCString htmldir = Config_getString(HTML_OUTPUT);
12255     if (!Htags::execute(htmldir))
12256        err("USE_HTAGS is YES but htags(1) failed. \n");
12257     else if (!Htags::loadFilemap(htmldir))
12258        err("htags(1) ended normally but failed to load the filemap. \n");
12259   }
12260 
12261   /**************************************************************************
12262    *                        Generate documentation                          *
12263    **************************************************************************/
12264 
12265   g_s.begin("Generating style sheet...\n");
12266   //printf("writing style info\n");
12267   g_outputList->writeStyleInfo(0); // write first part
12268   g_s.end();
12269 
12270   static bool searchEngine      = Config_getBool(SEARCHENGINE);
12271   static bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
12272 
12273   g_s.begin("Generating search indices...\n");
12274   if (searchEngine && !serverBasedSearch && (generateHtml || g_useOutputTemplate))
12275   {
12276     createJavaScriptSearchIndex();
12277   }
12278 
12279   // generate search indices (need to do this before writing other HTML
12280   // pages as these contain a drop down menu with options depending on
12281   // what categories we find in this function.
12282   if (generateHtml && searchEngine)
12283   {
12284     QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
12285     Dir searchDir(searchDirName.str());
12286     if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
12287     {
12288       term("Could not create search results directory '%s' $PWD='%s'\n",
12289           qPrint(searchDirName),Dir::currentDirPath().c_str());
12290     }
12291     HtmlGenerator::writeSearchData(searchDirName);
12292     if (!serverBasedSearch) // client side search index
12293     {
12294       writeJavaScriptSearchIndex();
12295     }
12296   }
12297   g_s.end();
12298 
12299   // copy static stuff
12300   if (generateHtml)
12301   {
12302     FTVHelp::generateTreeViewImages();
12303     copyStyleSheet();
12304     copyLogo(Config_getString(HTML_OUTPUT));
12305     copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
12306   }
12307   if (generateLatex)
12308   {
12309     copyLatexStyleSheet();
12310     copyLogo(Config_getString(LATEX_OUTPUT));
12311     copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
12312   }
12313   if (generateDocbook)
12314   {
12315     copyLogo(Config_getString(DOCBOOK_OUTPUT));
12316   }
12317   if (generateRtf)
12318   {
12319     copyLogo(Config_getString(RTF_OUTPUT));
12320   }
12321 
12322   const FormulaManager &fm = FormulaManager::instance();
12323   if (fm.hasFormulas() && generateHtml
12324       && !Config_getBool(USE_MATHJAX))
12325   {
12326     g_s.begin("Generating images for formulas in HTML...\n");
12327     fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
12328         FormulaManager::Format::Vector : FormulaManager::Format::Bitmap, FormulaManager::HighDPI::On);
12329     g_s.end();
12330   }
12331   if (fm.hasFormulas() && generateRtf)
12332   {
12333     g_s.begin("Generating images for formulas in RTF...\n");
12334     fm.generateImages(Config_getString(RTF_OUTPUT),FormulaManager::Format::Bitmap);
12335     g_s.end();
12336   }
12337 
12338   if (fm.hasFormulas() && generateDocbook)
12339   {
12340     g_s.begin("Generating images for formulas in Docbook...\n");
12341     fm.generateImages(Config_getString(DOCBOOK_OUTPUT),FormulaManager::Format::Bitmap);
12342     g_s.end();
12343   }
12344 
12345   g_s.begin("Generating example documentation...\n");
12346   generateExampleDocs();
12347   g_s.end();
12348 
12349   warn_flush();
12350 
12351   g_s.begin("Generating file sources...\n");
12352   generateFileSources();
12353   g_s.end();
12354 
12355   g_s.begin("Generating file documentation...\n");
12356   generateFileDocs();
12357   g_s.end();
12358 
12359   g_s.begin("Generating page documentation...\n");
12360   generatePageDocs();
12361   g_s.end();
12362 
12363   g_s.begin("Generating group documentation...\n");
12364   generateGroupDocs();
12365   g_s.end();
12366 
12367   g_s.begin("Generating class documentation...\n");
12368   generateClassDocs();
12369   g_s.end();
12370 
12371   g_s.begin("Generating concept documentation...\n");
12372   generateConceptDocs();
12373   g_s.end();
12374 
12375   g_s.begin("Generating namespace index...\n");
12376   generateNamespaceDocs();
12377   g_s.end();
12378 
12379   if (Config_getBool(GENERATE_LEGEND))
12380   {
12381     g_s.begin("Generating graph info page...\n");
12382     writeGraphInfo(*g_outputList);
12383     g_s.end();
12384   }
12385 
12386   g_s.begin("Generating directory documentation...\n");
12387   generateDirDocs(*g_outputList);
12388   g_s.end();
12389 
12390   if (g_outputList->size()>0)
12391   {
12392     writeIndexHierarchy(*g_outputList);
12393   }
12394 
12395   g_s.begin("finalizing index lists...\n");
12396   Doxygen::indexList->finalize();
12397   g_s.end();
12398 
12399   g_s.begin("writing tag file...\n");
12400   writeTagFile();
12401   g_s.end();
12402 
12403   if (Config_getBool(GENERATE_XML))
12404   {
12405     g_s.begin("Generating XML output...\n");
12406     Doxygen::generatingXmlOutput=TRUE;
12407     generateXML();
12408     Doxygen::generatingXmlOutput=FALSE;
12409     g_s.end();
12410   }
12411 #if USE_SQLITE3
12412   if (Config_getBool(GENERATE_SQLITE3))
12413   {
12414     g_s.begin("Generating SQLITE3 output...\n");
12415     generateSqlite3();
12416     g_s.end();
12417   }
12418 #endif
12419 
12420   if (Config_getBool(GENERATE_AUTOGEN_DEF))
12421   {
12422     g_s.begin("Generating AutoGen DEF output...\n");
12423     generateDEF();
12424     g_s.end();
12425   }
12426   if (Config_getBool(GENERATE_PERLMOD))
12427   {
12428     g_s.begin("Generating Perl module output...\n");
12429     generatePerlMod();
12430     g_s.end();
12431   }
12432   if (generateHtml && searchEngine && serverBasedSearch)
12433   {
12434     g_s.begin("Generating search index\n");
12435     if (Doxygen::searchIndex->kind()==SearchIndexIntf::Internal) // write own search index
12436     {
12437       HtmlGenerator::writeSearchPage();
12438       Doxygen::searchIndex->write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
12439     }
12440     else // write data for external search index
12441     {
12442       HtmlGenerator::writeExternalSearchPage();
12443       QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
12444       if (searchDataFile.isEmpty())
12445       {
12446         searchDataFile="searchdata.xml";
12447       }
12448       if (!Portable::isAbsolutePath(searchDataFile.data()))
12449       {
12450         searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
12451       }
12452       Doxygen::searchIndex->write(searchDataFile);
12453     }
12454     g_s.end();
12455   }
12456 
12457   if (g_useOutputTemplate)
12458   {
12459     g_s.begin("Generating output via template engine...\n");
12460     generateOutputViaTemplate();
12461     g_s.end();
12462   }
12463 
12464   warn_flush();
12465 
12466   if (generateRtf)
12467   {
12468     g_s.begin("Combining RTF output...\n");
12469     if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
12470     {
12471       err("An error occurred during post-processing the RTF files!\n");
12472     }
12473     g_s.end();
12474   }
12475 
12476   warn_flush();
12477 
12478   g_s.begin("Running plantuml with JAVA...\n");
12479   PlantumlManager::instance().run();
12480   g_s.end();
12481 
12482   warn_flush();
12483 
12484   if (Config_getBool(HAVE_DOT))
12485   {
12486     g_s.begin("Running dot...\n");
12487     DotManager::instance()->run();
12488     g_s.end();
12489   }
12490 
12491   if (generateHtml &&
12492       Config_getBool(GENERATE_HTMLHELP) &&
12493       !Config_getString(HHC_LOCATION).isEmpty())
12494   {
12495     g_s.begin("Running html help compiler...\n");
12496     std::string oldDir = Dir::currentDirPath();
12497     Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
12498     Portable::setShortDir();
12499     Portable::sysTimerStart();
12500     if (Portable::system(Config_getString(HHC_LOCATION).data(), "index.hhp", Debug::isFlagSet(Debug::ExtCmd))!=1)
12501     {
12502       err("failed to run html help compiler on index.hhp\n");
12503     }
12504     Portable::sysTimerStop();
12505     Dir::setCurrent(oldDir);
12506     g_s.end();
12507   }
12508 
12509   warn_flush();
12510 
12511   if ( generateHtml &&
12512        Config_getBool(GENERATE_QHP) &&
12513       !Config_getString(QHG_LOCATION).isEmpty())
12514   {
12515     g_s.begin("Running qhelpgenerator...\n");
12516     QCString qhpFileName = Qhp::getQhpFileName();
12517     QCString qchFileName = getQchFileName();
12518 
12519     QCString args = QCString().sprintf("%s -o \"%s\"", qPrint(qhpFileName), qPrint(qchFileName));
12520     std::string oldDir = Dir::currentDirPath();
12521     Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
12522     Portable::sysTimerStart();
12523     if (Portable::system(Config_getString(QHG_LOCATION).data(), args.data(), FALSE))
12524     {
12525       err("failed to run qhelpgenerator on index.qhp\n");
12526     }
12527     Portable::sysTimerStop();
12528     Dir::setCurrent(oldDir);
12529     g_s.end();
12530   }
12531 
12532   g_outputList->cleanup();
12533 
12534   int cacheParam;
12535   msg("lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n",
12536       Doxygen::lookupCache->size(),
12537       Doxygen::lookupCache->capacity(),
12538       Doxygen::lookupCache->hits(),
12539       Doxygen::lookupCache->misses());
12540   cacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::lookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
12541   if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
12542   {
12543     msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is %d at the cost of higher memory usage.\n",cacheParam);
12544   }
12545 
12546   if (Debug::isFlagSet(Debug::Time))
12547   {
12548     msg("Total elapsed time: %.6f seconds\n(of which %.6f seconds waiting for external tools to finish)\n",
12549          ((double)Debug::elapsedTime()),
12550          Portable::getSysElapsedTime()
12551         );
12552     g_s.print();
12553   }
12554   else
12555   {
12556     msg("finished...\n");
12557   }
12558 
12559 
12560   /**************************************************************************
12561    *                        Start cleaning up                               *
12562    **************************************************************************/
12563 
12564   cleanUpDoxygen();
12565 
12566   finalizeSearchIndexer();
12567   Dir thisDir;
12568   thisDir.remove(Doxygen::filterDBFileName.str());
12569   finishWarnExit();
12570   Config::deinit();
12571   delete Doxygen::clangUsrMap;
12572   g_successfulRun=TRUE;
12573 }
12574