1 /******************************************************************************
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
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.
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
22 #include <unordered_map>
34 #include "indexlist.h"
39 #include "tagreader.h"
42 #include "docparser.h"
44 #include "outputlist.h"
57 #include "sqlite3gen.h"
59 #include "docbookgen.h"
61 #include "perlmodgen.h"
65 #include "commentcnv.h"
66 #include "cmdmapper.h"
67 #include "searchindex.h"
68 #include "searchindex_js.h"
69 #include "parserintf.h"
72 #include "pyscanner.h"
73 #include "fortrancode.h"
74 #include "fortranscanner.h"
78 #include "lexscanner.h"
81 #include "vhdljjparser.h"
82 #include "vhdldocgen.h"
84 #include "eclipsehelp.h"
87 #include "arguments.h"
88 #include "memberlist.h"
91 #include "classlist.h"
92 #include "namespacedef.h"
94 #include "membername.h"
95 #include "membergroup.h"
99 #include "fileparser.h"
101 #include "plantuml.h"
102 #include "stlsupport.h"
103 #include "threadpool.h"
104 #include "clangparser.h"
105 #include "symbolresolver.h"
108 #include "fileinfo.h"
110 #include "conceptdef.h"
112 #include "moduledef.h"
117 #include <clang/Basic/Version.h>
120 // provided by the generated file resources.cpp
121 extern void initResources();
123 #if !defined(_WIN32) || defined(__CYGWIN__)
128 // globally accessible variables
129 ClassLinkedMap *Doxygen::classLinkedMap = 0;
130 ClassLinkedMap *Doxygen::hiddenClassLinkedMap = 0;
131 ConceptLinkedMap *Doxygen::conceptLinkedMap = 0;
132 NamespaceLinkedMap *Doxygen::namespaceLinkedMap = 0;
133 MemberNameLinkedMap *Doxygen::memberNameLinkedMap = 0;
134 MemberNameLinkedMap *Doxygen::functionNameLinkedMap = 0;
135 FileNameLinkedMap *Doxygen::inputNameLinkedMap = 0;
136 GroupLinkedMap *Doxygen::groupLinkedMap = 0;
137 PageLinkedMap *Doxygen::pageLinkedMap = 0;
138 PageLinkedMap *Doxygen::exampleLinkedMap = 0;
139 StringSet Doxygen::inputPaths;
140 FileNameLinkedMap *Doxygen::includeNameLinkedMap = 0; // include names
141 FileNameLinkedMap *Doxygen::exampleNameLinkedMap = 0; // examples
142 FileNameLinkedMap *Doxygen::imageNameLinkedMap = 0; // images
143 FileNameLinkedMap *Doxygen::dotFileNameLinkedMap = 0; // dot files
144 FileNameLinkedMap *Doxygen::mscFileNameLinkedMap = 0; // msc files
145 FileNameLinkedMap *Doxygen::diaFileNameLinkedMap = 0; // dia files
146 StringUnorderedMap Doxygen::namespaceAliasMap; // all namespace aliases
147 StringMap Doxygen::tagDestinationMap; // all tag locations
148 StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
149 MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
150 std::unique_ptr<PageDef> Doxygen::mainPage;
151 std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
152 NamespaceDefMutable *Doxygen::globalScope;
153 bool Doxygen::parseSourcesNeeded = FALSE;
154 std::unique_ptr<SearchIndexIntf> Doxygen::searchIndex;
155 SymbolMap<Definition>*Doxygen::symbolMap;
156 ClangUsrMap *Doxygen::clangUsrMap = 0;
157 Cache<std::string,LookupInfo> *Doxygen::typeLookupCache;
158 Cache<std::string,LookupInfo> *Doxygen::symbolLookupCache;
159 DirLinkedMap *Doxygen::dirLinkedMap;
160 DirRelationLinkedMap Doxygen::dirRelations;
161 ParserManager *Doxygen::parserManager = 0;
162 QCString Doxygen::htmlFileExtension;
163 bool Doxygen::suppressDocWarnings = FALSE;
164 QCString Doxygen::filterDBFileName;
165 IndexList *Doxygen::indexList;
166 QCString Doxygen::spaces;
167 bool Doxygen::generatingXmlOutput = FALSE;
168 DefinesPerFileList Doxygen::macroDefinitions;
169 bool Doxygen::clangAssistedParsing = FALSE;
170 QCString Doxygen::verifiedDotPath;
171 volatile bool Doxygen::terminating = false;
172 InputFileEncodingList Doxygen::inputFileEncodingList;
173 std::mutex Doxygen::countFlowKeywordsMutex;
174 std::mutex Doxygen::addExampleMutex;
175 StaticInitMap Doxygen::staticInitMap;
177 // locally accessible globals
178 static std::multimap< std::string, const Entry* > g_classEntries;
179 static StringVector g_inputFiles;
180 static StringSet g_compoundKeywords; // keywords recognised as compounds
181 static OutputList *g_outputList = 0; // list of output generating objects
182 static StringSet g_usingDeclarations; // used classes
183 static bool g_successfulRun = FALSE;
184 static bool g_dumpSymbolMap = FALSE;
188 g_inputFiles.clear();
189 //g_excludeNameDict.clear();
190 //delete g_outputList; g_outputList=0;
192 Doxygen::classLinkedMap->clear();
193 Doxygen::hiddenClassLinkedMap->clear();
194 Doxygen::conceptLinkedMap->clear();
195 Doxygen::namespaceLinkedMap->clear();
196 Doxygen::pageLinkedMap->clear();
197 Doxygen::exampleLinkedMap->clear();
198 Doxygen::inputNameLinkedMap->clear();
199 Doxygen::includeNameLinkedMap->clear();
200 Doxygen::exampleNameLinkedMap->clear();
201 Doxygen::imageNameLinkedMap->clear();
202 Doxygen::dotFileNameLinkedMap->clear();
203 Doxygen::mscFileNameLinkedMap->clear();
204 Doxygen::diaFileNameLinkedMap->clear();
205 Doxygen::tagDestinationMap.clear();
206 SectionManager::instance().clear();
207 CitationManager::instance().clear();
208 Doxygen::mainPage.reset();
209 FormulaManager::instance().clear();
216 void begin(const char *name)
219 stats.emplace_back(name,0);
220 startTime = std::chrono::steady_clock::now();
224 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
225 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
226 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
232 if (Debug::isFlagSet(Debug::Time))
234 Debug::clearFlag(Debug::Time);
237 msg("----------------------\n");
238 for (const auto &s : stats)
240 msg("Spent %.6f seconds in %s",s.elapsed,s.name);
242 if (restore) Debug::setFlag(Debug::Time);
249 //stat() : name(NULL),elapsed(0) {}
250 stat(const char *n, double el) : name(n),elapsed(el) {}
252 std::vector<stat> stats;
253 std::chrono::steady_clock::time_point startTime;
257 static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
258 const ArgumentList *al,bool over_load,uint64_t spec);
259 static void findMember(const Entry *root,
260 const QCString &relates,
261 const QCString &type,
262 const QCString &args,
268 enum FindBaseClassRelation_Mode
276 static bool findClassRelation(
281 const TemplateNameMap &templateNames,
282 /*bool insertUndocumented*/
283 FindBaseClassRelation_Mode mode,
287 //----------------------------------------------------------------------------
289 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
290 FileDef *fileScope,const TagInfo *tagInfo);
292 static void addPageToContext(PageDef *pd,Entry *root)
294 if (root->parent()) // add the page to it's scope
296 QCString scope = root->parent()->name;
297 if (root->parent()->section==Entry::PACKAGEDOC_SEC)
299 scope=substitute(scope,".","::");
301 scope = stripAnonymousNamespaceScope(scope);
302 scope+="::"+pd->name();
303 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
311 static void addRelatedPage(Entry *root)
314 for (const Grouping &g : root->groups)
316 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
318 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
319 QCString doc=root->doc+root->inbodyDocs;
321 PageDef *pd = addRelatedPage(root->name,root->args,doc,
332 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
333 pd->addSectionsToDefinition(root->anchors);
334 pd->setLocalToc(root->localToc);
335 addPageToContext(pd,root);
339 static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
341 if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
342 ((!includeExternal && root->tagInfo()==0) ||
343 ( includeExternal && root->tagInfo()!=0))
346 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
347 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
348 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
350 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
351 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
355 if ( !gd->hasGroupTitle() )
357 gd->setGroupTitle( root->type );
359 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
361 warn( root->fileName,root->startLine,
362 "group %s: ignoring title \"%s\" that does not match old title \"%s\"",
363 qPrint(root->name), qPrint(root->type), qPrint(gd->groupTitle()) );
365 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
366 gd->setDocumentation( root->doc, root->docFile, root->docLine );
367 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
368 gd->addSectionsToDefinition(root->anchors);
369 gd->setRefItems(root->sli);
370 gd->setLanguage(root->lang);
371 if (root->groupDocType==Entry::GROUPDOC_NORMAL)
373 gd->enableGroupGraph(root->groupGraph);
380 gd = Doxygen::groupLinkedMap->add(root->name,
381 std::unique_ptr<GroupDef>(
382 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
383 gd->setReference(root->tagInfo()->tagName);
387 gd = Doxygen::groupLinkedMap->add(root->name,
388 std::unique_ptr<GroupDef>(
389 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
391 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
392 // allow empty docs for group
393 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
394 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
395 gd->addSectionsToDefinition(root->anchors);
396 gd->setRefItems(root->sli);
397 gd->setLanguage(root->lang);
398 if (root->groupDocType==Entry::GROUPDOC_NORMAL)
400 gd->enableGroupGraph(root->groupGraph);
405 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
408 static void buildGroupList(const Entry *root)
410 // --- first process only local groups
411 // first process the @defgroups blocks
412 buildGroupListFiltered(root,FALSE,FALSE);
413 // then process the @addtogroup, @weakgroup blocks
414 buildGroupListFiltered(root,TRUE,FALSE);
416 // --- then also process external groups
417 // first process the @defgroups blocks
418 buildGroupListFiltered(root,FALSE,TRUE);
419 // then process the @addtogroup, @weakgroup blocks
420 buildGroupListFiltered(root,TRUE,TRUE);
423 static void findGroupScope(const Entry *root)
425 if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
426 root->parent() && !root->parent()->name.isEmpty())
429 if ((gd=Doxygen::groupLinkedMap->find(root->name)))
431 QCString scope = root->parent()->name;
432 if (root->parent()->section==Entry::PACKAGEDOC_SEC)
434 scope=substitute(scope,".","::");
436 scope = stripAnonymousNamespaceScope(scope);
437 scope+="::"+gd->name();
438 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
441 gd->setGroupScope(d);
445 for (const auto &e : root->children()) findGroupScope(e.get());
448 static void organizeSubGroupsFiltered(const Entry *root,bool additional)
450 if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty())
452 AUTO_TRACE("additional={}",additional);
453 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
454 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
457 if ((gd=Doxygen::groupLinkedMap->find(root->name)))
459 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
460 addGroupToGroups(root,gd);
464 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
467 static void organizeSubGroups(const Entry *root)
469 //printf("Defining groups\n");
470 // first process the @defgroups blocks
471 organizeSubGroupsFiltered(root,FALSE);
472 //printf("Additional groups\n");
473 // then process the @addtogroup, @weakgroup blocks
474 organizeSubGroupsFiltered(root,TRUE);
477 //----------------------------------------------------------------------
479 static void buildFileList(const Entry *root)
481 if (((root->section==Entry::FILEDOC_SEC) ||
482 ((root->section & Entry::FILE_MASK) && Config_getBool(EXTRACT_ALL))) &&
483 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
487 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,root->name,ambig);
490 bool save_ambig = ambig;
491 // use the directory of the file to see if the described file is in the same
492 // directory as the describing file.
493 QCString fn = root->fileName;
494 int newIndex=fn.findRev('/');
501 fn = fn.left(newIndex)+"/"+root->name;
503 fd=findFileDef(Doxygen::inputNameLinkedMap,fn,ambig);
504 if (!fd) ambig = save_ambig;
506 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
509 //printf("Adding documentation!\n");
510 // using FALSE in setDocumentation is small hack to make sure a file
511 // is documented even if a \file command is used without further
513 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
514 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
515 fd->addSectionsToDefinition(root->anchors);
516 fd->setRefItems(root->sli);
517 fd->enableIncludeGraph(root->includeGraph);
518 fd->enableIncludedByGraph(root->includedByGraph);
519 for (const Grouping &g : root->groups)
522 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
524 if (!gd->containsFile(fd))
527 fd->makePartOfGroup(gd);
528 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
536 text.sprintf("the name '%s' supplied as "
537 "the argument in the \\file statement ",
539 if (ambig) // name is ambiguous
541 text+="matches the following input files:\n";
542 text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->name);
543 text+="Please use a more specific name by "
544 "including a (larger) part of the path!";
546 else // name is not an input file
548 text+="is not an input file";
550 warn(root->fileName,root->startLine,"%s", qPrint(text));
553 for (const auto &e : root->children()) buildFileList(e.get());
556 template<class DefMutable>
557 static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
560 (!root->doc.stripWhiteSpace().isEmpty() ||
561 !root->brief.stripWhiteSpace().isEmpty() ||
562 Config_getBool(EXTRACT_ALL)
563 ) && root->protection!=Protection::Private
566 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
568 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
569 QCString includeFile = root->includeFile;
570 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
573 includeFile=includeFile.mid(1,includeFile.length()-2);
575 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
578 includeFile=includeFile.mid(1,includeFile.length()-2);
583 // see if we need to include a verbatim copy of the header file
584 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
585 if (!includeFile.isEmpty() &&
586 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==0
588 { // explicit request
590 text.sprintf("the name '%s' supplied as "
591 "the argument of the \\class, \\struct, \\union, or \\include command ",
594 if (ambig) // name is ambiguous
596 text+="matches the following input files:\n";
597 text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->includeFile);
598 text+="Please use a more specific name by "
599 "including a (larger) part of the path!";
601 else // name is not an input file
603 text+="is not an input file";
605 warn(root->fileName,root->startLine, "%s", qPrint(text));
607 else if (includeFile.isEmpty() && ifd &&
608 // see if the file extension makes sense
609 guessSection(ifd->name())==Entry::HEADER_SEC)
610 { // implicit assumption
614 // if a file is found, we mark it as a source file.
617 QCString iName = !root->includeName.isEmpty() ?
618 root->includeName : includeFile;
619 if (!iName.isEmpty()) // user specified include file
621 if (iName.at(0)=='<') local=FALSE; // explicit override
622 else if (iName.at(0)=='"') local=TRUE;
623 if (iName.at(0)=='"' || iName.at(0)=='<')
625 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
632 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
634 iName=stripFromIncludePath(fd->absFilePath());
636 else // use name of the file containing the class definition
640 if (fd->generateSourceFile()) // generate code for header
642 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
644 else // put #include in the class documentation without link
646 cd->setIncludeFile(0,iName,local,TRUE);
653 QCString stripTemplateSpecifiers(const QCString &s)
659 for (int i=0;i<l;i++)
663 else if (c==')' && round>0) round--;
664 else if (c=='<' && round==0) count++;
669 if (c=='>' && round==0 && count>0) count--;
671 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
675 /*! returns the Definition object belonging to the first \a level levels of
676 * full qualified name \a name. Creates an artificial scope if the scope is
677 * not found and set the parent/child scope relation if the scope is found.
680 static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
682 QCString name = stripTemplateSpecifiers(name_);
683 int level = name.contains("::");
684 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
687 Definition *prevScope=Doxygen::globalScope;
691 int idx=getScopeFragment(name,p,&l);
692 if (idx==-1) return prevScope;
693 QCString nsName = name.mid(idx,l);
694 if (nsName.isEmpty()) return prevScope;
695 if (!fullScope.isEmpty()) fullScope+="::";
697 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
698 DefinitionMutable *innerScope = toDefinitionMutable(nd);
700 if (nd==0) cd = getClass(fullScope);
701 if (nd==0 && cd) // scope is a class
703 innerScope = toDefinitionMutable(cd);
705 else if (nd==0 && cd==0 && fullScope.find('<')==-1) // scope is not known and could be a namespace!
707 // introduce bogus namespace
708 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
709 NamespaceDefMutable *newNd=
710 toNamespaceDefMutable(
711 Doxygen::namespaceLinkedMap->add(fullScope,
713 "[generated]",1,1,fullScope,
714 tagInfo?tagInfo->tagName:QCString(),
715 tagInfo?tagInfo->fileName:QCString())));
718 newNd->setLanguage(lang);
719 newNd->setArtificial(TRUE);
720 // add namespace to the list
724 else // scope is a namespace
729 // make the parent/child scope relation
730 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
731 if (prevScopeMutable)
733 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
735 innerScope->setOuterScope(prevScope);
737 else // current scope is a class, so return only the namespace part...
741 // proceed to the next scope fragment
743 prevScope=toDefinition(innerScope);
749 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
750 FileDef *fileScope,const TagInfo *tagInfo)
752 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
753 Definition *resultScope=toDefinition(startScope);
754 if (resultScope==0) resultScope=Doxygen::globalScope;
755 QCString scope=stripTemplateSpecifiersFromScope(n,FALSE);
757 i1=getScopeFragment(scope,0,&l1);
760 //printf(">no fragments!\n");
764 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
766 QCString nestedNameSpecifier = scope.mid(i1,l1);
767 Definition *orgScope = resultScope;
768 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
769 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
770 //printf(" resultScope=%p\n",resultScope);
773 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
774 // also search for used namespaces
776 for (const auto &nd : fileScope->getUsedNamespaces())
778 NamespaceDef *mnd = toNamespaceDefMutable(nd);
781 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
782 if (resultScope!=0) break;
787 // for a nested class A::I in used namespace N, we get
788 // N::A::I while looking for A, so we should compare
789 // resultScope->name() against scope.left(i2+l2)
790 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
791 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
799 // also search for used classes. Complication: we haven't been able
800 // to put them in the right scope yet, because we are still resolving
801 // the scope relations!
802 // Therefore loop through all used classes and see if there is a right
803 // scope match between the used class and nestedNameSpecifier.
804 for (const auto &usedName : g_usingDeclarations)
806 //printf("Checking using class %s\n",ui.currentKey());
807 if (rightScopeMatch(usedName.c_str(),nestedNameSpecifier))
809 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
810 // so use this instead.
811 QCString fqn = QCString(usedName) + scope.right(scope.length()-p);
812 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),0);
813 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
816 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
822 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
830 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
834 std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
835 const QCString &name,
836 const ArgumentLists &tArgLists)
838 // for each scope fragment, check if it is a template and advance through
841 auto alIt = tArgLists.begin();
842 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
844 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
847 ClassDef *cd = getClass(name.left(i));
850 if (!cd->templateArguments().empty())
858 return alIt!=tArgLists.end() ?
859 std::make_unique<ArgumentList>(*alIt) :
860 std::unique_ptr<ArgumentList>();
864 ClassDef::CompoundType convertToCompoundType(int section,uint64_t specifier)
866 ClassDef::CompoundType sec=ClassDef::Class;
867 if (specifier&Entry::Struct)
868 sec=ClassDef::Struct;
869 else if (specifier&Entry::Union)
871 else if (specifier&Entry::Category)
872 sec=ClassDef::Category;
873 else if (specifier&Entry::Interface)
874 sec=ClassDef::Interface;
875 else if (specifier&Entry::Protocol)
876 sec=ClassDef::Protocol;
877 else if (specifier&Entry::Exception)
878 sec=ClassDef::Exception;
879 else if (specifier&Entry::Service)
880 sec=ClassDef::Service;
881 else if (specifier&Entry::Singleton)
882 sec=ClassDef::Singleton;
886 //case Entry::UNION_SEC:
887 case Entry::UNIONDOC_SEC:
890 //case Entry::STRUCT_SEC:
891 case Entry::STRUCTDOC_SEC:
892 sec=ClassDef::Struct;
894 //case Entry::INTERFACE_SEC:
895 case Entry::INTERFACEDOC_SEC:
896 sec=ClassDef::Interface;
898 //case Entry::PROTOCOL_SEC:
899 case Entry::PROTOCOLDOC_SEC:
900 sec=ClassDef::Protocol;
902 //case Entry::CATEGORY_SEC:
903 case Entry::CATEGORYDOC_SEC:
904 sec=ClassDef::Category;
906 //case Entry::EXCEPTION_SEC:
907 case Entry::EXCEPTIONDOC_SEC:
908 sec=ClassDef::Exception;
910 case Entry::SERVICEDOC_SEC:
911 sec=ClassDef::Service;
913 case Entry::SINGLETONDOC_SEC:
914 sec=ClassDef::Singleton;
921 static void addClassToContext(const Entry *root)
923 AUTO_TRACE("name={}",root->name);
924 FileDef *fd = root->fileDef();
927 if (root->parent()->section&Entry::SCOPE_MASK)
929 scName=root->parent()->name;
931 // name without parent's scope
932 QCString fullName = root->name;
934 // strip off any template parameters (but not those for specializations)
935 fullName=stripTemplateSpecifiersFromScope(fullName);
937 // name with scope (if not present already)
938 QCString qualifiedName = fullName;
939 if (!scName.isEmpty() && !leftScopeMatch(fullName,scName))
941 qualifiedName.prepend(scName+"::");
944 // see if we already found the class before
945 ClassDefMutable *cd = getClassMutable(qualifiedName);
947 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
952 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
953 //if (cd->templateArguments()==0)
955 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
956 // cd->setTemplateArguments(tArgList);
959 cd->setDocumentation(root->doc,root->docFile,root->docLine);
960 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
962 if ((root->spec&Entry::ForwardDecl)==0 && cd->isForwardDeclared())
964 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
965 if (root->bodyLine!=-1)
967 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
972 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && (root->spec&Entry::ForwardDecl)==0))
974 // this happens if a template class declared with @class is found
975 // before the actual definition or if a forward declaration has different template
977 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
980 cd->setTemplateArguments(*tArgList);
983 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
985 cd->setRequiresClause(root->req);
988 cd->setCompoundType(convertToCompoundType(root->section,root->spec));
990 cd->setMetaData(root->metaData);
994 ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec);
997 QCString namespaceName;
998 extractNamespaceName(fullName,className,namespaceName);
1000 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1001 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1004 QCString refFileName;
1005 const TagInfo *tagInfo = root->tagInfo();
1009 tagName = tagInfo->tagName;
1010 refFileName = tagInfo->fileName;
1011 if (fullName.find("::")!=-1)
1012 // symbols imported via tag files may come without the parent scope,
1013 // so we artificially create it here
1015 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1018 std::unique_ptr<ArgumentList> tArgList;
1019 if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) && (i=fullName.findRev('<'))!=-1)
1021 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1022 // name and template arguments here
1023 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1024 fullName=fullName.left(i);
1028 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1030 // add class to the list
1031 cd = toClassDefMutable(
1032 Doxygen::classLinkedMap->add(fullName,
1033 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1034 fullName,sec,tagName,refFileName,TRUE,root->spec&Entry::Enum) ));
1037 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1038 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1039 fmt::ptr(tagInfo),root->hidden,root->artificial);
1040 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1041 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1042 cd->setLanguage(root->lang);
1043 cd->setId(root->id);
1044 cd->setHidden(root->hidden);
1045 cd->setArtificial(root->artificial);
1046 cd->setClassSpecifier(root->spec);
1047 cd->addQualifiers(root->qualifiers);
1048 cd->setTypeConstraints(root->typeConstr);
1049 cd->enableCollaborationGraph(root->collaborationGraph);
1053 cd->setTemplateArguments(*tArgList);
1055 cd->setRequiresClause(root->req);
1056 cd->setProtection(root->protection);
1057 cd->setIsStatic(root->isStatic);
1059 // file definition containing the class cd
1060 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1063 cd->setMetaData(root->metaData);
1065 cd->insertUsedFile(fd);
1069 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1075 cd->addSectionsToDefinition(root->anchors);
1076 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1077 if ((root->spec&Entry::ForwardDecl)==0)
1079 if (cd->hasDocumentation())
1081 addIncludeFile(cd,fd,root);
1083 if (fd && (root->section & Entry::COMPOUND_MASK))
1085 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1087 fd->insertClass(cd);
1090 addClassToGroups(root,cd);
1091 ModuleManager::instance().addClassToModule(root,cd);
1092 cd->setRefItems(root->sli);
1096 //----------------------------------------------------------------------
1097 // build a list of all classes mentioned in the documentation
1098 // and all classes that have a documentation block before their definition.
1099 static void buildClassList(const Entry *root)
1102 ((root->section & Entry::COMPOUND_MASK) ||
1103 root->section==Entry::OBJCIMPL_SEC) && !root->name.isEmpty()
1107 addClassToContext(root);
1109 for (const auto &e : root->children()) buildClassList(e.get());
1112 static void buildClassDocList(const Entry *root)
1115 (root->section & Entry::COMPOUNDDOC_MASK) && !root->name.isEmpty()
1119 addClassToContext(root);
1121 for (const auto &e : root->children()) buildClassDocList(e.get());
1124 //----------------------------------------------------------------------
1125 // build a list of all classes mentioned in the documentation
1126 // and all classes that have a documentation block before their definition.
1128 static void addConceptToContext(const Entry *root)
1131 FileDef *fd = root->fileDef();
1134 if (root->parent()->section&Entry::SCOPE_MASK)
1136 scName=root->parent()->name;
1139 // name with scope (if not present already)
1140 QCString qualifiedName = root->name;
1141 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1143 qualifiedName.prepend(scName+"::");
1146 // see if we already found the concept before
1147 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1149 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1153 qualifiedName=cd->name();
1154 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1156 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1157 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1159 addIncludeFile(cd,fd,root);
1164 QCString namespaceName;
1165 extractNamespaceName(qualifiedName,className,namespaceName);
1167 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1168 qualifiedName,namespaceName,className,root->brief,root->doc);
1171 QCString refFileName;
1172 const TagInfo *tagInfo = root->tagInfo();
1175 tagName = tagInfo->tagName;
1176 refFileName = tagInfo->fileName;
1177 if (qualifiedName.find("::")!=-1)
1178 // symbols imported via tag files may come without the parent scope,
1179 // so we artificially create it here
1181 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1184 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1185 // add concept to the list
1186 cd = toConceptDefMutable(
1187 Doxygen::conceptLinkedMap->add(qualifiedName,
1188 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1189 qualifiedName,tagName,refFileName)));
1192 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1193 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1194 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1195 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1196 cd->setLanguage(root->lang);
1197 cd->setId(root->id);
1198 cd->setHidden(root->hidden);
1199 cd->setGroupId(root->mGrpId);
1202 cd->setTemplateArguments(*tArgList);
1204 cd->setInitializer(root->initializer.str().c_str());
1205 // file definition containing the class cd
1206 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1208 addIncludeFile(cd,fd,root);
1210 // also add namespace to the correct structural context
1211 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,0,tagInfo);
1212 if (d && d->definitionType()==Definition::TypeNamespace)
1214 DefinitionMutable *dm = toDefinitionMutable(d);
1217 dm->addInnerCompound(cd);
1219 cd->setOuterScope(d);
1224 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1230 cd->addSectionsToDefinition(root->anchors);
1233 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1235 fd->insertConcept(cd);
1237 addConceptToGroups(root,cd);
1238 ModuleManager::instance().addConceptToModule(root,cd);
1239 cd->setRefItems(root->sli);
1243 static void findModuleDocumentation(const Entry *root)
1245 if (root->section==Entry::MODULEDOC_SEC)
1248 ModuleManager::instance().addDocs(root);
1250 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1253 static void buildConceptList(const Entry *root)
1255 if (root->section & Entry::CONCEPT_SEC)
1258 addConceptToContext(root);
1260 for (const auto &e : root->children()) buildConceptList(e.get());
1263 static void buildConceptDocList(const Entry *root)
1265 if (root->section & Entry::CONCEPTDOC_SEC)
1268 addConceptToContext(root);
1270 for (const auto &e : root->children()) buildConceptDocList(e.get());
1273 // This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1274 // (same also works for variable and functions because of logic in MemberGroup::insertMember)
1275 static void distributeConceptGroups()
1278 for (const auto &cd : *Doxygen::conceptLinkedMap)
1280 if (cd->groupId()!=DOX_NOGROUP)
1282 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1284 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1285 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1287 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1290 for (const auto &gd : cd->partOfGroups())
1294 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1295 ocdm->makePartOfGroup(gd);
1296 gd->addConcept(ocd.get());
1306 //----------------------------------------------------------------------
1308 static void resolveClassNestingRelations()
1310 ClassDefSet visitedClasses;
1320 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1321 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1322 QCString aliasFullName;
1323 std::unique_ptr<ClassDef> aliasCd;
1324 DefinitionMutable *aliasContext;
1326 std::vector<ClassAlias> aliases;
1327 for (const auto &icd : *Doxygen::classLinkedMap)
1329 ClassDefMutable *cd = toClassDefMutable(icd.get());
1330 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1332 QCString name = stripAnonymousNamespaceScope(icd->name());
1333 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1334 // also add class to the correct structural context
1335 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,
1336 name,icd->getFileDef(),0);
1339 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1340 DefinitionMutable *dm = toDefinitionMutable(d);
1343 dm->addInnerCompound(cd);
1345 cd->setOuterScope(d);
1347 // for inline namespace add an alias of the class to the outer scope
1348 while (d->definitionType()==Definition::TypeNamespace)
1350 NamespaceDef *nd = toNamespaceDef(d);
1351 //printf("nd->isInline()=%d\n",nd->isInline());
1352 if (nd && nd->isInline())
1354 d = d->getOuterScope();
1357 dm = toDefinitionMutable(d);
1360 auto aliasCd = createClassDefAlias(d,cd);
1361 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1362 aliases.push_back(ClassAlias(aliasFullName,std::move(aliasCd),dm));
1363 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1373 visitedClasses.insert(icd.get());
1378 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1383 for (auto &alias : aliases)
1385 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1388 alias.aliasContext->addInnerCompound(aliasCd);
1393 //give warnings for unresolved compounds
1394 for (const auto &icd : *Doxygen::classLinkedMap)
1396 ClassDefMutable *cd = toClassDefMutable(icd.get());
1397 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1399 QCString name = stripAnonymousNamespaceScope(cd->name());
1400 //printf("processing unresolved=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1401 /// create the scope artificially
1402 // anyway, so we can at least relate scopes properly.
1403 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),0);
1404 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1405 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1406 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1407 // also avoid warning for stuff imported via a tagfile.
1409 DefinitionMutable *dm = toDefinitionMutable(d);
1412 dm->addInnerCompound(cd);
1414 cd->setOuterScope(d);
1415 warn(cd->getDefFileName(),cd->getDefLine(),
1416 "Internal inconsistency: scope for class %s not "
1417 "found!",qPrint(name)
1424 void distributeClassGroupRelations()
1426 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1427 //if (!inlineGroupedClasses) return;
1428 //printf("** distributeClassGroupRelations()\n");
1430 ClassDefSet visitedClasses;
1431 for (const auto &cd : *Doxygen::classLinkedMap)
1433 //printf("Checking %s\n",qPrint(cd->name()));
1434 // distribute the group to nested classes as well
1435 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1437 //printf(" Candidate for merging\n");
1438 GroupDef *gd = cd->partOfGroups().front();
1439 for (auto &ncd : cd->getClasses())
1441 ClassDefMutable *ncdm = toClassDefMutable(ncd);
1442 if (ncdm && ncdm->partOfGroups().empty())
1444 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1445 // gd->groupTitle());
1446 ncdm->makePartOfGroup(gd);
1450 visitedClasses.insert(cd.get()); // only visit every class once
1455 //----------------------------
1457 static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1459 QCString fullName = removeAnonymousScopes(templ->name());
1460 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1461 fullName+="."+fieldName;
1463 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1464 ClassDefMutable *cd = toClassDefMutable(
1465 Doxygen::classLinkedMap->add(fullName,
1466 createClassDef(templ->getDefFileName(),
1467 templ->getDefLine(),
1468 templ->getDefColumn(),
1470 templ->compoundType())));
1473 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1474 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1475 cd->setLanguage(templ->getLanguage());
1476 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1477 cd->setBodyDef(templ->getBodyDef());
1479 cd->setOuterScope(rootCd->getOuterScope());
1480 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1482 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1485 outerScope->addInnerCompound(cd);
1489 FileDef *fd = templ->getFileDef();
1493 fd->insertClass(cd);
1495 for (const auto &gd : rootCd->partOfGroups())
1497 cd->makePartOfGroup(gd);
1498 const_cast<GroupDef*>(gd)->addClass(cd);
1501 MemberList *ml = templ->getMemberList(MemberListType_pubAttribs);
1504 for (const auto &md : *ml)
1506 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1507 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1508 md->typeString(),md->name(),md->argsString(),md->excpString(),
1509 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1511 ArgumentList(),ArgumentList(),"");
1512 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1513 imd->setMemberClass(cd);
1514 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1515 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1516 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1517 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1518 imd->setMemberGroupId(md->getMemberGroupId());
1519 imd->setInitializer(md->initializer());
1520 imd->setRequiresClause(md->requiresClause());
1521 imd->setMaxInitLines(md->initializerLines());
1522 imd->setBitfields(md->bitfieldString());
1523 imd->setLanguage(md->getLanguage());
1524 cd->insertMember(imd);
1525 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1526 mn->push_back(std::move(newMd));
1533 /** Look through the members of class \a cd and its public members.
1534 * If there is a member m of a tag less struct/union,
1535 * then we create a duplicate of the struct/union with the name of the
1536 * member to identify it.
1537 * So if cd has name S, then the tag less struct/union will get name S.m
1538 * Since tag less structs can be nested we need to call this function
1539 * recursively. Later on we need to patch the member types so we keep
1540 * track of the hierarchy of classes we create.
1542 static void processTagLessClasses(const ClassDef *rootCd,
1544 ClassDefMutable *tagParentCd,
1545 const QCString &prefix,int count)
1547 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1548 //printf("checking members for %s\n",qPrint(cd->name()));
1549 if (tagParentCd && !cd->getClasses().empty())
1551 MemberList *ml = cd->getMemberList(MemberListType_pubAttribs);
1554 for (const auto &md : *ml)
1556 QCString type = md->typeString();
1557 if (type.find("::@")!=-1) // member of tag less struct/union
1559 for (const auto &icd : cd->getClasses())
1561 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1562 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1563 if (type.find(icd->name())!=-1) // matching tag less struct/union
1565 QCString name = md->name();
1566 if (md->isAnonymous()) name = "__unnamed" + name.right(name.length()-1)+"__";
1567 if (!prefix.isEmpty()) name.prepend(prefix+".");
1568 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1569 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1572 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1573 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1574 ncd->setTagLessReference(icd);
1576 // replace tag-less type for generated/original member
1577 // by newly created class name.
1578 // note the difference between changing cd and tagParentCd.
1579 // for the initial call this is the same pointer, but for
1580 // recursive calls cd is the original tag-less struct (of which
1581 // there is only one instance) and tagParentCd is the newly
1582 // generated tagged struct of which there can be multiple instances!
1583 MemberList *pml = tagParentCd->getMemberList(MemberListType_pubAttribs);
1586 for (const auto &pmd : *pml)
1588 MemberDefMutable *pmdm = toMemberDefMutable(pmd);
1589 if (pmdm && pmd->name()==md->name())
1591 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1592 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1605 static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1607 for (const auto &icd : cd->getClasses())
1609 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1611 findTagLessClasses(candidates,icd);
1615 ClassDefMutable *cdm = toClassDefMutable(cd);
1618 candidates.push_back(cdm);
1622 static void findTagLessClasses()
1624 std::vector<ClassDefMutable *> candidates;
1625 for (auto &cd : *Doxygen::classLinkedMap)
1627 Definition *scope = cd->getOuterScope();
1628 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1630 findTagLessClasses(candidates,cd.get());
1634 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1635 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1636 for (auto &cd : candidates)
1638 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1643 //----------------------------------------------------------------------
1644 // build a list of all namespaces mentioned in the documentation
1645 // and all namespaces that have a documentation block before their definition.
1646 static void buildNamespaceList(const Entry *root)
1649 (root->section==Entry::NAMESPACE_SEC ||
1650 root->section==Entry::NAMESPACEDOC_SEC ||
1651 root->section==Entry::PACKAGEDOC_SEC
1653 !root->name.isEmpty()
1656 AUTO_TRACE("name={}",root->name);
1658 QCString fName = root->name;
1659 if (root->section==Entry::PACKAGEDOC_SEC)
1661 fName=substitute(fName,".","::");
1664 QCString fullName = stripAnonymousNamespaceScope(fName);
1665 if (!fullName.isEmpty())
1667 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1668 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1669 if (ndi) // existing namespace
1671 NamespaceDefMutable *nd = toNamespaceDefMutable(ndi);
1672 if (nd) // non-inline namespace
1674 AUTO_TRACE_ADD("Existing namespace");
1675 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1676 nd->setName(fullName); // change name to match docs
1677 nd->addSectionsToDefinition(root->anchors);
1678 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1679 if (nd->getLanguage()==SrcLangExt_Unknown)
1681 nd->setLanguage(root->lang);
1683 if (root->tagInfo()==0 && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1684 // if we previously found namespace nd in a tag file and now we find a
1685 // documented namespace with the same name in the project, then remove
1686 // the tag file reference
1688 nd->setReference("");
1689 nd->setFileName(fullName);
1691 nd->setMetaData(root->metaData);
1693 // file definition containing the namespace nd
1694 FileDef *fd=root->fileDef();
1695 if (nd->isArtificial())
1697 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1698 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1700 // insert the namespace in the file definition
1701 if (fd) fd->insertNamespace(nd);
1702 addNamespaceToGroups(root,nd);
1703 nd->setRefItems(root->sli);
1706 else // fresh namespace
1709 QCString tagFileName;
1710 const TagInfo *tagInfo = root->tagInfo();
1713 tagName = tagInfo->tagName;
1714 tagFileName = tagInfo->fileName;
1716 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1717 // add namespace to the list
1718 NamespaceDefMutable *nd = toNamespaceDefMutable(
1719 Doxygen::namespaceLinkedMap->add(fullName,
1720 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1721 root->startColumn,fullName,tagName,tagFileName,
1722 root->type,root->spec&Entry::Published)));
1725 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1726 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1727 nd->addSectionsToDefinition(root->anchors);
1728 nd->setHidden(root->hidden);
1729 nd->setArtificial(root->artificial);
1730 nd->setLanguage(root->lang);
1731 nd->setId(root->id);
1732 nd->setMetaData(root->metaData);
1733 nd->setInline((root->spec&Entry::Inline)!=0);
1734 nd->setExported(root->exported);
1736 addNamespaceToGroups(root,nd);
1737 nd->setRefItems(root->sli);
1739 // file definition containing the namespace nd
1740 FileDef *fd=root->fileDef();
1741 // insert the namespace in the file definition
1742 if (fd) fd->insertNamespace(nd);
1744 // the empty string test is needed for extract all case
1745 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1746 nd->insertUsedFile(fd);
1747 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1750 // also add namespace to the correct structural context
1751 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,0,tagInfo);
1752 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1753 if (d==0) // we didn't find anything, create the scope artificially
1754 // anyway, so we can at least relate scopes properly.
1756 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1757 DefinitionMutable *dm = toDefinitionMutable(d);
1760 dm->addInnerCompound(nd);
1762 nd->setOuterScope(d);
1763 // TODO: Due to the order in which the tag file is written
1764 // a nested class can be found before its parent!
1768 DefinitionMutable *dm = toDefinitionMutable(d);
1771 dm->addInnerCompound(nd);
1773 nd->setOuterScope(d);
1774 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1775 while (d->definitionType()==Definition::TypeNamespace)
1777 NamespaceDef *pnd = toNamespaceDef(d);
1778 if (pnd && pnd->isInline())
1780 d = d->getOuterScope();
1783 dm = toDefinitionMutable(d);
1786 auto aliasNd = createNamespaceDefAlias(d,nd);
1787 dm->addInnerCompound(aliasNd.get());
1788 QCString aliasName = aliasNd->name();
1789 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1790 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1808 for (const auto &e : root->children()) buildNamespaceList(e.get());
1811 //----------------------------------------------------------------------
1813 static NamespaceDef *findUsedNamespace(const LinkedRefMap<NamespaceDef> &unl,
1814 const QCString &name)
1816 NamespaceDef *usingNd =0;
1817 for (auto &und : unl)
1819 QCString uScope=und->name()+"::";
1820 usingNd = getResolvedNamespace(uScope+name);
1821 if (usingNd!=0) break;
1826 static void findUsingDirectives(const Entry *root)
1828 if (root->section==Entry::USINGDIR_SEC)
1830 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1831 QCString name=substitute(root->name,".","::");
1832 if (name.endsWith("::"))
1834 name=name.left(name.length()-2);
1836 if (!name.isEmpty())
1838 NamespaceDef *usingNd = 0;
1839 NamespaceDefMutable *nd = 0;
1840 FileDef *fd = root->fileDef();
1843 // see if the using statement was found inside a namespace or inside
1844 // the global file scope.
1845 if (root->parent() && root->parent()->section==Entry::NAMESPACE_SEC &&
1846 (fd==0 || fd->getLanguage()!=SrcLangExt_Java) // not a .java file
1849 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1850 if (!nsName.isEmpty())
1852 nd = getResolvedNamespaceMutable(nsName);
1856 // find the scope in which the 'using' namespace is defined by prepending
1857 // the possible scopes in which the using statement was found, starting
1858 // with the most inner scope and going to the most outer scope (i.e.
1860 int scopeOffset = nsName.length();
1863 QCString scope=scopeOffset>0 ?
1864 nsName.left(scopeOffset)+"::" : QCString();
1865 usingNd = getResolvedNamespace(scope+name);
1866 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1871 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1875 } while (scopeOffset>=0 && usingNd==0);
1877 if (usingNd==0 && nd) // not found, try used namespaces in this scope
1878 // or in one of the parent namespace scopes
1880 const NamespaceDefMutable *pnd = nd;
1881 while (pnd && usingNd==0)
1883 // also try with one of the used namespaces found earlier
1884 usingNd = toNamespaceDefMutable(findUsedNamespace(pnd->getUsedNamespaces(),name));
1887 Definition *s = pnd->getOuterScope();
1888 if (s && s->definitionType()==Definition::TypeNamespace)
1890 pnd = toNamespaceDefMutable(toNamespaceDef(s));
1898 if (usingNd==0 && fd) // still nothing, also try used namespace in the
1901 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1904 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1906 // add the namespace the correct scope
1909 //printf("using fd=%p nd=%p\n",fd,nd);
1912 //printf("Inside namespace %s\n",qPrint(nd->name()));
1913 nd->addUsingDirective(usingNd);
1917 //printf("Inside file %s\n",qPrint(fd->name()));
1918 fd->addUsingDirective(usingNd);
1921 else // unknown namespace, but add it anyway.
1923 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1924 // add namespace to the list
1925 nd = toNamespaceDefMutable(
1926 Doxygen::namespaceLinkedMap->add(name,
1927 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1930 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1931 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1932 nd->addSectionsToDefinition(root->anchors);
1933 nd->setHidden(root->hidden);
1934 nd->setArtificial(TRUE);
1935 nd->setLanguage(root->lang);
1936 nd->setId(root->id);
1937 nd->setMetaData(root->metaData);
1938 nd->setInline((root->spec&Entry::Inline)!=0);
1939 nd->setExported(root->exported);
1941 for (const Grouping &g : root->groups)
1944 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1945 gd->addNamespace(nd);
1948 // insert the namespace in the file definition
1951 fd->insertNamespace(nd);
1952 fd->addUsingDirective(nd);
1955 // the empty string test is needed for extract all case
1956 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1957 nd->insertUsedFile(fd);
1958 nd->setRefItems(root->sli);
1963 for (const auto &e : root->children()) findUsingDirectives(e.get());
1966 //----------------------------------------------------------------------
1968 static void buildListOfUsingDecls(const Entry *root)
1970 if (root->section==Entry::USINGDECL_SEC &&
1971 !(root->parent()->section&Entry::COMPOUND_MASK) // not a class/struct member
1974 QCString name = substitute(root->name,".","::");
1975 g_usingDeclarations.insert(name.str());
1977 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
1981 static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
1983 if (root->section==Entry::USINGDECL_SEC &&
1984 !(root->parent()->section&Entry::COMPOUND_MASK) && // not a class/struct member
1985 (!filterPythonPackages || (root->lang==SrcLangExt_Python && root->fileName.endsWith("__init__.py")))
1988 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {:#10x}",
1989 root->name,root->startLine,root->fileName,root->parent()->section);
1990 if (!root->name.isEmpty())
1992 ClassDefMutable *usingCd = 0;
1993 NamespaceDefMutable *nd = 0;
1994 FileDef *fd = root->fileDef();
1997 // see if the using statement was found inside a namespace or inside
1998 // the global file scope.
1999 if (root->parent()->section == Entry::NAMESPACE_SEC)
2001 scName=root->parent()->name;
2002 if (!scName.isEmpty())
2004 nd = getResolvedNamespaceMutable(scName);
2008 // Assume the using statement was used to import a class.
2009 // Find the scope in which the 'using' namespace is defined by prepending
2010 // the possible scopes in which the using statement was found, starting
2011 // with the most inner scope and going to the most outer scope (i.e.
2014 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2015 usingCd = getClassMutable(name); // try direct lookup first, this is needed to get
2016 // builtin STL classes to properly resolve, e.g.
2017 // vector -> std::vector
2020 SymbolResolver resolver(fd);
2021 usingCd = resolver.resolveClassMutable(nd,name); // try via resolving (see also bug757509)
2025 usingCd = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(name)); // check if it is already hidden
2028 if (usingCd==0) // definition not in the input => add an artificial class
2030 AUTO_TRACE_ADD("New using class '{}' (sec={:10x})! #tArgLists={}",
2031 name,root->section,root->tArgLists.size());
2032 usingCd = toClassDefMutable(
2033 Doxygen::hiddenClassLinkedMap->add(name,
2034 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2037 usingCd->setArtificial(TRUE);
2038 usingCd->setLanguage(root->lang);
2043 AUTO_TRACE_ADD("Found used class '{}' in scope='{}'",
2044 usingCd->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2049 nd->addUsingDeclaration(usingCd);
2053 fd->addUsingDeclaration(usingCd);
2057 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2060 //----------------------------------------------------------------------
2062 static void findUsingDeclImports(const Entry *root)
2064 if (root->section==Entry::USINGDECL_SEC &&
2065 (root->parent()->section&Entry::COMPOUND_MASK) // in a class/struct member
2068 AUTO_TRACE("Found using declaration '{}' inside section {:#10x}", root->name, root->parent()->section);
2069 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2070 fullName=stripAnonymousNamespaceScope(fullName);
2071 fullName=stripTemplateSpecifiersFromScope(fullName);
2072 ClassDefMutable *cd = getClassMutable(fullName);
2075 AUTO_TRACE_ADD("found class '{}'",cd->name());
2076 int i=root->name.find("::");
2079 QCString scope=root->name.left(i);
2080 QCString memName=root->name.right(root->name.length()-i-2);
2081 SymbolResolver resolver;
2082 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2085 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2086 const MemberNameInfoLinkedMap &mnlm=bcd->memberNameInfoLinkedMap();
2087 const MemberNameInfo *mni = mnlm.find(memName);
2090 for (auto &mi : *mni)
2092 const MemberDef *md = mi->memberDef();
2093 if (md && md->protection()!=Protection::Private)
2095 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2096 QCString fileName = root->fileName;
2097 if (fileName.isEmpty() && root->tagInfo())
2099 fileName = root->tagInfo()->tagName;
2101 const ArgumentList &templAl = md->templateArguments();
2102 const ArgumentList &al = md->argumentList();
2103 auto newMd = createMemberDef(
2104 fileName,root->startLine,root->startColumn,
2105 md->typeString(),memName,md->argsString(),
2106 md->excpString(),root->protection,root->virt,
2107 md->isStatic(),Relationship::Member,md->memberType(),
2108 templAl,al,root->metaData
2110 auto newMmd = toMemberDefMutable(newMd.get());
2111 newMmd->setMemberClass(cd);
2112 cd->insertMember(newMd.get());
2113 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2115 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2116 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2117 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2121 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2122 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2123 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2125 newMmd->setDefinition(md->definition());
2126 newMmd->enableCallGraph(root->callGraph);
2127 newMmd->enableCallerGraph(root->callerGraph);
2128 newMmd->enableReferencedByRelation(root->referencedByRelation);
2129 newMmd->enableReferencesRelation(root->referencesRelation);
2130 newMmd->addQualifiers(root->qualifiers);
2131 newMmd->setBitfields(md->bitfieldString());
2132 newMmd->addSectionsToDefinition(root->anchors);
2133 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2134 newMmd->setBodyDef(md->getBodyDef());
2135 newMmd->setInitializer(md->initializer());
2136 newMmd->setRequiresClause(md->requiresClause());
2137 newMmd->setMaxInitLines(md->initializerLines());
2138 newMmd->setMemberGroupId(root->mGrpId);
2139 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2140 newMmd->setLanguage(root->lang);
2141 newMmd->setId(root->id);
2142 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2143 mn->push_back(std::move(newMd));
2152 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2155 //----------------------------------------------------------------------
2157 static void findIncludedUsingDirectives()
2159 FileDefSet visitedFiles;
2160 // then recursively add using directives found in #include files
2161 // to files that have not been visited.
2162 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2164 for (const auto &fd : *fn)
2166 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2167 fd->addIncludedUsingDirectives(visitedFiles);
2172 //----------------------------------------------------------------------
2174 static MemberDef *addVariableToClass(
2176 ClassDefMutable *cd,
2178 const QCString &type,
2179 const QCString &name,
2180 const QCString &args,
2182 MemberDef *fromAnnMemb,
2184 Relationship related)
2186 QCString qualScope = cd->qualifiedNameWithTemplateParameters();
2187 QCString scopeSeparator="::";
2188 SrcLangExt lang = cd->getLanguage();
2189 if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2191 qualScope = substitute(qualScope,"::",".");
2194 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2195 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2198 if (!type.isEmpty())
2200 if (related!=Relationship::Member || mtype==MemberType_Friend || Config_getBool(HIDE_SCOPE_NAMES))
2202 if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2204 def="using "+name+" = "+type.mid(7);
2208 def=type+" "+name+args;
2213 if (root->spec&Entry::Alias) // turn 'typedef B C::A' into 'using C::A = B'
2215 def="using "+qualScope+scopeSeparator+name+" = "+type.mid(7);
2219 def=type+" "+qualScope+scopeSeparator+name+args;
2225 if (Config_getBool(HIDE_SCOPE_NAMES))
2231 def=qualScope+scopeSeparator+name+args;
2234 def.stripPrefix("static ");
2236 // see if the member is already found in the same scope
2237 // (this may be the case for a static member that is initialized
2238 // outside the class)
2239 MemberName *mn=Doxygen::memberNameLinkedMap->find(name);
2242 for (const auto &imd : *mn)
2244 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2245 // md->getClassDef(),cd,qPrint(type),md->typeString());
2246 MemberDefMutable *md = toMemberDefMutable(imd.get());
2248 md->getClassDef()==cd &&
2249 removeRedundantWhiteSpace(type)==md->typeString())
2250 // member already in the scope
2253 if (root->lang==SrcLangExt_ObjC &&
2254 root->mtype==MethodTypes::Property &&
2255 md->memberType()==MemberType_Variable)
2256 { // Objective-C 2.0 property
2257 // turn variable into a property
2258 md->setProtection(root->protection);
2259 cd->reclassifyMember(md,MemberType_Property);
2261 addMemberDocs(root,md,def,0,FALSE,root->spec);
2262 AUTO_TRACE_ADD("Member already found!");
2268 QCString fileName = root->fileName;
2269 if (fileName.isEmpty() && root->tagInfo())
2271 fileName = root->tagInfo()->tagName;
2274 // new member variable, typedef or enum value
2275 auto md = createMemberDef(
2276 fileName,root->startLine,root->startColumn,
2277 type,name,args,root->exception,
2278 prot,Specifier::Normal,root->isStatic,related,
2279 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2280 ArgumentList(), root->metaData);
2281 auto mmd = toMemberDefMutable(md.get());
2282 mmd->setTagInfo(root->tagInfo());
2283 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2284 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2285 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2286 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2287 mmd->setDefinition(def);
2288 mmd->setBitfields(root->bitfields);
2289 mmd->addSectionsToDefinition(root->anchors);
2290 mmd->setFromAnonymousScope(fromAnnScope);
2291 mmd->setFromAnonymousMember(fromAnnMemb);
2292 //md->setIndentDepth(indentDepth);
2293 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2294 std::string init = root->initializer.str();
2295 mmd->setInitializer(init.c_str());
2296 mmd->setMaxInitLines(root->initLines);
2297 mmd->setMemberGroupId(root->mGrpId);
2298 mmd->setMemberSpecifiers(root->spec);
2299 mmd->setReadAccessor(root->read);
2300 mmd->setWriteAccessor(root->write);
2301 mmd->enableCallGraph(root->callGraph);
2302 mmd->enableCallerGraph(root->callerGraph);
2303 mmd->enableReferencedByRelation(root->referencedByRelation);
2304 mmd->enableReferencesRelation(root->referencesRelation);
2305 mmd->setHidden(root->hidden);
2306 mmd->setArtificial(root->artificial);
2307 mmd->setLanguage(root->lang);
2308 mmd->setId(root->id);
2309 addMemberToGroups(root,md.get());
2310 ModuleManager::instance().addMemberToModule(root,md.get());
2311 mmd->setBodyDef(root->fileDef());
2312 mmd->addQualifiers(root->qualifiers);
2314 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2315 cd->insertMember(md.get());
2316 mmd->setRefItems(root->sli);
2318 //TODO: insert FileDef instead of filename strings.
2319 cd->insertUsedFile(root->fileDef());
2320 root->markAsProcessed();
2322 // add the member to the global list
2323 MemberDef *result = md.get();
2324 mn = Doxygen::memberNameLinkedMap->add(name);
2325 mn->push_back(std::move(md));
2330 //----------------------------------------------------------------------
2332 static MemberDef *addVariableToFile(
2335 const QCString &scope,
2336 const QCString &type,
2337 const QCString &name,
2338 const QCString &args,
2340 MemberDef *fromAnnMemb)
2342 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2343 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2345 FileDef *fd = root->fileDef();
2347 // see if we have a typedef that should hide a struct or union
2348 if (mtype==MemberType_Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2350 QCString ttype = type;
2351 ttype.stripPrefix("typedef ");
2352 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2354 static const reg::Ex re(R"(\a\w*)");
2356 std::string typ = ttype.str();
2357 if (reg::search(typ,match,re))
2359 QCString typeValue = match.str();
2360 ClassDefMutable *cd = getClassMutable(typeValue);
2363 // this typedef should hide compound name cd, so we
2364 // change the name that is displayed from cd.
2365 cd->setClassName(name);
2366 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2367 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2374 // see if the function is inside a namespace
2375 NamespaceDefMutable *nd = 0;
2376 if (!scope.isEmpty())
2378 if (scope.find('@')!=-1) return 0; // anonymous scope!
2379 nd = getResolvedNamespaceMutable(scope);
2383 // determine the definition of the global variable
2384 if (nd && !nd->isAnonymous() &&
2385 !Config_getBool(HIDE_SCOPE_NAMES)
2387 // variable is inside a namespace, so put the scope before the name
2389 SrcLangExt lang = nd->getLanguage();
2390 QCString sep=getLanguageSpecificSeparator(lang);
2392 if (!type.isEmpty())
2394 if (root->spec&Entry::Alias) // turn 'typedef B NS::A' into 'using NS::A = B'
2396 def="using "+nd->name()+sep+name+" = "+type;
2398 else // normal member
2400 def=type+" "+nd->name()+sep+name+args;
2405 def=nd->name()+sep+name+args;
2410 if (!type.isEmpty() && !root->name.isEmpty())
2412 if (name.at(0)=='@') // dummy variable representing anonymous union
2418 if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2420 def="using "+root->name+" = "+type.mid(7);
2422 else // normal member
2424 def=type+" "+name+args;
2433 def.stripPrefix("static ");
2435 MemberName *mn=Doxygen::functionNameLinkedMap->find(name);
2438 //QCString nscope=removeAnonymousScopes(scope);
2439 //NamespaceDef *nd=0;
2440 //if (!nscope.isEmpty())
2441 if (!scope.isEmpty())
2443 nd = getResolvedNamespaceMutable(scope);
2445 for (const auto &imd : *mn)
2447 MemberDefMutable *md = toMemberDefMutable(imd.get());
2449 ((nd==0 && md->getNamespaceDef()==0 && md->getFileDef() &&
2450 root->fileName==md->getFileDef()->absFilePath()
2451 ) // both variable names in the same file
2452 || (nd!=0 && md->getNamespaceDef()==nd) // both in same namespace
2454 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2455 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2457 // variable already in the scope
2459 bool isPHPArray = md->getLanguage()==SrcLangExt_PHP &&
2460 md->argsString()!=args &&
2462 bool staticsInDifferentFiles =
2463 root->isStatic && md->isStatic() &&
2464 root->fileName!=md->getDefFileName();
2466 if (md->getFileDef() &&
2467 !isPHPArray && // not a php array
2468 !staticsInDifferentFiles
2470 // not a php array variable
2472 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2473 addMemberDocs(root,md,def,0,FALSE,root->spec);
2474 md->setRefItems(root->sli);
2475 // if md is a variable forward declaration and root is the definition that
2476 // turn md into the definition
2477 if (!root->explicitExternal && md->isExternal())
2479 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2480 md->setExplicitExternal(FALSE,root->fileName,root->startLine,root->startColumn);
2482 // if md is the definition and root point at a declaration, then add the
2484 else if (root->explicitExternal && !md->isExternal())
2486 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2494 QCString fileName = root->fileName;
2495 if (fileName.isEmpty() && root->tagInfo())
2497 fileName = root->tagInfo()->tagName;
2500 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2501 // new global variable, enum value or typedef
2502 auto md = createMemberDef(
2503 fileName,root->startLine,root->startColumn,
2504 type,name,args,QCString(),
2505 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2506 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2507 root->argList, root->metaData);
2508 auto mmd = toMemberDefMutable(md.get());
2509 mmd->setTagInfo(root->tagInfo());
2510 mmd->setMemberSpecifiers(root->spec);
2511 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2512 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2513 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2514 mmd->addSectionsToDefinition(root->anchors);
2515 mmd->setFromAnonymousScope(fromAnnScope);
2516 mmd->setFromAnonymousMember(fromAnnMemb);
2517 std::string init = root->initializer.str();
2518 mmd->setInitializer(init.c_str());
2519 mmd->setMaxInitLines(root->initLines);
2520 mmd->setMemberGroupId(root->mGrpId);
2521 mmd->setDefinition(def);
2522 mmd->setLanguage(root->lang);
2523 mmd->setId(root->id);
2524 mmd->enableCallGraph(root->callGraph);
2525 mmd->enableCallerGraph(root->callerGraph);
2526 mmd->enableReferencedByRelation(root->referencedByRelation);
2527 mmd->enableReferencesRelation(root->referencesRelation);
2528 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2529 mmd->addQualifiers(root->qualifiers);
2530 //md->setOuterScope(fd);
2531 if (!root->explicitExternal)
2533 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2534 mmd->setBodyDef(fd);
2536 addMemberToGroups(root,md.get());
2537 ModuleManager::instance().addMemberToModule(root,md.get());
2539 mmd->setRefItems(root->sli);
2540 if (nd && !nd->isAnonymous())
2542 mmd->setNamespace(nd);
2543 nd->insertMember(md.get());
2546 // add member to the file (we do this even if we have already inserted
2547 // it into the namespace.
2550 mmd->setFileDef(fd);
2551 fd->insertMember(md.get());
2554 root->markAsProcessed();
2556 // add member definition to the list of globals
2557 MemberDef *result = md.get();
2558 mn = Doxygen::functionNameLinkedMap->add(name);
2559 mn->push_back(std::move(md));
2564 /*! See if the return type string \a type is that of a function pointer
2565 * \returns -1 if this is not a function pointer variable or
2566 * the index at which the closing brace of (...*name) was found.
2568 static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=0)
2570 AUTO_TRACE("type='{}' lang={}",type,lang);
2571 if (lang == SrcLangExt_Fortran || lang == SrcLangExt_VHDL)
2573 return -1; // Fortran and VHDL do not have function pointers
2576 static const reg::Ex re(R"(\([^)]*[*^][^)]*\))");
2578 size_t i=std::string::npos;
2580 if (reg::search(type,match,re)) // contains (...*...)
2582 i = match.position();
2585 size_t bb=type.find('<');
2586 size_t be=type.rfind('>');
2588 if (!type.empty() && // return type is non-empty
2589 i!=std::string::npos && // contains (...*...)
2590 type.find("operator")==std::string::npos && // not an operator
2591 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2592 // not a function pointer return type
2593 !(bb<i && i<be) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2596 if (pLength) *pLength=static_cast<int>(l);
2597 //printf("findFunctionPtr=%d\n",(int)i);
2598 AUTO_TRACE_EXIT("result={}",i);
2599 return static_cast<int>(i);
2603 //printf("findFunctionPtr=%d\n",-1);
2604 AUTO_TRACE_EXIT("result=-1");
2610 /*! Returns TRUE iff \a type is a class within scope \a context.
2611 * Used to detect variable declarations that look like function prototypes.
2613 static bool isVarWithConstructor(const Entry *root)
2615 bool result = false;
2616 bool typeIsClass = false;
2617 bool typePtrType = false;
2619 Definition *ctx = 0;
2620 FileDef *fd = root->fileDef();
2622 SymbolResolver resolver(fd);
2624 AUTO_TRACE("isVarWithConstructor({})",root->name);
2625 if (root->parent()->section & Entry::COMPOUND_MASK)
2628 AUTO_TRACE_EXIT("inside class: result={}",result);
2631 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2632 { // inside a .c file
2634 AUTO_TRACE_EXIT("inside C file: result={}",result);
2637 if (root->type.isEmpty())
2640 AUTO_TRACE_EXIT("no type: result={}",result);
2643 if (!root->parent()->name.isEmpty())
2645 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2648 // remove qualifiers
2649 findAndRemoveWord(type,"const");
2650 findAndRemoveWord(type,"static");
2651 findAndRemoveWord(type,"volatile");
2652 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2655 typeIsClass = resolver.resolveClass(ctx,type)!=0;
2656 if (!typeIsClass && (ti=type.find('<'))!=-1)
2658 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=0;
2661 if (typeIsClass) // now we still have to check if the arguments are
2662 // types or values. Since we do not have complete type info
2663 // we need to rely on heuristics :-(
2665 if (root->argList.empty())
2667 result=FALSE; // empty arg list -> function prototype.
2668 AUTO_TRACE_EXIT("empty arg list: result={}",result);
2671 for (const Argument &a : root->argList)
2673 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2675 if (!a.name.isEmpty() || !a.defval.isEmpty())
2677 std::string name = a.name.str();
2678 if (reg::search(name,match,initChars) && match.position()==0)
2684 result=FALSE; // arg has (type,name) pair -> function prototype
2686 AUTO_TRACE_EXIT("function prototype: result={}",result);
2689 if (!a.type.isEmpty() &&
2690 (a.type.at(a.type.length()-1)=='*' ||
2691 a.type.at(a.type.length()-1)=='&'))
2692 // type ends with * or & => pointer or reference
2695 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
2698 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=0)
2700 result=FALSE; // arg type is a known type
2701 AUTO_TRACE_EXIT("known type: result={}",result);
2704 if (checkIfTypedef(ctx,fd,a.type))
2706 result=FALSE; // argument is a typedef
2707 AUTO_TRACE_EXIT("typedef: result={}",result);
2710 std::string atype = a.type.str();
2711 if (reg::search(atype,match,initChars) && match.position()==0)
2713 result=TRUE; // argument type starts with typical initializer char
2714 AUTO_TRACE_EXIT("argument with init char: result={}",result);
2717 std::string resType=resolveTypeDef(ctx,a.type).str();
2718 if (resType.empty()) resType=atype;
2719 static const reg::Ex idChars(R"(\a\w*)");
2720 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2722 resType=match.str();
2723 if (resType=="int" || resType=="long" ||
2724 resType=="float" || resType=="double" ||
2725 resType=="char" || resType=="void" ||
2726 resType=="signed" || resType=="unsigned" ||
2727 resType=="const" || resType=="volatile" )
2729 result=FALSE; // type keyword -> function prototype
2730 AUTO_TRACE_EXIT("type keyword: result={}",result);
2738 AUTO_TRACE_EXIT("end: result={}",result);
2742 static void addVariable(const Entry *root,int isFuncPtr=-1)
2744 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
2746 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
2747 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
2748 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
2750 QCString type = root->type;
2751 QCString name = root->name;
2752 QCString args = root->args;
2753 if (type.isEmpty() && name.find("operator")==-1 &&
2754 (name.find('*')!=-1 || name.find('&')!=-1))
2756 // recover from parse error caused by redundant braces
2757 // like in "int *(var[10]);", which is parsed as
2758 // type="" name="int *" args="(var[10])"
2761 std::string sargs = args.str();
2762 static const reg::Ex reName(R"(\a\w*)");
2764 if (reg::search(sargs,match,reName))
2766 name = match.str(); // e.g. 'var' in '(var[10])'
2767 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
2768 size_t j = sargs.find(')');
2769 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
2775 if (i==-1 && (root->spec&Entry::Alias)==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
2776 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
2777 if (i>=0) // function pointer
2779 int ai = type.find('[',i);
2780 if (ai>i) // function pointer array
2782 args.prepend(type.right(type.length()-ai));
2785 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
2787 type=type.left(type.length()-1);
2792 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
2795 name=removeRedundantWhiteSpace(name);
2797 // find the scope of this variable
2798 int index = computeQualifiedIndex(name);
2799 if (index!=-1 && root->parent()->section==Entry::GROUPDOC_SEC && root->parent()->tagInfo())
2800 // grouped members are stored with full scope
2802 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
2803 scope=name.left(index);
2804 name=name.mid(index+2);
2808 Entry *p = root->parent();
2809 while ((p->section & Entry::SCOPE_MASK))
2811 QCString scopeName = p->name;
2812 if (!scopeName.isEmpty())
2814 scope.prepend(scopeName);
2822 type=type.stripWhiteSpace();
2823 ClassDefMutable *cd=0;
2824 bool isRelated=FALSE;
2825 bool isMemberOf=FALSE;
2827 QCString classScope=stripAnonymousNamespaceScope(scope);
2828 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
2829 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
2831 if (name.findRev("::")!=-1)
2833 if (type=="friend class" || type=="friend struct" ||
2834 type=="friend union")
2836 cd=getClassMutable(scope);
2839 addVariableToClass(root, // entry
2840 cd, // class to add member to
2841 MemberType_Friend, // type of member
2842 type, // type value as string
2843 name, // name of the member
2844 args, // arguments as string
2845 FALSE, // from Anonymous scope
2846 0, // anonymous member
2847 Protection::Public, // protection
2848 Relationship::Member // related to a class
2852 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
2854 Doxygen::staticInitMap.insert(std::make_pair(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine}));
2858 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
2859 return; /* skip this member, because it is a
2860 * static variable definition (always?), which will be
2861 * found in a class scope as well, but then we know the
2862 * correct protection level, so only then it will be
2863 * inserted in the correct list!
2868 mtype=MemberType_EnumValue;
2869 else if (type.startsWith("typedef "))
2870 mtype=MemberType_Typedef;
2871 else if (type.startsWith("friend "))
2872 mtype=MemberType_Friend;
2873 else if (root->mtype==MethodTypes::Property)
2874 mtype=MemberType_Property;
2875 else if (root->mtype==MethodTypes::Event)
2876 mtype=MemberType_Event;
2877 else if (type.find("sequence<") != -1)
2878 mtype=sliceOpt ? MemberType_Sequence : MemberType_Typedef;
2879 else if (type.find("dictionary<") != -1)
2880 mtype=sliceOpt ? MemberType_Dictionary : MemberType_Typedef;
2882 mtype=MemberType_Variable;
2884 if (!root->relates.isEmpty()) // related variable
2887 isMemberOf=(root->relatesType==RelatesType::MemberOf);
2888 if (getClass(root->relates)==0 && !scope.isEmpty())
2889 scope=mergeScopes(scope,root->relates);
2891 scope=root->relates;
2894 cd=getClassMutable(scope);
2895 if (cd==0 && classScope!=scope) cd=getClassMutable(classScope);
2900 // if cd is an anonymous (=tag less) scope we insert the member
2901 // into a non-anonymous parent scope as well. This is needed to
2902 // be able to refer to it using \var or \fn
2904 //int indentDepth=0;
2905 int si=scope.find('@');
2906 //int anonyScopes = 0;
2909 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
2910 Relationship relationship = isMemberOf ? Relationship::Foreign :
2911 isRelated ? Relationship::Related :
2912 Relationship::Member ;
2913 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
2916 ClassDefMutable *pcd=0;
2917 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
2918 if (!pScope.isEmpty())
2919 pScope.prepend(annScopePrefix);
2920 else if (annScopePrefix.length()>2)
2921 pScope=annScopePrefix.left(annScopePrefix.length()-2);
2922 if (name.at(0)!='@')
2924 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
2926 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
2927 md=addVariableToClass(root, // entry
2928 pcd, // class to add member to
2929 mtype, // member type
2930 type, // type value as string
2931 name, // member name
2932 args, // arguments as string
2933 TRUE, // from anonymous scope
2934 0, // from anonymous member
2940 else // anonymous scope inside namespace or file => put variable in the global scope
2942 if (mtype==MemberType_Variable)
2944 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
2945 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,0);
2952 addVariableToClass(root, // entry
2953 cd, // class to add member to
2954 mtype, // member type
2955 type, // type value as string
2956 name, // name of the member
2957 args, // arguments as string
2958 FALSE, // from anonymous scope
2959 md, // from anonymous member
2964 else if (!name.isEmpty()) // global variable
2966 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*0,*/0);
2971 //----------------------------------------------------------------------
2972 // Searches the Entry tree for typedef documentation sections.
2973 // If found they are stored in their class or in the global list.
2974 static void buildTypedefList(const Entry *root)
2976 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
2977 if (!root->name.isEmpty() &&
2978 root->section==Entry::VARIABLE_SEC &&
2979 root->type.find("typedef ")!=-1 // its a typedef
2985 for (const auto &e : root->children())
2986 if (e->section!=Entry::ENUM_SEC)
2987 buildTypedefList(e.get());
2990 //----------------------------------------------------------------------
2991 // Searches the Entry tree for sequence documentation sections.
2992 // If found they are stored in the global list.
2993 static void buildSequenceList(const Entry *root)
2995 if (!root->name.isEmpty() &&
2996 root->section==Entry::VARIABLE_SEC &&
2997 root->type.find("sequence<")!=-1 // it's a sequence
3003 for (const auto &e : root->children())
3004 if (e->section!=Entry::ENUM_SEC)
3005 buildSequenceList(e.get());
3008 //----------------------------------------------------------------------
3009 // Searches the Entry tree for dictionary documentation sections.
3010 // If found they are stored in the global list.
3011 static void buildDictionaryList(const Entry *root)
3013 if (!root->name.isEmpty() &&
3014 root->section==Entry::VARIABLE_SEC &&
3015 root->type.find("dictionary<")!=-1 // it's a dictionary
3021 for (const auto &e : root->children())
3022 if (e->section!=Entry::ENUM_SEC)
3023 buildDictionaryList(e.get());
3026 //----------------------------------------------------------------------
3027 // Searches the Entry tree for Variable documentation sections.
3028 // If found they are stored in their class or in the global list.
3030 static void buildVarList(const Entry *root)
3032 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3034 if (!root->name.isEmpty() &&
3035 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3037 (root->section==Entry::VARIABLE_SEC // it's a variable
3039 (root->section==Entry::FUNCTION_SEC && // or maybe a function pointer variable
3040 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3042 (root->section==Entry::FUNCTION_SEC && // class variable initialized by constructor
3043 isVarWithConstructor(root)
3046 ) // documented variable
3049 addVariable(root,isFuncPtr);
3051 for (const auto &e : root->children())
3052 if (e->section!=Entry::ENUM_SEC)
3053 buildVarList(e.get());
3056 //----------------------------------------------------------------------
3057 // Searches the Entry tree for Interface sections (UNO IDL only).
3058 // If found they are stored in their service or in the global list.
3061 static void addInterfaceOrServiceToServiceOrSingleton(
3063 ClassDefMutable *cd,
3064 QCString const& rname)
3066 FileDef *fd = root->fileDef();
3067 enum MemberType type = (root->section==Entry::EXPORTED_INTERFACE_SEC)
3068 ? MemberType_Interface
3069 : MemberType_Service;
3070 QCString fileName = root->fileName;
3071 if (fileName.isEmpty() && root->tagInfo())
3073 fileName = root->tagInfo()->tagName;
3075 auto md = createMemberDef(
3076 fileName, root->startLine, root->startColumn, root->type, rname,
3077 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3078 type, ArgumentList(), root->argList, root->metaData);
3079 auto mmd = toMemberDefMutable(md.get());
3080 mmd->setTagInfo(root->tagInfo());
3081 mmd->setMemberClass(cd);
3082 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3083 mmd->setDocsForDefinition(false);
3084 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3085 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3086 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3087 mmd->setMemberSpecifiers(root->spec);
3088 mmd->setMemberGroupId(root->mGrpId);
3089 mmd->setTypeConstraints(root->typeConstr);
3090 mmd->setLanguage(root->lang);
3091 mmd->setBodyDef(fd);
3092 mmd->setFileDef(fd);
3093 mmd->addSectionsToDefinition(root->anchors);
3094 QCString const def = root->type + " " + rname;
3095 mmd->setDefinition(def);
3096 mmd->enableCallGraph(root->callGraph);
3097 mmd->enableCallerGraph(root->callerGraph);
3098 mmd->enableReferencedByRelation(root->referencedByRelation);
3099 mmd->enableReferencesRelation(root->referencesRelation);
3100 mmd->addQualifiers(root->qualifiers);
3102 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3103 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3105 // add member to the class cd
3106 cd->insertMember(md.get());
3107 // also add the member as a "base" (to get nicer diagrams)
3108 // "optional" interface/service get Protected which turns into dashed line
3109 BaseInfo base(rname,
3110 (root->spec & (Entry::Optional)) ? Protection::Protected : Protection::Public, Specifier::Normal);
3111 TemplateNameMap templateNames;
3112 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3113 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3114 // add file to list of used files
3115 cd->insertUsedFile(fd);
3117 addMemberToGroups(root,md.get());
3118 ModuleManager::instance().addMemberToModule(root,md.get());
3119 root->markAsProcessed();
3120 mmd->setRefItems(root->sli);
3122 // add member to the global list of all members
3123 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3124 mn->push_back(std::move(md));
3127 static void buildInterfaceAndServiceList(const Entry *root)
3129 if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
3130 root->section==Entry::INCLUDED_SERVICE_SEC)
3132 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3133 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3134 " mGrpId={} spec={:#x} proto={} docFile='{}'",
3135 root->type, root->parent()->name, root->name, root->args,
3136 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3137 root->mGrpId, root->spec, root->proto, root->docFile);
3139 QCString rname = removeRedundantWhiteSpace(root->name);
3141 if (!rname.isEmpty())
3143 QCString scope = root->parent()->name;
3144 ClassDefMutable *cd = getClassMutable(scope);
3146 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3147 (ClassDef::Service == cd->compoundType()) ||
3148 (ClassDef::Singleton == cd->compoundType())))
3150 addInterfaceOrServiceToServiceOrSingleton(root,cd,rname);
3154 assert(false); // was checked by scanner.l
3157 else if (rname.isEmpty())
3159 warn(root->fileName,root->startLine,
3160 "Illegal member name found.");
3163 // can only have these in IDL anyway
3166 case SrcLangExt_Unknown: // fall through (root node always is Unknown)
3167 case SrcLangExt_IDL:
3168 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3171 return; // nothing to do here
3176 //----------------------------------------------------------------------
3177 // Searches the Entry tree for Function sections.
3178 // If found they are stored in their class or in the global list.
3180 static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3181 const QCString &rtype,const QCString &rname,const QCString &rargs,
3183 Protection protection,bool stat,Specifier virt,uint64_t spec,
3184 const QCString &relates
3187 FileDef *fd=root->fileDef();
3189 QCString type = rtype;
3190 QCString args = rargs;
3192 QCString name=removeRedundantWhiteSpace(rname);
3193 name.stripPrefix("::");
3196 if (isFriend) mtype=MemberType_Friend;
3197 else if (root->mtype==MethodTypes::Signal) mtype=MemberType_Signal;
3198 else if (root->mtype==MethodTypes::Slot) mtype=MemberType_Slot;
3199 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType_DCOP;
3200 else mtype=MemberType_Function;
3202 // strip redundant template specifier for constructors
3205 if ((fd==0 || fd->getLanguage()==SrcLangExt_Cpp) &&
3206 !name.startsWith("operator ") && // not operator
3207 (i=name.find('<'))!=-1 && // containing <
3208 (j=name.find('>'))!=-1 && // or >
3209 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3215 QCString fileName = root->fileName;
3216 if (fileName.isEmpty() && root->tagInfo())
3218 fileName = root->tagInfo()->tagName;
3221 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3222 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3225 // adding class member
3226 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3227 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3228 Relationship::Related ;
3229 auto md = createMemberDef(
3230 fileName,root->startLine,root->startColumn,
3231 type,name,args,root->exception,
3233 stat && root->relatesType!=RelatesType::MemberOf,
3235 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3236 root->argList, root->metaData);
3237 auto mmd = toMemberDefMutable(md.get());
3238 mmd->setTagInfo(root->tagInfo());
3239 mmd->setMemberClass(cd);
3240 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3241 mmd->setDocsForDefinition(!root->proto);
3242 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3243 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3244 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3245 mmd->setMemberSpecifiers(spec);
3246 mmd->setMemberGroupId(root->mGrpId);
3247 mmd->setTypeConstraints(root->typeConstr);
3248 mmd->setLanguage(root->lang);
3249 mmd->setRequiresClause(root->req);
3250 mmd->setId(root->id);
3251 mmd->setBodyDef(fd);
3252 mmd->setFileDef(fd);
3253 mmd->addSectionsToDefinition(root->anchors);
3255 QCString qualScope = cd->qualifiedNameWithTemplateParameters();
3256 SrcLangExt lang = cd->getLanguage();
3257 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3258 if (scopeSeparator!="::")
3260 qualScope = substitute(qualScope,"::",scopeSeparator);
3262 if (lang==SrcLangExt_PHP)
3264 // for PHP we use Class::method and Namespace\method
3265 scopeSeparator="::";
3267 // QCString optArgs = root->argList.empty() ? args : QCString();
3268 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3270 if (!type.isEmpty())
3272 def=type+" "+name; //+optArgs;
3276 def=name; //+optArgs;
3281 if (!type.isEmpty())
3283 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3287 def=qualScope+scopeSeparator+name; //+optArgs;
3290 def.stripPrefix("friend ");
3291 mmd->setDefinition(def);
3292 mmd->enableCallGraph(root->callGraph);
3293 mmd->enableCallerGraph(root->callerGraph);
3294 mmd->enableReferencedByRelation(root->referencedByRelation);
3295 mmd->enableReferencesRelation(root->referencesRelation);
3296 mmd->addQualifiers(root->qualifiers);
3298 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3299 type, qualScope, rname, args, root->proto, def);
3301 // add member to the class cd
3302 cd->insertMember(md.get());
3303 // add file to list of used files
3304 cd->insertUsedFile(fd);
3306 addMemberToGroups(root,md.get());
3307 ModuleManager::instance().addMemberToModule(root,md.get());
3308 root->markAsProcessed();
3309 mmd->setRefItems(root->sli);
3311 // add member to the global list of all members
3312 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3313 MemberName *mn = Doxygen::memberNameLinkedMap->add(name);
3314 mn->push_back(std::move(md));
3317 //------------------------------------------------------------------------------------------
3319 static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3321 QCString scope = sc;
3323 // new global function
3324 QCString name=removeRedundantWhiteSpace(rname);
3325 auto md = createMemberDef(
3326 root->fileName,root->startLine,root->startColumn,
3327 root->type,name,root->args,root->exception,
3328 root->protection,root->virt,root->isStatic,Relationship::Member,
3329 MemberType_Function,
3330 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3331 root->argList,root->metaData);
3332 auto mmd = toMemberDefMutable(md.get());
3333 mmd->setTagInfo(root->tagInfo());
3334 mmd->setLanguage(root->lang);
3335 mmd->setId(root->id);
3336 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3337 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3338 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3339 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3340 mmd->setDocsForDefinition(!root->proto);
3341 mmd->setTypeConstraints(root->typeConstr);
3342 //md->setBody(root->body);
3343 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3344 FileDef *fd=root->fileDef();
3345 mmd->setBodyDef(fd);
3346 mmd->addSectionsToDefinition(root->anchors);
3347 mmd->setMemberSpecifiers(root->spec);
3348 mmd->setMemberGroupId(root->mGrpId);
3349 mmd->setRequiresClause(root->req);
3350 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3352 NamespaceDefMutable *nd = 0;
3353 // see if the function is inside a namespace that was not part of
3354 // the name already (in that case nd should be non-zero already)
3355 if (root->parent()->section == Entry::NAMESPACE_SEC )
3357 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3358 QCString nscope=root->parent()->name;
3359 if (!nscope.isEmpty())
3361 nd = getResolvedNamespaceMutable(nscope);
3364 else if (root->parent()->section==Entry::GROUPDOC_SEC && !scope.isEmpty())
3366 nd = getResolvedNamespaceMutable(sc);
3369 if (!scope.isEmpty())
3371 QCString sep = getLanguageSpecificSeparator(root->lang);
3374 scope = substitute(scope,"::",sep);
3379 if (Config_getBool(HIDE_SCOPE_NAMES)) scope = "";
3381 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3382 if (!root->type.isEmpty())
3384 def=root->type+" "+scope+name; //+optArgs;
3388 def=scope+name; //+optArgs;
3390 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3391 root->type,scope,rname,root->args,root->proto,def);
3392 mmd->setDefinition(def);
3393 mmd->enableCallGraph(root->callGraph);
3394 mmd->enableCallerGraph(root->callerGraph);
3395 mmd->enableReferencedByRelation(root->referencedByRelation);
3396 mmd->enableReferencesRelation(root->referencesRelation);
3397 mmd->addQualifiers(root->qualifiers);
3399 mmd->setRefItems(root->sli);
3400 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3402 // add member to namespace
3403 mmd->setNamespace(nd);
3404 nd->insertMember(md.get());
3408 // add member to the file (we do this even if we have already
3409 // inserted it into the namespace)
3410 mmd->setFileDef(fd);
3411 fd->insertMember(md.get());
3414 addMemberToGroups(root,md.get());
3415 ModuleManager::instance().addMemberToModule(root,md.get());
3416 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3417 // allow find Member to pick it up
3419 root->markAsProcessed(); // Otherwise we have finished with this entry.
3422 // add member to the list of file members
3423 MemberName *mn = Doxygen::functionNameLinkedMap->add(name);
3424 mn->push_back(std::move(md));
3427 //------------------------------------------------------------------------------------------
3429 static void buildFunctionList(const Entry *root)
3431 if (root->section==Entry::FUNCTION_SEC)
3433 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3434 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3435 " spec={:#x} proto={} docFile='{}'",
3436 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3437 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3438 root->spec, root->proto, root->docFile);
3440 bool isFriend=root->type.find("friend ")!=-1;
3441 QCString rname = removeRedundantWhiteSpace(root->name);
3442 //printf("rname=%s\n",qPrint(rname));
3445 int index = computeQualifiedIndex(rname);
3446 if (index!=-1 && root->parent()->section==Entry::GROUPDOC_SEC && root->parent()->tagInfo())
3447 // grouped members are stored with full scope
3449 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3450 scope=rname.left(index);
3451 rname=rname.mid(index+2);
3455 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3457 if (!rname.isEmpty() && scope.find('@')==-1)
3459 ClassDefMutable *cd=0;
3460 // check if this function's parent is a class
3461 scope=stripTemplateSpecifiersFromScope(scope,FALSE);
3463 FileDef *rfd=root->fileDef();
3465 int memIndex=rname.findRev("::");
3467 cd=getClassMutable(scope);
3468 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3470 // strip scope from name
3471 rname=rname.right(rname.length()-root->parent()->name.length()-2);
3474 bool isMember=FALSE;
3477 int ts=rname.find('<');
3478 int te=rname.find('>');
3479 if (memIndex>0 && (ts==-1 || te==-1))
3481 // note: the following code was replaced by inMember=TRUE to deal with a
3482 // function rname='X::foo' of class X inside a namespace also called X...
3484 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3488 // // strip namespace scope from name
3489 // scope=rname.left(memIndex);
3490 // rname=rname.right(rname.length()-memIndex-2);
3496 isMember=memIndex<ts || memIndex>te;
3500 if (!root->parent()->name.isEmpty() &&
3501 (root->parent()->section & Entry::COMPOUND_MASK) &&
3505 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
3506 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3507 root->protection,root->isStatic,root->virt,root->spec,root->relates);
3509 else if (!((root->parent()->section & Entry::COMPOUND_MASK)
3510 || root->parent()->section==Entry::OBJCIMPL_SEC
3513 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
3514 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
3516 // no member => unrelated function
3518 /* check the uniqueness of the function name in the file.
3519 * A file could contain a function prototype and a function definition
3520 * or even multiple function prototypes.
3524 MemberDef *md_found=0;
3525 if ((mn=Doxygen::functionNameLinkedMap->find(rname)))
3527 AUTO_TRACE_ADD("function '{}' already found",rname);
3528 for (const auto &imd : *mn)
3530 MemberDefMutable *md = toMemberDefMutable(imd.get());
3533 const NamespaceDef *mnd = md->getNamespaceDef();
3534 NamespaceDef *rnd = 0;
3535 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
3536 QCString fullScope = scope;
3537 QCString parentScope = root->parent()->name;
3538 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
3540 if (!scope.isEmpty()) fullScope.prepend("::");
3541 fullScope.prepend(parentScope);
3543 //printf("fullScope=%s\n",qPrint(fullScope));
3544 rnd = getResolvedNamespace(fullScope);
3545 const FileDef *mfd = md->getFileDef();
3546 QCString nsName,rnsName;
3547 if (mnd) nsName = mnd->name();
3548 if (rnd) rnsName = rnd->name();
3549 //printf("matching arguments for %s%s %s%s\n",
3550 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
3551 const ArgumentList &mdAl = md->argumentList();
3552 const ArgumentList &mdTempl = md->templateArguments();
3554 // in case of template functions, we need to check if the
3555 // functions have the same number of template parameters
3556 bool sameNumTemplateArgs = TRUE;
3557 bool matchingReturnTypes = TRUE;
3558 bool sameRequiresClause = TRUE;
3559 if (!mdTempl.empty() && !root->tArgLists.empty())
3561 if (mdTempl.size()!=root->tArgLists.back().size())
3563 sameNumTemplateArgs = FALSE;
3565 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
3567 matchingReturnTypes = FALSE;
3569 if (md->requiresClause()!=root->req)
3571 sameRequiresClause = FALSE;
3574 else if (!mdTempl.empty() || !root->tArgLists.empty())
3575 { // if one has template parameters and the other doesn't then that also counts as a
3577 sameNumTemplateArgs = FALSE;
3580 bool staticsInDifferentFiles =
3581 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
3584 matchArguments2(md->getOuterScope(),mfd,&mdAl,
3585 rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
3586 FALSE,root->lang) &&
3587 sameNumTemplateArgs &&
3588 matchingReturnTypes &&
3589 sameRequiresClause &&
3590 !staticsInDifferentFiles
3594 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
3596 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
3598 //printf("match!\n");
3599 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
3600 // see if we need to create a new member
3601 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
3602 ((mnd==0 && rnd==0 && mfd!=0 && // no external reference and
3603 mfd->absFilePath()==root->fileName // prototype in the same file
3606 // otherwise, allow a duplicate global member with the same argument list
3607 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
3609 // member is already in the group, so we don't want to add it again.
3613 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
3617 // merge argument lists
3618 ArgumentList mergedArgList = root->argList;
3619 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
3620 // merge documentation
3621 if (md->documentation().isEmpty() && !root->doc.isEmpty())
3625 md->moveDeclArgumentList(stringToArgumentList(root->lang,root->args));
3629 md->moveArgumentList(stringToArgumentList(root->lang,root->args));
3633 md->setDocumentation(root->doc,root->docFile,root->docLine);
3634 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3635 md->setDocsForDefinition(!root->proto);
3636 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
3638 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3639 md->setBodyDef(rfd);
3642 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
3644 md->setArgsString(root->args);
3646 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3648 md->addSectionsToDefinition(root->anchors);
3650 md->enableCallGraph(md->hasCallGraph() || root->callGraph);
3651 md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
3652 md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
3653 md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
3654 md->addQualifiers(root->qualifiers);
3656 // merge ingroup specifiers
3657 if (md->getGroupDef()==0 && !root->groups.empty())
3659 addMemberToGroups(root,md);
3661 else if (md->getGroupDef()!=0 && root->groups.empty())
3663 //printf("existing member is grouped, new member not\n");
3665 else if (md->getGroupDef()!=0 && !root->groups.empty())
3667 //printf("both members are grouped\n");
3669 ModuleManager::instance().addMemberToModule(root,md);
3671 // if md is a declaration and root is the corresponding
3672 // definition, then turn md into a definition.
3673 if (md->isPrototype() && !root->proto)
3675 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
3676 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
3678 // if md is already the definition, then add the declaration info
3679 else if (!md->isPrototype() && root->proto)
3681 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
3693 if (!found) /* global function is unique with respect to the file */
3695 addGlobalFunction(root,rname,scope);
3699 FileDef *fd=root->fileDef();
3702 // add member to the file (we do this even if we have already
3703 // inserted it into the namespace)
3704 fd->insertMember(md_found);
3708 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
3712 AUTO_TRACE_ADD("function '{}' is not processed",rname);
3715 else if (rname.isEmpty())
3717 warn(root->fileName,root->startLine,
3718 "Illegal member name found."
3722 for (const auto &e : root->children()) buildFunctionList(e.get());
3725 //----------------------------------------------------------------------
3727 static void findFriends()
3730 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
3733 if ((mn=Doxygen::memberNameLinkedMap->find(fn->memberName())))
3734 { // there are members with the same name
3735 // for each function with that name
3736 for (const auto &ifmd : *fn)
3738 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
3739 // for each member with that name
3740 for (const auto &immd : *mn)
3742 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
3743 //printf("Checking for matching arguments
3744 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
3745 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
3747 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
3748 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
3749 fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
3750 TRUE,mmd->getLanguage()
3753 ) // if the member is related and the arguments match then the
3754 // function is actually a friend.
3756 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
3757 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
3758 const ArgumentList &mmdAl = mmd->argumentList();
3759 const ArgumentList &fmdAl = fmd->argumentList();
3760 mergeArguments(const_cast<ArgumentList&>(mmdAl),const_cast<ArgumentList&>(fmdAl));
3761 if (!fmd->documentation().isEmpty())
3763 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
3765 else if (!mmd->documentation().isEmpty())
3767 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
3769 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3771 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
3773 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3775 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
3777 if (!fmd->inbodyDocumentation().isEmpty())
3779 mmd->setInbodyDocumentation(fmd->inbodyDocumentation(),fmd->inbodyFile(),fmd->inbodyLine());
3781 else if (!mmd->inbodyDocumentation().isEmpty())
3783 fmd->setInbodyDocumentation(mmd->inbodyDocumentation(),mmd->inbodyFile(),mmd->inbodyLine());
3785 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
3786 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
3788 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
3789 mmd->setBodyDef(fmd->getBodyDef());
3790 //mmd->setBodyMember(fmd);
3792 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
3794 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
3795 fmd->setBodyDef(mmd->getBodyDef());
3796 //fmd->setBodyMember(mmd);
3798 mmd->setDocsForDefinition(fmd->isDocsForDefinition());
3800 mmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3801 mmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3802 mmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3803 mmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3804 mmd->addQualifiers(fmd->getQualifiers());
3806 fmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3807 fmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3808 fmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3809 fmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3810 fmd->addQualifiers(mmd->getQualifiers());
3818 //----------------------------------------------------------------------
3820 static void transferFunctionDocumentation()
3824 // find matching function declaration and definitions.
3825 for (const auto &mn : *Doxygen::functionNameLinkedMap)
3827 //printf("memberName=%s count=%d\n",mn->memberName(),mn->count());
3828 /* find a matching function declaration and definition for this function */
3829 for (const auto &imdec : *mn)
3831 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
3833 (mdec->isPrototype() ||
3834 (mdec->isVariable() && mdec->isExternal())
3837 for (const auto &imdef : *mn)
3839 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
3840 if (mdef && mdec!=mdef &&
3841 mdec->getNamespaceDef()==mdef->getNamespaceDef())
3843 combineDeclarationAndDefinition(mdec,mdef);
3851 //----------------------------------------------------------------------
3853 static void transferFunctionReferences()
3856 for (const auto &mn : *Doxygen::functionNameLinkedMap)
3858 MemberDefMutable *mdef=0,*mdec=0;
3859 /* find a matching function declaration and definition for this function */
3860 for (const auto &imd : *mn)
3862 MemberDefMutable *md = toMemberDefMutable(imd.get());
3865 if (md->isPrototype())
3867 else if (md->isVariable() && md->isExternal())
3870 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
3872 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
3876 if (mdef && mdec) break;
3880 const ArgumentList &mdefAl = mdef->argumentList();
3881 const ArgumentList &mdecAl = mdec->argumentList();
3883 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
3884 mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
3885 TRUE,mdef->getLanguage()
3889 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
3890 mdef->mergeReferences(mdec);
3891 mdec->mergeReferences(mdef);
3892 mdef->mergeReferencedBy(mdec);
3893 mdec->mergeReferencedBy(mdef);
3899 //----------------------------------------------------------------------
3901 static void transferRelatedFunctionDocumentation()
3904 // find match between function declaration and definition for
3905 // related functions
3906 for (const auto &mn : *Doxygen::functionNameLinkedMap)
3908 /* find a matching function declaration and definition for this function */
3909 // for each global function
3910 for (const auto &imd : *mn)
3912 MemberDefMutable *md = toMemberDefMutable(imd.get());
3915 //printf(" Function '%s'\n",qPrint(md->name()));
3917 if ((rmn=Doxygen::memberNameLinkedMap->find(md->name()))) // check if there is a member with the same name
3919 //printf(" Member name found\n");
3920 // for each member with the same name
3921 for (const auto &irmd : *rmn)
3923 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
3924 //printf(" Member found: related='%d'\n",rmd->isRelated());
3926 (rmd->isRelated() || rmd->isForeign()) && // related function
3927 matchArguments2( md->getOuterScope(), md->getFileDef(), &md->argumentList(),
3928 rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
3929 TRUE,md->getLanguage()
3933 AUTO_TRACE_ADD("Found related member '{}'",md->name());
3934 if (rmd->relatedAlso())
3935 md->setRelatedAlso(rmd->relatedAlso());
3936 else if (rmd->isForeign())
3948 //----------------------------------------------------------------------
3950 void transferStaticInstanceInitializers()
3953 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
3955 size_t i=qualifiedName.rfind("::");
3956 if (i!=std::string::npos)
3958 QCString scope = qualifiedName.substr(0,i);
3959 QCString name = qualifiedName.substr(i+2);
3960 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
3963 for (const auto &imd : *mn)
3965 MemberDefMutable *md = toMemberDefMutable(imd.get());
3966 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
3968 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
3969 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
3970 md->setBodySegment(bodyInfo.defLine,
3980 //----------------------------------------------------------------------
3982 /*! make a dictionary of all template arguments of class cd
3983 * that are part of the base class name.
3984 * Example: A template class A with template arguments <R,S,T>
3985 * that inherits from B<T,T,S> will have T and S in the dictionary.
3987 static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
3989 std::map<std::string,int> templateNames;
3991 for (const Argument &arg : templateArguments)
3993 static const reg::Ex re(R"(\a[\w:]*)");
3994 reg::Iterator it(name,re);
3996 for (; it!=end ; ++it)
3998 const auto &match = *it;
3999 std::string n = match.str();
4000 if (n==arg.name.str())
4002 if (templateNames.find(n)==templateNames.end())
4004 templateNames.insert(std::make_pair(n,count));
4009 return templateNames;
4012 /*! Searches a class from within \a context and \a cd and returns its
4013 * definition if found (otherwise 0 is returned).
4015 static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,const QCString &name)
4022 FileDef *fd=cd->getFileDef();
4023 SymbolResolver resolver(fd);
4024 if (context && cd!=context)
4026 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4028 //printf("1. result=%p\n",result);
4031 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4033 //printf("2. result=%p\n",result);
4034 if (result==0) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4036 result = getClass(name);
4038 //printf("3. result=%p\n",result);
4039 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4041 // context ? qPrint(context->name()) : "<none>",
4042 // cd ? qPrint(cd->name()) : "<none>",
4043 // result ? qPrint(result->name()) : "<none>",
4044 // Doxygen::classLinkedMap->find(name)
4050 static void findUsedClassesForClass(const Entry *root,
4051 Definition *context,
4052 ClassDefMutable *masterCd,
4053 ClassDefMutable *instanceCd,
4055 const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4056 const TemplateNameMap &templateNames = TemplateNameMap()
4060 const ArgumentList &formalArgs = masterCd->templateArguments();
4061 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4063 for (auto &mi : *mni)
4065 const MemberDef *md=mi->memberDef();
4066 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4068 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4069 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4070 QCString typedefValue = md->getLanguage()==SrcLangExt_Java ? type : resolveTypeDef(masterCd,type);
4071 if (!typedefValue.isEmpty())
4073 type = typedefValue;
4076 QCString usedClassName;
4079 // the type can contain template variables, replace them if present
4080 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4082 //printf(" template substitution gives=%s\n",qPrint(type));
4083 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4085 // find the type (if any) that matches usedClassName
4086 SymbolResolver resolver(masterCd->getFileDef());
4087 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4088 //printf("====> usedClassName=%s -> typeCd=%s\n",
4089 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4092 usedClassName = typeCd->name();
4095 int sp=usedClassName.find('<');
4097 int si=usedClassName.findRev("::",sp);
4100 // replace any namespace aliases
4101 replaceNamespaceAliases(usedClassName,si);
4103 // add any template arguments to the class
4104 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4105 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4107 TemplateNameMap formTemplateNames;
4108 if (templateNames.empty())
4110 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4112 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4113 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4115 for (const Argument &arg : masterCd->templateArguments())
4117 if (arg.name==usedName) // type is a template argument
4119 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4120 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4123 usedCdm = toClassDefMutable(
4124 Doxygen::hiddenClassLinkedMap->add(usedName,
4126 masterCd->getDefFileName(),masterCd->getDefLine(),
4127 masterCd->getDefColumn(),
4132 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4133 usedCdm->makeTemplateArgument();
4134 usedCdm->setUsedOnly(TRUE);
4135 usedCdm->setLanguage(masterCd->getLanguage());
4142 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4143 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4146 if (isArtificial) usedCdm->setArtificial(TRUE);
4147 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4155 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4156 //printf("Looking for used class %s: result=%s master=%s\n",
4157 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4162 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4163 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4164 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4167 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4172 if (!found && !type.isEmpty()) // used class is not documented in any scope
4174 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4175 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4176 if (usedCd==0 && !Config_getBool(HIDE_UNDOC_RELATIONS))
4178 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4180 type+=md->argsString();
4182 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4183 usedCdm = toClassDefMutable(
4184 Doxygen::hiddenClassLinkedMap->add(type,
4186 masterCd->getDefFileName(),masterCd->getDefLine(),
4187 masterCd->getDefColumn(),
4188 type,ClassDef::Class)));
4191 usedCdm->setUsedOnly(TRUE);
4192 usedCdm->setLanguage(masterCd->getLanguage());
4198 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4199 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4202 if (isArtificial) usedCdm->setArtificial(TRUE);
4203 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4212 static void findBaseClassesForClass(
4214 Definition *context,
4215 ClassDefMutable *masterCd,
4216 ClassDefMutable *instanceCd,
4217 FindBaseClassRelation_Mode mode,
4219 const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4220 const TemplateNameMap &templateNames=TemplateNameMap()
4223 AUTO_TRACE("name={}",root->name);
4224 // The base class could ofcouse also be a non-nested class
4225 const ArgumentList &formalArgs = masterCd->templateArguments();
4226 for (const BaseInfo &bi : root->extends)
4228 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4229 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4230 TemplateNameMap formTemplateNames;
4231 if (templateNames.empty())
4233 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4236 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4237 //printf("bi->name=%s tbi.name=%s\n",qPrint(bi->name),qPrint(tbi.name));
4239 if (mode==DocumentedOnly)
4241 // find a documented base class in the correct scope
4242 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4244 // 1.8.2: decided to show inheritance relations even if not documented,
4245 // we do make them artificial, so they do not appear in the index
4246 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4247 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4249 // no documented base class -> try to find an undocumented one
4250 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4254 else if (mode==TemplateInstances)
4256 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4261 //----------------------------------------------------------------------
4263 static void findTemplateInstanceRelation(const Entry *root,
4264 Definition *context,
4265 ClassDefMutable *templateClass,const QCString &templSpec,
4266 const TemplateNameMap &templateNames,
4269 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4270 templateClass->name(),templSpec,isArtificial);
4272 bool existingClass = (templSpec ==
4273 tempArgListToString(templateClass->templateArguments(),root->lang,false)
4275 if (existingClass) return;
4277 bool freshInstance=FALSE;
4278 ClassDefMutable *instanceClass = toClassDefMutable(
4279 templateClass->insertTemplateInstance(
4280 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4283 instanceClass->setArtificial(TRUE);
4284 instanceClass->setLanguage(root->lang);
4288 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4289 instanceClass->setTemplateBaseClassNames(templateNames);
4291 // search for new template instances caused by base classes of
4293 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4294 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4296 const Entry *templateRoot = it->second;
4297 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4298 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4299 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4300 TemplateInstances,isArtificial,templArgs,templateNames);
4302 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4303 isArtificial,templArgs,templateNames);
4308 AUTO_TRACE_ADD("instance already exists");
4313 static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4316 int index=n.find('<');
4321 bool result = rightScopeMatch(scope,n);
4325 /*! Searches for the end of a template in prototype \a s starting from
4326 * character position \a startPos. If the end was found the position
4327 * of the closing \> is returned, otherwise -1 is returned.
4329 * Handles exotic cases such as
4338 static int findEndOfTemplate(const QCString &s,int startPos)
4340 // locate end of template
4344 int len = s.length();
4345 bool insideString=FALSE;
4346 bool insideChar=FALSE;
4348 while (e<len && brCount!=0)
4354 if (!insideString && !insideChar)
4356 if (e<len-1 && s.at(e+1)=='<')
4358 else if (roundCount==0)
4363 if (!insideString && !insideChar)
4365 if (e<len-1 && s.at(e+1)=='>')
4367 else if (roundCount==0)
4372 if (!insideString && !insideChar)
4376 if (!insideString && !insideChar)
4382 if (insideString && pc!='\\')
4391 if (insideChar && pc!='\\')
4401 return brCount==0 ? e : -1;
4404 static int findTemplateSpecializationPosition(const QCString &name)
4406 if (name.isEmpty()) return 0;
4407 int l = static_cast<int>(name.length());
4408 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4413 while (count>0 && i>=0)
4418 case '>': if (!insideQuote) count++; break;
4419 case '<': if (!insideQuote) count--; break;
4420 case '\'': if (!insideQuote) insideQuote=c;
4421 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4423 case '"': if (!insideQuote) insideQuote=c;
4424 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4434 static bool findClassRelation(
4436 Definition *context,
4437 ClassDefMutable *cd,
4439 const TemplateNameMap &templateNames,
4440 FindBaseClassRelation_Mode mode,
4444 AUTO_TRACE("name={} base={} isArtificial={}",cd->name(),bi->name,isArtificial);
4446 QCString biName=bi->name;
4447 bool explicitGlobalScope=FALSE;
4448 if (biName.startsWith("::")) // explicit global scope
4450 biName=biName.right(biName.length()-2);
4451 explicitGlobalScope=TRUE;
4454 Entry *parentNode=root->parent();
4455 bool lastParent=FALSE;
4456 do // for each parent scope, starting with the largest scope
4457 // (in case of nested classes)
4459 QCString scopeName= parentNode ? parentNode->name : QCString();
4460 int scopeOffset=explicitGlobalScope ? 0 : scopeName.length();
4461 do // try all parent scope prefixes, starting with the largest scope
4463 //printf("scopePrefix='%s' biName='%s'\n",
4464 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4466 QCString baseClassName=biName;
4469 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4471 //QCString stripped;
4472 //baseClassName=stripTemplateSpecifiersFromScope
4473 // (removeRedundantWhiteSpace(baseClassName),TRUE,
4475 SymbolResolver resolver(cd->getFileDef());
4476 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4481 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4482 QCString templSpec = resolver.getTemplateSpec();
4483 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4484 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4485 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4486 // cd ? qPrint(cd->name()):"<none>",
4487 // qPrint(baseClassName),
4488 // baseClass?qPrint(baseClass->name()):"<none>",
4489 // qPrint(templSpec)
4491 //if (baseClassName.left(root->name.length())!=root->name ||
4492 // baseClassName.at(root->name.length())!='<'
4493 // ) // Check for base class with the same name.
4494 // // If found then look in the outer scope for a match
4495 // // and prevent recursion.
4496 if (!isRecursiveBaseClass(root->name,baseClassName)
4497 || explicitGlobalScope
4498 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4499 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4500 || (root->lang==SrcLangExt_IDL &&
4501 (root->section==Entry::EXPORTED_INTERFACE_SEC ||
4502 root->section==Entry::INCLUDED_SERVICE_SEC)))
4504 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4505 baseClassName, root->name, bi->prot, bi->virt, templSpec);
4507 int i=findTemplateSpecializationPosition(baseClassName);
4508 int si=baseClassName.findRev("::",i);
4510 if (baseClass==0 && static_cast<uint32_t>(i)!=baseClassName.length())
4511 // base class has template specifiers
4513 // TODO: here we should try to find the correct template specialization
4514 // but for now, we only look for the unspecialized base class.
4515 int e=findEndOfTemplate(baseClassName,i+1);
4516 //printf("baseClass==0 i=%d e=%d\n",i,e);
4517 if (e!=-1) // end of template was found at e
4519 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4520 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4521 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4526 baseClassTypeDef = resolver.getTypedef();
4527 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4528 // baseClass,qPrint(baseClassName),qPrint(templSpec));
4531 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4532 // know it is a template, so see if
4533 // we can also link to the explicit
4534 // instance (for instance if a class
4535 // derived from a template argument)
4537 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4538 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4541 // use the template instance instead of the template base.
4542 baseClass = templClass;
4543 templSpec.resize(0);
4547 //printf("cd=%p baseClass=%p\n",cd,baseClass);
4548 bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances);
4549 //printf("1. found=%d\n",found);
4550 if (!found && si!=-1)
4552 // replace any namespace aliases
4553 replaceNamespaceAliases(baseClassName,si);
4554 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4559 baseClassTypeDef = resolver.getTypedef();
4560 found=baseClass!=0 && baseClass!=cd;
4561 if (found) templSpec = resolver.getTemplateSpec();
4563 //printf("2. found=%d\n",found);
4567 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4568 //printf("findClassWithinClassContext(%s,%s)=%p\n",
4569 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
4570 found = baseClass!=0 && baseClass!=cd;
4573 //printf("3. found=%d\n",found);
4576 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4577 // the class name also in the alias mapping.
4578 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4579 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4581 baseClass=getClassMutable(it->second.c_str());
4582 found = baseClass!=0 && baseClass!=cd;
4585 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
4587 //printf("4. found=%d\n",found);
4590 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
4591 // add base class to this class
4593 // if templSpec is not empty then we should "instantiate"
4594 // the template baseClass. A new ClassDef should be created
4595 // to represent the instance. To be able to add the (instantiated)
4596 // members and documentation of a template class
4597 // (inserted in that template class at a later stage),
4598 // the template should know about its instances.
4599 // the instantiation process, should be done in a recursive way,
4600 // since instantiating a template may introduce new inheritance
4602 if (!templSpec.isEmpty() && mode==TemplateInstances)
4604 // if baseClass is actually a typedef then we should not
4605 // instantiate it, since typedefs are in a different namespace
4606 // see bug531637 for an example where this would otherwise hang
4608 if (baseClassTypeDef==0)
4610 //printf(" => findTemplateInstanceRelation: %p\n",baseClassTypeDef);
4611 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
4614 else if (mode==DocumentedOnly || mode==Undocumented)
4616 //printf(" => insert base class\n");
4618 if (baseClassTypeDef || cd->isCSharp())
4621 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
4623 Protection prot = bi->prot;
4624 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
4625 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
4627 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
4628 // add this class as super class to the base class
4629 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
4633 warn(root->fileName,root->startLine,
4634 "Detected potential recursive class relation "
4635 "between class %s and base class %s!",
4636 qPrint(cd->name()),qPrint(baseClass->name())
4642 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
4644 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
4645 biName,baseClassName,templSpec,isArtificial);
4647 if (isATemplateArgument)
4649 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
4650 if (baseClass==0) // not found (or alias)
4652 baseClass= toClassDefMutable(
4653 Doxygen::hiddenClassLinkedMap->add(baseClassName,
4654 createClassDef(root->fileName,root->startLine,root->startColumn,
4657 if (baseClass) // really added (not alias)
4659 if (isArtificial) baseClass->setArtificial(TRUE);
4660 baseClass->setLanguage(root->lang);
4666 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
4667 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
4668 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
4669 if (baseClass==0) // not found (or alias)
4671 baseClass = toClassDefMutable(
4672 Doxygen::classLinkedMap->add(baseClassName,
4673 createClassDef(root->fileName,root->startLine,root->startColumn,
4676 if (baseClass) // really added (not alias)
4678 if (isArtificial) baseClass->setArtificial(TRUE);
4679 baseClass->setLanguage(root->lang);
4680 si = baseClassName.findRev("::");
4681 if (si!=-1) // class is nested
4683 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),0,root->tagInfo());
4684 if (sd==0 || sd==Doxygen::globalScope) // outer scope not found
4686 baseClass->setArtificial(TRUE); // see bug678139
4694 if (biName.endsWith("-p"))
4696 biName="<"+biName.left(biName.length()-2)+">";
4698 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
4700 // add base class to this class
4701 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
4702 // add this class as super class to the base class
4703 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
4705 // the undocumented base was found in this file
4706 baseClass->insertUsedFile(root->fileDef());
4708 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,0);
4709 if (scope!=baseClass)
4711 baseClass->setOuterScope(scope);
4714 if (baseClassName.endsWith("-p"))
4716 baseClass->setCompoundType(ClassDef::Protocol);
4722 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
4727 AUTO_TRACE_ADD("Base class '{}' not found",biName);
4732 if (mode!=TemplateInstances)
4734 warn(root->fileName,root->startLine,
4735 "Detected potential recursive class relation "
4736 "between class %s and base class %s!",
4737 qPrint(root->name),qPrint(baseClassName)
4740 // for mode==TemplateInstance this case is quite common and
4741 // indicates a relation between a template class and a template
4742 // instance with the same name.
4748 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4752 //printf("new scopeOffset='%d'",scopeOffset);
4753 } while (scopeOffset>=0);
4761 parentNode=parentNode->parent();
4763 } while (lastParent);
4768 //----------------------------------------------------------------------
4769 // Computes the base and super classes for each class in the tree
4771 static bool isClassSection(const Entry *root)
4773 if ( !root->name.isEmpty() )
4775 if (root->section & Entry::COMPOUND_MASK)
4776 // is it a compound (class, struct, union, interface ...)
4780 else if (root->section & Entry::COMPOUNDDOC_MASK)
4781 // is it a documentation block with inheritance info.
4783 bool hasExtends = !root->extends.empty();
4784 if (hasExtends) return TRUE;
4791 /*! Builds a dictionary of all entry nodes in the tree starting with \a root
4793 static void findClassEntries(const Entry *root)
4795 if (isClassSection(root))
4797 g_classEntries.insert({root->name.str(),root});
4799 for (const auto &e : root->children()) findClassEntries(e.get());
4802 static QCString extractClassName(const Entry *root)
4804 // strip any anonymous scopes first
4805 QCString bName=stripAnonymousNamespaceScope(root->name);
4806 bName=stripTemplateSpecifiersFromScope(bName);
4808 if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) &&
4809 (i=bName.find('<'))!=-1)
4811 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
4812 // template part before looking for matches
4813 bName=bName.left(i);
4818 /*! Using the dictionary build by findClassEntries(), this
4819 * function will look for additional template specialization that
4820 * exists as inheritance relations only. These instances will be
4821 * added to the template they are derived from.
4823 static void findInheritedTemplateInstances()
4826 ClassDefSet visitedClasses;
4827 for (const auto &[name,root] : g_classEntries)
4830 QCString bName = extractClassName(root);
4831 if ((cd=getClass(bName)))
4833 ClassDefMutable *cdm = toClassDefMutable(cd);
4836 findBaseClassesForClass(root,cd,cdm,cdm,TemplateInstances,FALSE);
4842 static void findUsedTemplateInstances()
4845 for (const auto &[name,root] : g_classEntries)
4848 QCString bName = extractClassName(root);
4849 if ((cd=getClass(bName)))
4851 ClassDefMutable *cdm = toClassDefMutable(cd);
4854 findUsedClassesForClass(root,cd,cdm,cdm,TRUE);
4855 cdm->addTypeConstraints();
4861 static void computeClassRelations()
4864 for (const auto &[name,root] : g_classEntries)
4866 ClassDefMutable *cd;
4868 QCString bName = extractClassName(root);
4869 if ((cd=getClassMutable(bName)))
4871 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,FALSE);
4873 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
4874 if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 &&
4875 !bName.endsWith("::"))
4877 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
4878 (guessSection(root->fileName)==Entry::HEADER_SEC ||
4879 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
4880 protectionLevelVisible(root->protection) && // hidden by protection
4881 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
4884 root->fileName,root->startLine,
4885 "Compound %s is not documented.",
4892 static void computeTemplateClassRelations()
4895 for (const auto &[name,root] : g_classEntries)
4897 QCString bName=stripAnonymousNamespaceScope(root->name);
4898 bName=stripTemplateSpecifiersFromScope(bName);
4899 ClassDefMutable *cd=getClassMutable(bName);
4900 // strip any anonymous scopes first
4901 if (cd && !cd->getTemplateInstances().empty())
4903 AUTO_TRACE_ADD("Template class '{}'",cd->name());
4904 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
4906 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
4909 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
4910 QCString templSpec = ti.templSpec;
4911 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
4912 for (const BaseInfo &bi : root->extends)
4914 // check if the base class is a template argument
4916 const ArgumentList &tl = cd->templateArguments();
4919 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
4920 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
4921 // for each template name that we inherit from we need to
4922 // substitute the formal with the actual arguments
4923 TemplateNameMap actualTemplateNames;
4924 for (const auto &tn_kv : templateNames)
4926 size_t templIndex = tn_kv.second;
4928 bool hasActArg=FALSE;
4929 if (templIndex<templArgs->size())
4931 actArg=templArgs->at(templIndex);
4935 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
4936 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
4939 actualTemplateNames.insert(std::make_pair(actArg.type.str(),static_cast<int>(templIndex)));
4943 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs);
4944 // find a documented base class in the correct scope
4945 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
4947 // no documented base class -> try to find an undocumented one
4948 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
4958 //-----------------------------------------------------------------------
4959 // compute the references (anchors in HTML) for each function in the file
4961 static void computeMemberReferences()
4964 for (const auto &cd : *Doxygen::classLinkedMap)
4966 ClassDefMutable *cdm = toClassDefMutable(cd.get());
4969 cdm->computeAnchors();
4972 for (const auto &fn : *Doxygen::inputNameLinkedMap)
4974 for (const auto &fd : *fn)
4976 fd->computeAnchors();
4979 for (const auto &nd : *Doxygen::namespaceLinkedMap)
4981 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
4984 ndm->computeAnchors();
4987 for (const auto &gd : *Doxygen::groupLinkedMap)
4989 gd->computeAnchors();
4993 //----------------------------------------------------------------------
4995 static void addListReferences()
4998 for (const auto &cd : *Doxygen::classLinkedMap)
5000 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5003 cdm->addListReferences();
5007 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5009 for (const auto &fd : *fn)
5011 fd->addListReferences();
5015 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5017 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
5020 ndm->addListReferences();
5024 for (const auto &gd : *Doxygen::groupLinkedMap)
5026 gd->addListReferences();
5029 for (const auto &pd : *Doxygen::pageLinkedMap)
5031 QCString name = pd->getOutputFileBase();
5032 if (pd->getGroupDef())
5034 name = pd->getGroupDef()->getOutputFileBase();
5037 const RefItemVector &xrefItems = pd->xrefListItems();
5038 addRefItem(xrefItems,
5040 theTranslator->trPage(TRUE,TRUE),
5041 name,pd->title(),QCString(),0);
5045 for (const auto &dd : *Doxygen::dirLinkedMap)
5047 QCString name = dd->getOutputFileBase();
5048 //if (dd->getGroupDef())
5050 // name = dd->getGroupDef()->getOutputFileBase();
5052 const RefItemVector &xrefItems = dd->xrefListItems();
5053 addRefItem(xrefItems,
5055 theTranslator->trDir(TRUE,TRUE),
5056 name,dd->displayName(),QCString(),0);
5059 ModuleManager::instance().addListReferences();
5062 //----------------------------------------------------------------------
5064 static void generateXRefPages()
5067 for (RefListManager::Ptr &rl : RefListManager::instance())
5073 //----------------------------------------------------------------------
5074 // Copy the documentation in entry 'root' to member definition 'md' and
5075 // set the function declaration of the member to 'funcDecl'. If the boolean
5076 // over_load is set the standard overload text is added.
5078 static void addMemberDocs(const Entry *root,
5079 MemberDefMutable *md, const QCString &funcDecl,
5080 const ArgumentList *al,
5086 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={:#x}",
5087 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5088 QCString fDecl=funcDecl;
5089 // strip extern specifier
5090 fDecl.stripPrefix("extern ");
5091 md->setDefinition(fDecl);
5092 md->enableCallGraph(root->callGraph);
5093 md->enableCallerGraph(root->callerGraph);
5094 md->enableReferencedByRelation(root->referencedByRelation);
5095 md->enableReferencesRelation(root->referencesRelation);
5096 md->addQualifiers(root->qualifiers);
5097 ClassDefMutable *cd=md->getClassDefMutable();
5098 const NamespaceDef *nd=md->getNamespaceDef();
5101 fullName = cd->name();
5103 fullName = nd->name();
5105 if (!fullName.isEmpty()) fullName+="::";
5106 fullName+=md->name();
5107 FileDef *rfd=root->fileDef();
5109 // TODO determine scope based on root not md
5110 Definition *rscope = md->getOuterScope();
5112 const ArgumentList &mdAl = md->argumentList();
5115 ArgumentList mergedAl = *al;
5116 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5117 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5122 matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5123 rscope,rfd,&root->argList,
5128 //printf("merging arguments (2)\n");
5129 ArgumentList mergedArgList = root->argList;
5130 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5133 if (over_load) // the \overload keyword was used
5135 QCString doc=getOverloadDocs();
5136 if (!root->doc.isEmpty())
5141 md->setDocumentation(doc,root->docFile,root->docLine);
5142 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5143 md->setDocsForDefinition(!root->proto);
5147 //printf("overwrite!\n");
5148 md->setDocumentation(root->doc,root->docFile,root->docLine);
5149 md->setDocsForDefinition(!root->proto);
5151 //printf("overwrite!\n");
5152 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5155 (md->inbodyDocumentation().isEmpty() ||
5156 !root->parent()->name.isEmpty()
5157 ) && !root->inbodyDocs.isEmpty()
5160 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5164 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5165 // qPrint(md->initializer()),md->initializer().isEmpty(),
5166 // qPrint(root->initializer),root->initializer.isEmpty()
5168 std::string rootInit = root->initializer.str();
5169 if (md->initializer().isEmpty() && !rootInit.empty())
5171 //printf("setInitializer\n");
5172 md->setInitializer(rootInit.c_str());
5174 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5176 md->setRequiresClause(root->req);
5179 md->setMaxInitLines(root->initLines);
5183 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5186 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5187 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5188 md->setBodyDef(rfd);
5191 md->setRefItems(root->sli);
5194 md->enableCallGraph(md->hasCallGraph() || root->callGraph);
5195 md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
5196 md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
5197 md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
5198 md->addQualifiers(root->qualifiers);
5200 md->mergeMemberSpecifiers(spec);
5201 md->addSectionsToDefinition(root->anchors);
5202 addMemberToGroups(root,md);
5203 ModuleManager::instance().addMemberToModule(root,md);
5204 if (cd) cd->insertUsedFile(rfd);
5205 //printf("root->mGrpId=%d\n",root->mGrpId);
5206 if (root->mGrpId!=-1)
5208 if (md->getMemberGroupId()!=-1)
5210 if (md->getMemberGroupId()!=root->mGrpId)
5213 root->fileName,root->startLine,
5214 "member %s belongs to two different groups. The second "
5215 "one found here will be ignored.",
5220 else // set group id
5222 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5223 md->setMemberGroupId(root->mGrpId);
5226 md->addQualifiers(root->qualifiers);
5229 //----------------------------------------------------------------------
5230 // find a class definition given the scope name and (optionally) a
5231 // template list specifier
5233 static const ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd,
5234 const QCString &scopeName)
5236 SymbolResolver resolver(fd);
5237 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5241 //----------------------------------------------------------------------------
5242 // Returns TRUE, if the entry belongs to the group of the member definition,
5245 static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md)
5247 const GroupDef *gd = md->getGroupDef();
5253 for (const auto &g : root->groups)
5255 if (g.groupname == gd->name())
5264 //----------------------------------------------------------------------
5265 // Adds the documentation contained in 'root' to a global function
5266 // with name 'name' and argument list 'args' (for overloading) and
5267 // function declaration 'decl' to the corresponding member definition.
5269 static bool findGlobalMember(const Entry *root,
5270 const QCString &namespaceName,
5271 const QCString &type,
5272 const QCString &name,
5273 const QCString &tempArg,
5275 const QCString &decl,
5276 uint64_t /* spec */)
5278 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5280 if (n.isEmpty()) return FALSE;
5281 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5282 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5285 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5287 if (mn) // function name defined
5289 AUTO_TRACE_ADD("Found symbol name");
5292 for (const auto &md : *mn)
5294 // If the entry has groups, then restrict the search to members which are
5295 // in one of the groups of the entry.
5296 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get()))
5300 const NamespaceDef *nd=0;
5301 if (md->isAlias() && md->getOuterScope() &&
5302 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5304 nd = toNamespaceDef(md->getOuterScope());
5308 nd = md->getNamespaceDef();
5311 // special case for strong enums
5313 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5314 { // md part of a strong enum in a namespace?
5315 QCString enumName = namespaceName.mid(enumNamePos+2);
5316 if (namespaceName.left(enumNamePos)==nd->name())
5318 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5321 for (const auto &emd : *enumMn)
5323 found = emd->isStrong() && md->getEnumScope()==emd.get();
5326 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5337 else if (nd==0 && md->isEnumValue()) // md part of global strong enum?
5339 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5342 for (const auto &emd : *enumMn)
5344 found = emd->isStrong() && md->getEnumScope()==emd.get();
5347 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5354 const FileDef *fd=root->fileDef();
5355 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5356 LinkedRefMap<NamespaceDef> nl;
5359 nl = fd->getUsedNamespaces();
5361 //printf("NamespaceList %p\n",nl);
5363 // search in the list of namespaces that are imported via a
5364 // using declaration
5365 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=0;
5367 if ((namespaceName.isEmpty() && nd==0) || // not in a namespace
5368 (nd && nd->name()==namespaceName) || // or in the same namespace
5369 viaUsingDirective // member in 'using' namespace
5372 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5374 NamespaceDef *rnd = 0;
5375 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5377 const ArgumentList &mdAl = const_cast<const MemberDef *>(md.get())->argumentList();
5379 (mdAl.empty() && root->argList.empty()) ||
5380 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5381 matchArguments2(md->getOuterScope(),const_cast<const MemberDef *>(md.get())->getFileDef(),&mdAl,
5382 rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5385 // for template members we need to check if the number of
5386 // template arguments is the same, otherwise we are dealing with
5387 // different functions.
5388 if (matching && !root->tArgLists.empty())
5390 const ArgumentList &mdTempl = md->templateArguments();
5391 if (root->tArgLists.back().size()!=mdTempl.size())
5397 //printf("%s<->%s\n",
5398 // qPrint(argListToString(md->argumentList())),
5399 // qPrint(argListToString(root->argList)));
5401 // For static members we also check if the comment block was found in
5402 // the same file. This is needed because static members with the same
5403 // name can be in different files. Thus it would be wrong to just
5404 // put the comment block at the first syntactically matching member. If
5405 // the comment block belongs to a group of the static member, then add
5406 // the documentation even if it is in a different file.
5407 if (matching && md->isStatic() &&
5408 md->getDefFileName()!=root->fileName &&
5410 !isEntryInGroupOfMember(root,md.get()))
5415 // for template member we also need to check the return type and requires
5416 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5418 //printf("Comparing return types '%s'<->'%s'\n",
5419 // md->typeString(),type);
5420 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5421 md->typeString()!=type ||
5422 md->requiresClause()!=root->req)
5424 //printf(" ---> no matching\n");
5429 if (matching) // add docs to the member
5431 AUTO_TRACE_ADD("Match found");
5432 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5438 if (!found && root->relatesType!=RelatesType::Duplicate && root->section==Entry::FUNCTION_SEC) // no match
5440 QCString fullFuncDecl=decl;
5441 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5443 QCString("no matching file member found for \n")+substitute(fullFuncDecl,"%","%%");
5446 warnMsg+="\nPossible candidates:";
5447 for (const auto &md : *mn)
5450 warnMsg+=substitute(md->declaration(),"%","%%");
5451 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5454 warn(root->fileName,root->startLine, "%s", qPrint(warnMsg));
5457 else // got docs for an undefined member!
5459 if (root->type!="friend class" &&
5460 root->type!="friend struct" &&
5461 root->type!="friend union" &&
5462 root->type!="friend" &&
5463 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5464 root->type.find("typedef ")==-1)
5467 warn(root->fileName,root->startLine,
5468 "documented symbol '%s' was not declared or defined.",qPrint(decl)
5475 static bool isSpecialization(
5476 const ArgumentLists &srcTempArgLists,
5477 const ArgumentLists &dstTempArgLists
5480 auto srcIt = srcTempArgLists.begin();
5481 auto dstIt = dstTempArgLists.begin();
5482 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5484 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5491 static bool scopeIsTemplate(const Definition *d)
5494 if (d && d->definitionType()==Definition::TypeClass)
5496 result = !(toClassDef(d))->templateArguments().empty() ||
5497 scopeIsTemplate(d->getOuterScope());
5502 static QCString substituteTemplatesInString(
5503 const ArgumentLists &srcTempArgLists,
5504 const ArgumentLists &dstTempArgLists,
5505 const std::string &src
5509 static const reg::Ex re(R"(\a\w*)");
5510 reg::Iterator it(src,re);
5512 //printf("type=%s\n",qPrint(sa->type));
5514 for (; it!=end ; ++it) // for each word in srcType
5516 const auto &match = *it;
5517 size_t i = match.position();
5518 size_t l = match.length();
5520 dst+=src.substr(p,i-p);
5521 std::string name=match.str();
5523 auto srcIt = srcTempArgLists.begin();
5524 auto dstIt = dstTempArgLists.begin();
5525 while (srcIt!=srcTempArgLists.end() && !found)
5527 const ArgumentList *tdAli = 0;
5528 std::vector<Argument>::const_iterator tdaIt;
5529 if (dstIt!=dstTempArgLists.end())
5532 tdaIt = tdAli->begin();
5536 const ArgumentList &tsaLi = *srcIt;
5537 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5539 Argument tsa = *tsaIt;
5540 const Argument *tda = 0;
5541 if (tdAli && tdaIt!=tdAli->end())
5546 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5547 // qPrint(tsa.type),qPrint(tsa.name),
5548 // qPrint(tda->type),qPrint(tda->name));
5549 if (name==tsa.name.str())
5551 if (tda && tda->name.isEmpty())
5553 QCString tdaName = tda->name;
5554 QCString tdaType = tda->type;
5556 if (tdaType.startsWith("class ")) vc=6;
5557 else if (tdaType.startsWith("typename ")) vc=9;
5558 if (vc>0) // convert type=="class T" to type=="class" name=="T"
5560 tdaName = tdaType.mid(vc);
5562 if (!tdaName.isEmpty())
5564 name=tdaName.str(); // substitute
5571 //printf(" srcList='%s' dstList='%s faList='%s'\n",
5572 // qPrint(argListToString(srclali.current())),
5573 // qPrint(argListToString(dstlali.current())),
5574 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
5581 //printf(" substituteTemplatesInString(%s)=%s\n",
5582 // qPrint(src),qPrint(dst));
5583 return QCString(dst);
5586 static void substituteTemplatesInArgList(
5587 const ArgumentLists &srcTempArgLists,
5588 const ArgumentLists &dstTempArgLists,
5589 const ArgumentList &src,
5593 auto dstIt = dst.begin();
5594 for (const Argument &sa : src)
5596 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
5597 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
5598 if (dstIt == dst.end())
5602 da.array = dstArray;
5608 Argument da = *dstIt;
5610 da.array = dstArray;
5614 dst.setConstSpecifier(src.constSpecifier());
5615 dst.setVolatileSpecifier(src.volatileSpecifier());
5616 dst.setPureSpecifier(src.pureSpecifier());
5617 dst.setTrailingReturnType(substituteTemplatesInString(
5618 srcTempArgLists,dstTempArgLists,
5619 src.trailingReturnType().str()));
5620 dst.setIsDeleted(src.isDeleted());
5621 dst.setRefQualifier(src.refQualifier());
5622 dst.setNoParameters(src.noParameters());
5623 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
5624 // qPrint(argListToString(src)),qPrint(argListToString(dst))
5628 //-------------------------------------------------------------------------------------------
5630 static void addLocalObjCMethod(const Entry *root,
5631 const QCString &scopeName,
5632 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
5633 const QCString &exceptions,const QCString &funcDecl,
5637 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
5638 ClassDefMutable *cd=0;
5639 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
5641 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
5642 auto md = createMemberDef(
5643 root->fileName,root->startLine,root->startColumn,
5644 funcType,funcName,funcArgs,exceptions,
5645 root->protection,root->virt,root->isStatic,Relationship::Member,
5646 MemberType_Function,ArgumentList(),root->argList,root->metaData);
5647 auto mmd = toMemberDefMutable(md.get());
5648 mmd->setTagInfo(root->tagInfo());
5649 mmd->setLanguage(root->lang);
5650 mmd->setId(root->id);
5651 mmd->makeImplementationDetail();
5652 mmd->setMemberClass(cd);
5653 mmd->setDefinition(funcDecl);
5654 mmd->enableCallGraph(root->callGraph);
5655 mmd->enableCallerGraph(root->callerGraph);
5656 mmd->enableReferencedByRelation(root->referencedByRelation);
5657 mmd->enableReferencesRelation(root->referencesRelation);
5658 mmd->addQualifiers(root->qualifiers);
5659 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
5660 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5661 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5662 mmd->setDocsForDefinition(!root->proto);
5663 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
5664 mmd->addSectionsToDefinition(root->anchors);
5665 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5666 FileDef *fd=root->fileDef();
5667 mmd->setBodyDef(fd);
5668 mmd->setMemberSpecifiers(spec);
5669 mmd->setMemberGroupId(root->mGrpId);
5670 cd->insertMember(md.get());
5671 cd->insertUsedFile(fd);
5672 mmd->setRefItems(root->sli);
5674 MemberName *mn = Doxygen::memberNameLinkedMap->add(root->name);
5675 mn->push_back(std::move(md));
5679 // local objective C method found for class without interface
5683 //-------------------------------------------------------------------------------------------
5685 static void addMemberFunction(const Entry *root,
5687 const QCString &scopeName,
5688 const QCString &namespaceName,
5689 const QCString &className,
5690 const QCString &funcTyp,
5691 const QCString &funcName,
5692 const QCString &funcArgs,
5693 const QCString &funcTempList,
5694 const QCString &exceptions,
5695 const QCString &type,
5696 const QCString &args,
5699 const QCString &relates,
5700 const QCString &funcDecl,
5705 QCString funcType = funcTyp;
5708 bool memFound=FALSE;
5709 for (const auto &imd : *mn)
5711 MemberDefMutable *md = toMemberDefMutable(imd.get());
5712 if (md==0) continue;
5713 ClassDefMutable *cd=md->getClassDefMutable();
5714 if (cd==0) continue;
5715 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
5716 // scopeName, cd->name(), md->argsString(), root->fileName);
5717 FileDef *fd=root->fileDef();
5719 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
5721 //printf("scopeName %s->%s\n",qPrint(scopeName),
5722 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
5724 // if the member we are searching for is an enum value that is part of
5725 // a "strong" enum, we need to look into the fields of the enum for a match
5727 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
5729 QCString enumName = className.mid(enumNamePos+2);
5730 QCString fullScope = className.left(enumNamePos);
5731 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
5732 if (fullScope==cd->name())
5734 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
5735 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
5738 for (const auto &emd : *enumMn)
5740 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
5743 addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5746 if (memFound) break;
5751 if (memFound) break;
5753 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
5754 if (tcd==0 && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
5756 // don't be fooled by anonymous scopes
5759 //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
5760 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd,tcd?qPrint(tcd->name()):"",cd);
5762 if (cd && tcd==cd) // member's classes match
5764 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
5766 // get the template parameter lists found at the member declaration
5767 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
5768 const ArgumentList &templAl = md->templateArguments();
5769 if (!templAl.empty())
5771 declTemplArgs.push_back(templAl);
5774 // get the template parameter lists found at the member definition
5775 const ArgumentLists &defTemplArgs = root->tArgLists;
5776 //printf("defTemplArgs=%p\n",defTemplArgs);
5778 // do we replace the decl argument lists with the def argument lists?
5779 bool substDone=FALSE;
5780 ArgumentList argList;
5782 /* substitute the occurrences of class template names in the
5783 * argument list before matching
5785 const ArgumentList &mdAl = md->argumentList();
5786 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
5788 /* the function definition has template arguments
5789 * and the class definition also has template arguments, so
5790 * we must substitute the template names of the class by that
5791 * of the function definition before matching.
5793 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
5797 else /* no template arguments, compare argument lists directly */
5802 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}'",
5803 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName);
5806 md->isVariable() || md->isTypedef() || // needed for function pointers
5808 md->getClassDef(),md->getFileDef(),&argList,
5809 cd,fd,&root->argList,
5812 if (md->getLanguage()==SrcLangExt_ObjC && md->isVariable() && (root->section&Entry::FUNCTION_SEC))
5814 matching = FALSE; // don't match methods and attributes with the same name
5817 // for template member we also need to check the return type
5818 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5820 QCString memType = md->typeString();
5821 memType.stripPrefix("static "); // see bug700696
5822 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,TRUE),
5823 className+"::",""); // see bug700693 & bug732594
5824 memType=substitute(stripTemplateSpecifiersFromScope(memType,TRUE),
5825 className+"::",""); // see bug758900
5826 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
5827 md->typeString(),funcType,md->templateArguments().size(),root->tArgLists.back().size());
5828 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
5830 //printf(" ---> no matching\n");
5834 else if (defTemplArgs.size()>declTemplArgs.size())
5836 // avoid matching a non-template function in a template class against a
5837 // template function with the same name and parameters, see issue #10184
5841 bool rootIsUserDoc = (root->section&Entry::MEMBERDOC_SEC)!=0;
5842 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
5843 bool mdIsTemplate = md->templateArguments().hasParameters();
5844 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
5845 bool rootIsTemplate = !root->tArgLists.empty();
5846 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
5847 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
5848 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
5849 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
5852 // Method with template return type does not match method without return type
5853 // even if the parameters are the same. See also bug709052
5854 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
5858 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
5860 if (substDone) // found a new argument list
5862 if (matching) // replace member's argument list
5864 md->setDefinitionTemplateParameterLists(root->tArgLists);
5865 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
5869 if (!funcTempList.isEmpty() &&
5870 isSpecialization(declTemplArgs,defTemplArgs))
5872 // check if we are dealing with a partial template
5873 // specialization. In this case we add it to the class
5874 // even though the member arguments do not match.
5876 addMethodToClass(root,cd,type,md->name(),args,isFriend,
5877 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
5884 addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5889 else if (cd && cd!=tcd) // we did find a class with the same name as cd
5890 // but in a different namespace
5895 if (memFound) break;
5897 if (count==0 && root->parent() &&
5898 root->parent()->section==Entry::OBJCIMPL_SEC)
5900 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
5903 if (count==0 && !(isFriend && funcType=="class"))
5906 const ClassDef *ecd = 0, *ucd = 0;
5907 MemberDef *emd = 0, *umd = 0;
5908 //printf("Assume template class\n");
5909 for (const auto &md : *mn)
5911 MemberDef *cmd=md.get();
5912 MemberDefMutable *cdmdm = toMemberDefMutable(cmd);
5913 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : 0;
5914 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
5915 if (ccd!=0 && rightScopeMatch(ccd->name(),className))
5917 const ArgumentList &templAl = md->templateArguments();
5918 if (!root->tArgLists.empty() && !templAl.empty() &&
5919 root->tArgLists.back().size()<=templAl.size())
5921 AUTO_TRACE_ADD("add template specialization");
5922 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
5923 root->protection,root->isStatic,root->virt,spec,relates);
5926 if (argListToString(md->argumentList(),FALSE,FALSE) ==
5927 argListToString(root->argList,FALSE,FALSE))
5928 { // exact argument list match -> remember
5931 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
5932 className,ccd->name(),md->argsString());
5934 else // arguments do not match, but member name and scope do -> remember
5938 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
5939 className,ccd->name(),md->argsString());
5944 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
5945 if (!strictProtoMatching)
5947 if (candidates==1 && ucd && umd)
5949 // we didn't find an actual match on argument lists, but there is only 1 member with this
5950 // name in the same scope, so that has to be the one.
5951 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,0,overloaded,spec);
5954 else if (candidates>1 && ecd && emd)
5956 // we didn't find a unique match using type resolution,
5957 // but one of the matches has the exact same signature so
5958 // we take that one.
5959 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,0,overloaded,spec);
5964 QCString warnMsg = "no ";
5965 if (noMatchCount>1) warnMsg+="uniquely ";
5966 warnMsg+="matching class member found for \n";
5968 for (const ArgumentList &al : root->tArgLists)
5970 warnMsg+=" template ";
5971 warnMsg+=tempArgListToString(al,root->lang);
5975 QCString fullFuncDecl=funcDecl;
5976 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
5979 warnMsg+=fullFuncDecl;
5981 if (candidates>0 || noMatchCount>=1)
5983 warnMsg+="\nPossible candidates:";
5986 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
5987 FileDef *fd=root->fileDef();
5989 for (const auto &md : *mn)
5991 const ClassDef *cd=md->getClassDef();
5992 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
5993 if (tcd==0 && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
5995 // don't be fooled by anonymous scopes
5998 if (cd!=0 && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6001 const ArgumentList &templAl = md->templateArguments();
6003 if (templAl.hasParameters())
6005 warnMsg+="template ";
6006 warnMsg+=tempArgListToString(templAl,root->lang);
6010 if (!md->typeString().isEmpty())
6012 warnMsg+=md->typeString();
6015 QCString qScope = cd->qualifiedNameWithTemplateParameters();
6016 if (!qScope.isEmpty())
6017 warnMsg+=qScope+"::"+md->name();
6018 warnMsg+=md->argsString();
6019 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6023 warn(root->fileName,root->startLine,"%s",qPrint(warnMsg));
6027 //-------------------------------------------------------------------------------------------
6029 static void addMemberSpecialization(const Entry *root,
6031 ClassDefMutable *cd,
6032 const QCString &funcType,
6033 const QCString &funcName,
6034 const QCString &funcArgs,
6035 const QCString &funcDecl,
6036 const QCString &exceptions,
6040 MemberDef *declMd=0;
6041 for (const auto &md : *mn)
6043 if (md->getClassDef()==cd)
6045 // TODO: we should probably also check for matching arguments
6050 MemberType mtype=MemberType_Function;
6051 ArgumentList tArgList;
6052 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6053 auto md = createMemberDef(
6054 root->fileName,root->startLine,root->startColumn,
6055 funcType,funcName,funcArgs,exceptions,
6056 declMd ? declMd->protection() : root->protection,
6057 root->virt,root->isStatic,Relationship::Member,
6058 mtype,tArgList,root->argList,root->metaData);
6059 auto mmd = toMemberDefMutable(md.get());
6060 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6061 mmd->setTagInfo(root->tagInfo());
6062 mmd->setLanguage(root->lang);
6063 mmd->setId(root->id);
6064 mmd->setMemberClass(cd);
6065 mmd->setTemplateSpecialization(TRUE);
6066 mmd->setTypeConstraints(root->typeConstr);
6067 mmd->setDefinition(funcDecl);
6068 mmd->enableCallGraph(root->callGraph);
6069 mmd->enableCallerGraph(root->callerGraph);
6070 mmd->enableReferencedByRelation(root->referencedByRelation);
6071 mmd->enableReferencesRelation(root->referencesRelation);
6072 mmd->addQualifiers(root->qualifiers);
6073 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6074 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6075 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6076 mmd->setDocsForDefinition(!root->proto);
6077 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6078 mmd->addSectionsToDefinition(root->anchors);
6079 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6080 FileDef *fd=root->fileDef();
6081 mmd->setBodyDef(fd);
6082 mmd->setMemberSpecifiers(spec);
6083 mmd->setMemberGroupId(root->mGrpId);
6084 cd->insertMember(md.get());
6085 mmd->setRefItems(root->sli);
6087 mn->push_back(std::move(md));
6090 //-------------------------------------------------------------------------------------------
6092 static void addOverloaded(const Entry *root,MemberName *mn,
6093 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6094 const QCString &funcDecl,const QCString &exceptions,uint64_t spec)
6096 // for unique overloaded member we allow the class to be
6097 // omitted, this is to be Qt compatible. Using this should
6098 // however be avoided, because it is error prone
6099 bool sameClass=false;
6102 // check if all members with the same name are also in the same class
6103 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6104 [](const auto &md1,const auto &md2)
6105 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6109 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6110 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : 0;
6114 if (root->mtype==MethodTypes::Signal) mtype=MemberType_Signal;
6115 else if (root->mtype==MethodTypes::Slot) mtype=MemberType_Slot;
6116 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType_DCOP;
6117 else mtype=MemberType_Function;
6119 // new overloaded member function
6120 std::unique_ptr<ArgumentList> tArgList =
6121 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6122 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6123 auto md = createMemberDef(
6124 root->fileName,root->startLine,root->startColumn,
6125 funcType,funcName,funcArgs,exceptions,
6126 root->protection,root->virt,root->isStatic,Relationship::Related,
6127 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6128 auto mmd = toMemberDefMutable(md.get());
6129 mmd->setTagInfo(root->tagInfo());
6130 mmd->setLanguage(root->lang);
6131 mmd->setId(root->id);
6132 mmd->setTypeConstraints(root->typeConstr);
6133 mmd->setMemberClass(cd);
6134 mmd->setDefinition(funcDecl);
6135 mmd->enableCallGraph(root->callGraph);
6136 mmd->enableCallerGraph(root->callerGraph);
6137 mmd->enableReferencedByRelation(root->referencedByRelation);
6138 mmd->enableReferencesRelation(root->referencesRelation);
6139 mmd->addQualifiers(root->qualifiers);
6140 QCString doc=getOverloadDocs();
6143 mmd->setDocumentation(doc,root->docFile,root->docLine);
6144 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6145 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6146 mmd->setDocsForDefinition(!root->proto);
6147 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6148 mmd->addSectionsToDefinition(root->anchors);
6149 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6150 FileDef *fd=root->fileDef();
6151 mmd->setBodyDef(fd);
6152 mmd->setMemberSpecifiers(spec);
6153 mmd->setMemberGroupId(root->mGrpId);
6154 cd->insertMember(md.get());
6155 cd->insertUsedFile(fd);
6156 mmd->setRefItems(root->sli);
6158 mn->push_back(std::move(md));
6162 //-------------------------------------------------------------------------------------------
6164 /*! This function tries to find a member (in a documented class/file/namespace)
6165 * that corresponds to the function/variable declaration given in \a funcDecl.
6167 * The boolean \a overloaded is used to specify whether or not a standard
6168 * overload documentation line should be generated.
6170 * The boolean \a isFunc is a hint that indicates that this is a function
6171 * instead of a variable or typedef.
6173 static void findMember(const Entry *root,
6174 const QCString &relates,
6175 const QCString &type,
6176 const QCString &args,
6182 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={:#x} lang={}",
6183 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6184 root->spec, root->lang);
6188 QCString namespaceName;
6192 QCString funcTempList;
6193 QCString exceptions;
6195 bool isRelated=FALSE;
6196 bool isMemberOf=FALSE;
6197 bool isFriend=FALSE;
6199 uint64_t spec = root->spec;
6203 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6208 if (funcDecl.stripPrefix("inline "))
6210 spec|=Entry::Inline;
6213 if (funcDecl.stripPrefix("explicit "))
6215 spec|=Entry::Explicit;
6218 if (funcDecl.stripPrefix("mutable "))
6220 spec|=Entry::Mutable;
6223 if (funcDecl.stripPrefix("virtual "))
6229 // delete any ; from the function declaration
6231 while ((sep=funcDecl.find(';'))!=-1)
6233 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6236 // make sure the first character is a space to simplify searching.
6237 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6239 // remove some superfluous spaces
6240 funcDecl= substitute(
6242 substitute(funcDecl,"~ ","~"),
6246 ).stripWhiteSpace();
6248 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6249 if (isFriend && funcDecl.startsWith("class "))
6251 //printf("friend class\n");
6252 funcDecl=funcDecl.right(funcDecl.length()-6);
6253 funcName = funcDecl;
6255 else if (isFriend && funcDecl.startsWith("struct "))
6257 funcDecl=funcDecl.right(funcDecl.length()-7);
6258 funcName = funcDecl;
6262 // extract information from the declarations
6263 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6264 funcArgs,funcTempList,exceptions
6268 // the class name can also be a namespace name, we decide this later.
6269 // if a related class name is specified and the class name could
6270 // not be derived from the function declaration, then use the
6272 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6273 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6274 if (!relates.isEmpty())
6275 { // related member, prefix user specified scope
6277 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6278 if (getClass(relates)==0 && !scopeName.isEmpty())
6280 scopeName= mergeScopes(scopeName,relates);
6284 scopeName = relates;
6288 if (relates.isEmpty() && root->parent() &&
6289 ((root->parent()->section&Entry::SCOPE_MASK) ||
6290 (root->parent()->section==Entry::OBJCIMPL_SEC)
6292 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6293 // with the scope in which it was found
6295 QCString joinedName = root->parent()->name+"::"+scopeName;
6296 if (!scopeName.isEmpty() &&
6297 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6299 scopeName = joinedName;
6303 scopeName = mergeScopes(root->parent()->name,scopeName);
6306 else // see if we can prefix a namespace or class that is used from the file
6308 FileDef *fd=root->fileDef();
6311 for (const auto &fnd : fd->getUsedNamespaces())
6313 QCString joinedName = fnd->name()+"::"+scopeName;
6314 if (Doxygen::namespaceLinkedMap->find(joinedName))
6316 scopeName=joinedName;
6322 scopeName=stripTemplateSpecifiersFromScope(
6323 removeRedundantWhiteSpace(scopeName),FALSE,&funcSpec);
6325 // funcSpec contains the last template specifiers of the given scope.
6326 // If this method does not have any template arguments or they are
6327 // empty while funcSpec is not empty we assume this is a
6328 // specialization of a method. If not, we clear the funcSpec and treat
6329 // this as a normal method of a template class.
6330 if (!(root->tArgLists.size()>0 &&
6331 root->tArgLists.front().size()==0
6338 // split scope into a namespace and a class part
6339 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6340 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6342 //namespaceName=removeAnonymousScopes(namespaceName);
6343 if (namespaceName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6345 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6346 // merge class and namespace scopes again
6347 scopeName.resize(0);
6348 if (!namespaceName.isEmpty())
6350 if (className.isEmpty())
6352 scopeName=namespaceName;
6354 else if (!relates.isEmpty() || // relates command with explicit scope
6355 !getClass(className)) // class name only exists in a namespace
6357 scopeName=namespaceName+"::"+className;
6361 scopeName=className;
6364 else if (!className.isEmpty())
6366 scopeName=className;
6368 //printf("new scope='%s'\n",qPrint(scopeName));
6370 QCString tempScopeName=scopeName;
6371 ClassDefMutable *cd=getClassMutable(scopeName);
6374 if (funcSpec.isEmpty())
6376 uint32_t argListIndex=0;
6377 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6381 tempScopeName=scopeName+funcSpec;
6384 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6385 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6387 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6388 // rebuild the function declaration (needed to get the scope right).
6389 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6391 if (!funcType.isEmpty())
6393 if (isFunc) // a function -> we use argList for the arguments
6395 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6399 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6404 if (isFunc) // a function => we use argList for the arguments
6406 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6408 else // variable => add 'argument' list
6410 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6414 else // build declaration without scope
6416 if (!funcType.isEmpty()) // but with a type
6418 if (isFunc) // function => omit argument list
6420 funcDecl=funcType+" "+funcName+funcTempList;
6422 else // variable => add 'argument' list
6424 funcDecl=funcType+" "+funcName+funcArgs;
6431 funcDecl=funcName+funcTempList;
6435 funcDecl=funcName+funcArgs;
6440 if (funcType=="template class" && !funcTempList.isEmpty())
6441 return; // ignore explicit template instantiations
6443 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6444 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6445 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6446 namespaceName, className, funcType, funcSpec,
6447 funcName, funcArgs, funcTempList, funcDecl, relates,
6448 exceptions, isRelated, isMemberOf, isFriend, isFunc);
6450 if (!funcName.isEmpty()) // function name is valid
6452 // check if 'className' is actually a scoped enum, in which case we need to
6453 // process it as a global, see issue #6471
6454 bool strongEnum = false;
6456 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6458 for (const auto &imd : *mn)
6460 MemberDefMutable *md = toMemberDefMutable(imd.get());
6461 Definition *mdScope = nullptr;
6462 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
6463 // need filter for the correct scope, see issue #9668
6464 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
6466 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
6468 // pass the scope name name as a 'namespace' to the findGlobalMember function
6469 if (!namespaceName.isEmpty())
6471 namespaceName+="::"+className;
6475 namespaceName=className;
6481 if (funcName.startsWith("operator ")) // strip class scope from cast operator
6483 funcName = substitute(funcName,className+"::","");
6486 if (!funcTempList.isEmpty()) // try with member specialization
6488 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6490 if (mn==0) // try without specialization
6492 mn=Doxygen::memberNameLinkedMap->find(funcName);
6494 if (!isRelated && !strongEnum && mn) // function name already found
6496 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
6497 if (!className.isEmpty()) // class name is valid
6499 if (funcSpec.isEmpty()) // not a member specialization
6501 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6502 funcArgs,funcTempList,exceptions,
6503 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6505 else if (cd) // member specialization
6507 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6511 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6512 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6515 else if (overloaded) // check if the function belongs to only one class
6517 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6519 else // unrelated function with the same name as a member
6521 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6523 QCString fullFuncDecl=funcDecl;
6524 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6525 warn(root->fileName,root->startLine,
6526 "Cannot determine class for function\n%s",
6527 qPrint(fullFuncDecl)
6532 else if (isRelated && !relates.isEmpty())
6534 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
6535 if (className.isEmpty()) className=relates;
6536 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6537 if ((cd=getClassMutable(scopeName)))
6539 bool newMember=TRUE; // assume we have a new member
6540 MemberDefMutable *mdDefine=0;
6542 mn = Doxygen::functionNameLinkedMap->find(funcName);
6545 for (const auto &imd : *mn)
6547 MemberDefMutable *md = toMemberDefMutable(imd.get());
6548 if (md && md->isDefine())
6557 FileDef *fd=root->fileDef();
6559 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==0)
6561 mn=Doxygen::memberNameLinkedMap->add(funcName);
6565 // see if we got another member with matching arguments
6566 MemberDefMutable *rmd_found = 0;
6567 for (const auto &irmd : *mn)
6569 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6572 const ArgumentList &rmdAl = rmd->argumentList();
6575 className!=rmd->getOuterScope()->name() ||
6576 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6577 cd,fd,&root->argList,
6585 if (rmd_found) // member already exists as rmd -> add docs
6587 //printf("addMemberDocs for related member %s\n",qPrint(root->name));
6588 //rmd->setMemberDefTemplateArguments(root->mtArgList);
6589 addMemberDocs(root,rmd_found,funcDecl,0,overloaded,spec);
6593 if (newMember) // need to create a new member
6597 mtype=MemberType_Define;
6598 else if (root->mtype==MethodTypes::Signal)
6599 mtype=MemberType_Signal;
6600 else if (root->mtype==MethodTypes::Slot)
6601 mtype=MemberType_Slot;
6602 else if (root->mtype==MethodTypes::DCOP)
6603 mtype=MemberType_DCOP;
6605 mtype=MemberType_Function;
6609 mdDefine->setHidden(TRUE);
6611 funcArgs=mdDefine->argsString();
6612 funcDecl=funcType + " " + funcName;
6615 //printf("New related name '%s' '%d'\n",qPrint(funcName),
6616 // root->argList ? (int)root->argList->count() : -1);
6618 // first note that we pass:
6619 // (root->tArgLists ? root->tArgLists->last() : 0)
6620 // for the template arguments for the new "member."
6621 // this accurately reflects the template arguments of
6622 // the related function, which don't have to do with
6623 // those of the related class.
6624 auto md = createMemberDef(
6625 root->fileName,root->startLine,root->startColumn,
6626 funcType,funcName,funcArgs,exceptions,
6627 root->protection,root->virt,
6629 isMemberOf ? Relationship::Foreign : Relationship::Related,
6631 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
6632 funcArgs.isEmpty() ? ArgumentList() : root->argList,
6634 auto mmd = toMemberDefMutable(md.get());
6638 mmd->setInitializer(mdDefine->initializer());
6642 // we still have the problem that
6643 // MemberDef::writeDocumentation() in memberdef.cpp
6644 // writes the template argument list for the class,
6645 // as if this member is a member of the class.
6646 // fortunately, MemberDef::writeDocumentation() has
6647 // a special mechanism that allows us to totally
6648 // override the set of template argument lists that
6649 // are printed. We use that and set it to the
6650 // template argument lists of the related function.
6652 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
6654 mmd->setTagInfo(root->tagInfo());
6656 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
6657 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
6659 // try to find the matching line number of the body from the
6660 // global function list
6662 if (root->bodyLine==-1)
6664 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
6667 const MemberDefMutable *rmd_found=0;
6668 for (const auto &irmd : *rmn)
6670 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6673 const ArgumentList &rmdAl = rmd->argumentList();
6674 // check for matching argument lists
6676 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6677 cd,fd,&root->argList,
6687 if (rmd_found) // member found -> copy line number info
6689 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
6690 mmd->setBodyDef(rmd_found->getBodyDef());
6691 //md->setBodyMember(rmd);
6695 if (!found) // line number could not be found or is available in this
6698 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6699 mmd->setBodyDef(fd);
6702 //if (root->mGrpId!=-1)
6704 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
6706 mmd->setMemberClass(cd);
6707 mmd->setMemberSpecifiers(spec);
6708 mmd->setDefinition(funcDecl);
6709 mmd->enableCallGraph(root->callGraph);
6710 mmd->enableCallerGraph(root->callerGraph);
6711 mmd->enableReferencedByRelation(root->referencedByRelation);
6712 mmd->enableReferencesRelation(root->referencesRelation);
6713 mmd->addQualifiers(root->qualifiers);
6714 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6715 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6716 mmd->setDocsForDefinition(!root->proto);
6717 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6718 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6719 mmd->addSectionsToDefinition(root->anchors);
6720 mmd->setMemberGroupId(root->mGrpId);
6721 mmd->setLanguage(root->lang);
6722 mmd->setId(root->id);
6723 //md->setMemberDefTemplateArguments(root->mtArgList);
6724 cd->insertMember(md.get());
6725 cd->insertUsedFile(fd);
6726 mmd->setRefItems(root->sli);
6727 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
6730 addMemberToGroups(root,md.get());
6731 ModuleManager::instance().addMemberToModule(root,md.get());
6733 //printf("Adding member=%s\n",qPrint(md->name()));
6734 mn->push_back(std::move(md));
6736 if (root->relatesType==RelatesType::Duplicate)
6738 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6740 QCString fullFuncDecl=funcDecl;
6741 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6742 warn(root->fileName,root->startLine,
6743 "Cannot determine file/namespace for relatedalso function\n%s",
6744 qPrint(fullFuncDecl)
6751 warn_undoc(root->fileName,root->startLine,
6752 "class '%s' for related function '%s' is not "
6754 qPrint(className),qPrint(funcName)
6758 else if (root->parent() && root->parent()->section==Entry::OBJCIMPL_SEC)
6760 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6762 else // unrelated not overloaded member found
6764 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
6765 if (className.isEmpty() && !globMem)
6767 warn(root->fileName,root->startLine,
6768 "class for member '%s' cannot "
6769 "be found.", qPrint(funcName)
6772 else if (!className.isEmpty() && !globMem)
6774 warn(root->fileName,root->startLine,
6775 "member '%s' of class '%s' cannot be found",
6776 qPrint(funcName),qPrint(className));
6782 // this should not be called
6783 warn(root->fileName,root->startLine,
6784 "member with no name found.");
6789 //----------------------------------------------------------------------
6790 // find the members corresponding to the different documentation blocks
6791 // that are extracted from the sources.
6793 static void filterMemberDocumentation(const Entry *root,const QCString &relates)
6796 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={:#x} root->spec={:#x} root->mGrpId={}",
6797 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
6798 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
6801 QCString type = root->type;
6802 QCString args = root->args;
6803 if ( // detect func variable/typedef to func ptr
6804 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
6807 //printf("Fixing function pointer!\n");
6808 // fix type and argument
6809 args.prepend(type.right(type.length()-i-l));
6810 type=type.left(i+l);
6811 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
6814 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
6815 // detect function types marked as functions
6820 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
6821 if (root->section==Entry::MEMBERDOC_SEC)
6823 //printf("Documentation for inline member '%s' found args='%s'\n",
6824 // qPrint(root->name),qPrint(args));
6825 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
6832 root->name + args + root->exception,
6842 type + " " + root->name + args + root->exception,
6847 else if (root->section==Entry::OVERLOADDOC_SEC)
6849 //printf("Overloaded member %s found\n",qPrint(root->name));
6859 ((root->section==Entry::FUNCTION_SEC // function
6861 (root->section==Entry::VARIABLE_SEC && // variable
6862 !type.isEmpty() && // with a type
6863 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
6864 // (to skip forward declaration of class etc.)
6869 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
6870 // qPrint(root->name),qPrint(args),qPrint(root->exception));
6871 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
6872 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
6873 if (type=="friend class" || type=="friend struct" ||
6874 type=="friend union")
6880 type+" "+root->name,
6884 else if (!type.isEmpty())
6890 type+" "+ root->inside + root->name + args + root->exception,
6899 root->inside + root->name + args + root->exception,
6903 else if (root->section==Entry::DEFINE_SEC && !relates.isEmpty())
6913 else if (root->section==Entry::VARIABLEDOC_SEC)
6915 //printf("Documentation for variable %s found\n",qPrint(root->name));
6916 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
6925 else if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
6926 root->section==Entry::INCLUDED_SERVICE_SEC)
6932 type + " " + root->name,
6939 //printf("skip section\n");
6943 static void findMemberDocumentation(const Entry *root)
6945 if (root->section==Entry::MEMBERDOC_SEC ||
6946 root->section==Entry::OVERLOADDOC_SEC ||
6947 root->section==Entry::FUNCTION_SEC ||
6948 root->section==Entry::VARIABLE_SEC ||
6949 root->section==Entry::VARIABLEDOC_SEC ||
6950 root->section==Entry::DEFINE_SEC ||
6951 root->section==Entry::INCLUDED_SERVICE_SEC ||
6952 root->section==Entry::EXPORTED_INTERFACE_SEC
6956 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
6958 filterMemberDocumentation(root,"");
6960 filterMemberDocumentation(root,root->relates);
6962 for (const auto &e : root->children())
6964 if (e->section!=Entry::ENUM_SEC)
6966 findMemberDocumentation(e.get());
6971 //----------------------------------------------------------------------
6973 static void findObjCMethodDefinitions(const Entry *root)
6976 for (const auto &objCImpl : root->children())
6978 if (objCImpl->section==Entry::OBJCIMPL_SEC)
6980 for (const auto &objCMethod : objCImpl->children())
6982 if (objCMethod->section==Entry::FUNCTION_SEC)
6984 //Printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
6985 findMember(objCMethod.get(),
6986 objCMethod->relates,
6989 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
6991 objCMethod->section=Entry::EMPTY_SEC;
6998 //----------------------------------------------------------------------
6999 // find and add the enumeration to their classes, namespaces or files
7001 static void findEnums(const Entry *root)
7003 if (root->section==Entry::ENUM_SEC)
7005 ClassDefMutable *cd=0;
7007 NamespaceDefMutable *nd=0;
7008 MemberNameLinkedMap *mnsd=0;
7010 bool isRelated=FALSE;
7011 bool isMemberOf=FALSE;
7012 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7018 if ((i=root->name.findRev("::"))!=-1) // scope is specified
7020 scope=root->name.left(i); // extract scope
7021 name=root->name.right(root->name.length()-i-2); // extract name
7022 if ((cd=getClassMutable(scope))==0)
7024 nd=toNamespaceDefMutable(buildScopeFromQualifiedName(root->name.left(i+2),root->lang,root->tagInfo()));
7027 else // no scope, check the scope in which the docs where found
7029 if (( root->parent()->section & Entry::SCOPE_MASK )
7030 && !root->parent()->name.isEmpty()
7031 ) // found enum docs inside a compound
7033 scope=root->parent()->name;
7034 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7039 if (!root->relates.isEmpty())
7040 { // related member, prefix user specified scope
7042 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7043 if (getClass(root->relates)==0 && !scope.isEmpty())
7044 scope=mergeScopes(scope,root->relates);
7046 scope=root->relates;
7047 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7050 if (cd && !name.isEmpty()) // found a enum inside a compound
7052 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7054 mnsd=Doxygen::memberNameLinkedMap;
7057 else if (nd) // found enum inside namespace
7059 mnsd=Doxygen::functionNameLinkedMap;
7062 else // found a global enum
7065 mnsd=Doxygen::functionNameLinkedMap;
7069 if (!name.isEmpty())
7072 auto md = createMemberDef(
7073 root->fileName,root->startLine,root->startColumn,
7074 QCString(),name,QCString(),QCString(),
7075 root->protection,Specifier::Normal,FALSE,
7076 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7077 MemberType_Enumeration,
7078 ArgumentList(),ArgumentList(),root->metaData);
7079 auto mmd = toMemberDefMutable(md.get());
7080 mmd->setTagInfo(root->tagInfo());
7081 mmd->setLanguage(root->lang);
7082 mmd->setId(root->id);
7083 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7084 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7085 mmd->setBodyDef(root->fileDef());
7086 mmd->setMemberSpecifiers(root->spec);
7087 mmd->setEnumBaseType(root->args);
7088 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7089 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7090 mmd->addSectionsToDefinition(root->anchors);
7091 mmd->setMemberGroupId(root->mGrpId);
7092 mmd->enableCallGraph(root->callGraph);
7093 mmd->enableCallerGraph(root->callerGraph);
7094 mmd->enableReferencedByRelation(root->referencedByRelation);
7095 mmd->enableReferencesRelation(root->referencesRelation);
7096 mmd->addQualifiers(root->qualifiers);
7097 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7098 mmd->setRefItems(root->sli);
7099 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7102 QCString baseType = root->args;
7103 if (!baseType.isEmpty())
7105 baseType.prepend(" : ");
7110 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7112 mmd->setDefinition(name+baseType);
7116 mmd->setDefinition(nd->name()+"::"+name+baseType);
7118 //printf("definition=%s\n",md->definition());
7120 mmd->setNamespace(nd);
7121 nd->insertMember(md.get());
7124 // even if we have already added the enum to a namespace, we still
7125 // also want to add it to other appropriate places such as file
7127 if (isGlobal && (nd==0 || !nd->isAnonymous()))
7129 if (!defSet) mmd->setDefinition(name+baseType);
7130 if (fd==0 && root->parent())
7132 fd=root->parent()->fileDef();
7136 mmd->setFileDef(fd);
7137 fd->insertMember(md.get());
7142 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7144 mmd->setDefinition(name+baseType);
7148 mmd->setDefinition(cd->name()+"::"+name+baseType);
7150 cd->insertMember(md.get());
7151 cd->insertUsedFile(fd);
7153 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7154 mmd->setDocsForDefinition(!root->proto);
7155 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7156 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7158 //printf("Adding member=%s\n",qPrint(md->name()));
7159 addMemberToGroups(root,md.get());
7160 ModuleManager::instance().addMemberToModule(root,md.get());
7162 MemberName *mn = mnsd->add(name);
7163 mn->push_back(std::move(md));
7168 for (const auto &e : root->children()) findEnums(e.get());
7172 //----------------------------------------------------------------------
7174 static void addEnumValuesToEnums(const Entry *root)
7176 if (root->section==Entry::ENUM_SEC)
7177 // non anonymous enumeration
7179 ClassDefMutable *cd=0;
7181 NamespaceDefMutable *nd=0;
7182 MemberNameLinkedMap *mnsd=0;
7184 bool isRelated=FALSE;
7185 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7191 if ((i=root->name.findRev("::"))!=-1) // scope is specified
7193 scope=root->name.left(i); // extract scope
7194 name=root->name.right(root->name.length()-i-2); // extract name
7195 if ((cd=getClassMutable(scope))==0)
7197 nd=toNamespaceDefMutable(buildScopeFromQualifiedName(root->name.left(i+2),root->lang,root->tagInfo()));
7200 else // no scope, check the scope in which the docs where found
7202 if (( root->parent()->section & Entry::SCOPE_MASK )
7203 && !root->parent()->name.isEmpty()
7204 ) // found enum docs inside a compound
7206 scope=root->parent()->name;
7207 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7212 if (!root->relates.isEmpty())
7213 { // related member, prefix user specified scope
7215 if (getClassMutable(root->relates)==0 && !scope.isEmpty())
7216 scope=mergeScopes(scope,root->relates);
7218 scope=root->relates;
7219 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7222 if (cd && !name.isEmpty()) // found a enum inside a compound
7224 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7226 mnsd=Doxygen::memberNameLinkedMap;
7229 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7231 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7232 mnsd=Doxygen::functionNameLinkedMap;
7235 else // found a global enum
7238 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7239 mnsd=Doxygen::functionNameLinkedMap;
7243 if (!name.isEmpty())
7245 //printf("** name=%s\n",qPrint(name));
7246 MemberName *mn = mnsd->find(name); // for all members with this name
7249 struct EnumValueInfo
7251 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7252 name(n), member(std::move(md)) {}
7254 std::unique_ptr<MemberDef> member;
7256 std::vector< EnumValueInfo > extraMembers;
7257 // for each enum in this list
7258 for (const auto &imd : *mn)
7260 MemberDefMutable *md = toMemberDefMutable(imd.get());
7261 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7262 if (md && md->isEnumerate() && !root->children().empty())
7264 //printf(" enum with %zu children\n",root->children().size());
7265 for (const auto &e : root->children())
7267 SrcLangExt sle = root->lang;
7268 bool isJavaLike = sle==SrcLangExt_CSharp || sle==SrcLangExt_Java || sle==SrcLangExt_XML;
7270 (root->spec&Entry::Strong)
7273 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7274 // values are only visible inside the enum scope, so we must create
7275 // them here and only add them to the enum
7276 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7277 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7278 QCString qualifiedName = root->name;
7281 qualifiedName=substitute(qualifiedName,"::",".");
7283 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7285 QCString fileName = e->fileName;
7286 if (fileName.isEmpty() && e->tagInfo())
7288 fileName = e->tagInfo()->tagName;
7290 auto fmd = createMemberDef(
7291 fileName,e->startLine,e->startColumn,
7292 e->type,e->name,e->args,QCString(),
7293 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7294 MemberType_EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7295 auto fmmd = toMemberDefMutable(fmd.get());
7296 NamespaceDef *mnd = md->getNamespaceDef();
7297 if (md->getClassDef())
7298 fmmd->setMemberClass(md->getClassDef());
7299 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7300 fmmd->setNamespace(mnd);
7301 else if (md->getFileDef())
7302 fmmd->setFileDef(md->getFileDef());
7303 fmmd->setOuterScope(md->getOuterScope());
7304 fmmd->setTagInfo(e->tagInfo());
7305 fmmd->setLanguage(e->lang);
7307 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7308 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7309 fmmd->addSectionsToDefinition(e->anchors);
7310 std::string init = e->initializer.str();
7311 fmmd->setInitializer(init.c_str());
7312 fmmd->setMaxInitLines(e->initLines);
7313 fmmd->setMemberGroupId(e->mGrpId);
7314 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7315 fmmd->setRefItems(e->sli);
7317 md->insertEnumField(fmd.get());
7318 fmmd->setEnumScope(md,TRUE);
7319 extraMembers.push_back(EnumValueInfo(e->name,std::move(fmd)));
7324 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7326 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7327 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7328 // get list of members with the same name as the field
7330 for (const auto &ifmd : *fmn)
7332 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7333 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7335 //printf("found enum value with same name %s in scope %s\n",
7336 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7337 if (nd && !nd->isAnonymous())
7339 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7341 const NamespaceDef *fnd=fmd->getNamespaceDef();
7342 if (fnd==nd) // enum value is inside a namespace
7344 md->insertEnumField(fmd);
7345 fmd->setEnumScope(md);
7351 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7353 const FileDef *ffd=fmd->getFileDef();
7354 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7356 md->insertEnumField(fmd);
7357 fmd->setEnumScope(md);
7361 else if (isRelated && cd) // reparent enum value to
7362 // match the enum's scope
7364 md->insertEnumField(fmd); // add field def to list
7365 fmd->setEnumScope(md); // cross ref with enum name
7366 fmd->setEnumClassScope(cd); // cross ref with enum name
7367 fmd->setOuterScope(cd);
7369 cd->insertMember(fmd);
7373 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7375 const ClassDef *fcd=fmd->getClassDef();
7376 if (fcd==cd) // enum value is inside a class
7378 //printf("Inserting enum field %s in enum scope %s\n",
7379 // qPrint(fmd->name()),qPrint(md->name()));
7380 md->insertEnumField(fmd); // add field def to list
7381 fmd->setEnumScope(md); // cross ref with enum name
7392 // move the newly added members into mn
7393 for (auto &e : extraMembers)
7395 MemberName *emn=mnsd->add(e.name);
7396 emn->push_back(std::move(e.member));
7403 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7407 //----------------------------------------------------------------------
7409 static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7412 // documentation outside a compound overrides the documentation inside it
7414 md->setDocumentation(root->doc,root->docFile,root->docLine);
7415 md->setDocsForDefinition(!root->proto);
7418 // brief descriptions inside a compound override the documentation
7421 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7424 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7426 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7429 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7431 md->setMemberGroupId(root->mGrpId);
7434 md->addSectionsToDefinition(root->anchors);
7435 md->setRefItems(root->sli);
7437 const GroupDef *gd=md->getGroupDef();
7438 if (gd==0 && !root->groups.empty()) // member not grouped but out-of-line documentation is
7440 addMemberToGroups(root,md);
7442 ModuleManager::instance().addMemberToModule(root,md);
7445 //----------------------------------------------------------------------
7446 // Search for the name in the associated groups. If a matching member
7447 // definition exists, then add the documentation to it and return TRUE,
7450 static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
7452 for (const auto &g : root->groups)
7454 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
7457 MemberList *ml = gd->getMemberList(MemberListType_decEnumMembers);
7460 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
7463 addEnumDocs(root,md);
7473 //----------------------------------------------------------------------
7474 // find the documentation blocks for the enumerations
7476 static void findEnumDocumentation(const Entry *root)
7478 if (root->section==Entry::ENUMDOC_SEC
7479 && !root->name.isEmpty()
7480 && root->name.at(0)!='@' // skip anonymous enums
7486 if ((i=root->name.findRev("::"))!=-1) // scope is specified as part of the name
7488 name=root->name.right(root->name.length()-i-2); // extract name
7489 scope=root->name.left(i); // extract scope
7490 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7492 else // just the name
7496 if (( root->parent()->section & Entry::SCOPE_MASK )
7497 && !root->parent()->name.isEmpty()
7498 ) // found enum docs inside a compound
7500 if (!scope.isEmpty()) scope.prepend("::");
7501 scope.prepend(root->parent()->name);
7503 const ClassDef *cd = getClass(scope);
7504 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
7505 const FileDef *fd = root->fileDef();
7506 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
7507 name,scope,root->parent()->name,
7508 cd ? cd->name() : QCString("<none>"),
7509 nd ? nd->name() : QCString("<none>"),
7510 fd ? fd->name() : QCString("<none>"));
7512 if (!name.isEmpty())
7515 if (root->groups.empty())
7520 mn = Doxygen::memberNameLinkedMap->find(name);
7524 mn = Doxygen::functionNameLinkedMap->find(name);
7528 for (const auto &imd : *mn)
7530 MemberDefMutable *md = toMemberDefMutable(imd.get());
7531 if (md && md->isEnumerate())
7533 const ClassDef *mcd = md->getClassDef();
7534 const NamespaceDef *mnd = md->getNamespaceDef();
7535 const FileDef *mfd = md->getFileDef();
7538 AUTO_TRACE_ADD("Match found for class scope");
7539 addEnumDocs(root,md);
7543 else if (cd==0 && mcd==0 && nd!=0 && mnd==nd)
7545 AUTO_TRACE_ADD("Match found for namespace scope");
7546 addEnumDocs(root,md);
7550 else if (cd==0 && nd==0 && mcd==0 && mnd==0 && fd==mfd)
7552 AUTO_TRACE_ADD("Match found for global scope");
7553 addEnumDocs(root,md);
7563 found = tryAddEnumDocsToGroupMember(root, name);
7567 warn(root->fileName,root->startLine,
7568 "Documentation for undefined enum '%s' found.",
7574 for (const auto &e : root->children()) findEnumDocumentation(e.get());
7577 // search for each enum (member or function) in mnl if it has documented
7579 static void findDEV(const MemberNameLinkedMap &mnsd)
7581 // for each member name
7582 for (const auto &mn : mnsd)
7584 // for each member definition
7585 for (const auto &imd : *mn)
7587 MemberDefMutable *md = toMemberDefMutable(imd.get());
7588 if (md && md->isEnumerate()) // member is an enum
7590 int documentedEnumValues=0;
7591 // for each enum value
7592 for (const auto &fmd : md->enumFieldList())
7594 if (fmd->isLinkableInProject()) documentedEnumValues++;
7596 // at least one enum value is documented
7597 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
7603 // search for each enum (member or function) if it has documented enum
7605 static void findDocumentedEnumValues()
7607 findDEV(*Doxygen::memberNameLinkedMap);
7608 findDEV(*Doxygen::functionNameLinkedMap);
7611 //----------------------------------------------------------------------
7613 static void addMembersToIndex()
7615 auto &index = Index::instance();
7616 // for each class member name
7617 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7619 // for each member definition
7620 for (const auto &md : *mn)
7622 index.addClassMemberNameToIndex(md.get());
7623 if (md->getModuleDef())
7625 index.addModuleMemberNameToIndex(md.get());
7629 // for each file/namespace function name
7630 for (const auto &mn : *Doxygen::functionNameLinkedMap)
7632 // for each member definition
7633 for (const auto &md : *mn)
7635 if (md->getNamespaceDef())
7637 index.addNamespaceMemberNameToIndex(md.get());
7641 index.addFileMemberNameToIndex(md.get());
7643 if (md->getModuleDef())
7645 index.addModuleMemberNameToIndex(md.get());
7650 index.sortMemberIndexLists();
7653 //----------------------------------------------------------------------
7655 static void addToIndices()
7657 for (const auto &cd : *Doxygen::classLinkedMap)
7659 if (cd->isLinkableInProject())
7661 Doxygen::indexList->addIndexItem(cd.get(),0);
7662 if (Doxygen::searchIndex)
7664 Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7665 Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7670 for (const auto &cd : *Doxygen::conceptLinkedMap)
7672 if (cd->isLinkableInProject())
7674 Doxygen::indexList->addIndexItem(cd.get(),0);
7675 if (Doxygen::searchIndex)
7677 Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7678 Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7683 for (const auto &nd : *Doxygen::namespaceLinkedMap)
7685 if (nd->isLinkableInProject())
7687 Doxygen::indexList->addIndexItem(nd.get(),0);
7688 if (Doxygen::searchIndex)
7690 Doxygen::searchIndex->setCurrentDoc(nd.get(),nd->anchor(),FALSE);
7691 Doxygen::searchIndex->addWord(nd->localName(),TRUE);
7696 for (const auto &fn : *Doxygen::inputNameLinkedMap)
7698 for (const auto &fd : *fn)
7700 if (Doxygen::searchIndex && fd->isLinkableInProject())
7702 Doxygen::searchIndex->setCurrentDoc(fd.get(),fd->anchor(),FALSE);
7703 Doxygen::searchIndex->addWord(fd->localName(),TRUE);
7708 for (const auto &gd : *Doxygen::groupLinkedMap)
7710 if (gd->isLinkableInProject())
7712 Doxygen::indexList->addIndexItem(gd.get(),0,QCString(),gd->groupTitle());
7713 if (Doxygen::searchIndex)
7715 Doxygen::searchIndex->setCurrentDoc(gd.get(),gd->anchor(),FALSE);
7716 std::string title = gd->groupTitle().str();
7717 static const reg::Ex re(R"(\a[\w-]*)");
7718 reg::Iterator it(title,re);
7720 for (; it!=end ; ++it)
7722 const auto &match = *it;
7723 std::string matchStr = match.str();
7724 Doxygen::searchIndex->addWord(matchStr.c_str(),TRUE);
7730 for (const auto &pd : *Doxygen::pageLinkedMap)
7732 if (pd->isLinkableInProject())
7734 Doxygen::indexList->addIndexItem(pd.get(),0,QCString(),filterTitle(pd->title()));
7738 auto addMemberToSearchIndex = [](const MemberDef *md)
7740 if (Doxygen::searchIndex)
7742 Doxygen::searchIndex->setCurrentDoc(md,md->anchor(),FALSE);
7743 QCString ln=md->localName();
7744 QCString qn=md->qualifiedName();
7745 Doxygen::searchIndex->addWord(ln,TRUE);
7748 Doxygen::searchIndex->addWord(qn,TRUE);
7749 if (md->getClassDef())
7751 Doxygen::searchIndex->addWord(md->getClassDef()->displayName(),TRUE);
7753 if (md->getNamespaceDef())
7755 Doxygen::searchIndex->addWord(md->getNamespaceDef()->displayName(),TRUE);
7761 auto getScope = [](const MemberDef *md)
7763 const Definition *scope = 0;
7764 if (md->getGroupDef()) scope = md->getGroupDef();
7765 else if (md->getClassDef()) scope = md->getClassDef();
7766 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
7767 else if (md->getFileDef()) scope = md->getFileDef();
7771 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
7773 if (md->isLinkableInProject())
7775 if (!(md->isEnumerate() && md->isAnonymous()))
7777 Doxygen::indexList->addIndexItem(getScope(md),md);
7778 addMemberToSearchIndex(md);
7780 if (md->isEnumerate())
7782 for (const auto &fmd : md->enumFieldList())
7784 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
7785 addMemberToSearchIndex(fmd);
7791 // for each class member name
7792 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7794 // for each member definition
7795 for (const auto &md : *mn)
7797 addMemberToIndices(md.get());
7800 // for each file/namespace function name
7801 for (const auto &mn : *Doxygen::functionNameLinkedMap)
7803 // for each member definition
7804 for (const auto &md : *mn)
7806 addMemberToIndices(md.get());
7811 //----------------------------------------------------------------------
7813 static void vhdlCorrectMemberProperties()
7815 // for each member name
7816 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7818 // for each member definition
7819 for (const auto &imd : *mn)
7821 MemberDefMutable *md = toMemberDefMutable(imd.get());
7824 VhdlDocGen::correctMemberProperties(md);
7828 // for each member name
7829 for (const auto &mn : *Doxygen::functionNameLinkedMap)
7831 // for each member definition
7832 for (const auto &imd : *mn)
7834 MemberDefMutable *md = toMemberDefMutable(imd.get());
7837 VhdlDocGen::correctMemberProperties(md);
7843 // recursive helper function looking for reimplements/implemented
7844 // by relations between class cd and direct or indirect base class bcd
7845 static void computeMemberRelationsForBaseClass(const ClassDef *cd,const BaseClassDef *bcd)
7847 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
7849 for (const auto &imd : *mn) // for each member with a given name
7851 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
7852 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
7854 ClassDef *mbcd = bcd->classDef;
7855 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
7857 const auto &bmn = mbcd->memberNameInfoLinkedMap();
7858 const auto &bmni = bmn.find(mn->memberName());
7859 if (bmni) // there are base class members with the same name
7861 for (const auto &ibmd : *bmni) // for base class member with that name
7863 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
7864 if (bmd) // not part of an inline namespace
7866 if (bmd->virtualness()!=Specifier::Normal ||
7867 bmd->getLanguage()==SrcLangExt_Python ||
7868 bmd->getLanguage()==SrcLangExt_Java ||
7869 bmd->getLanguage()==SrcLangExt_PHP ||
7870 mbcd->compoundType()==ClassDef::Interface ||
7871 mbcd->compoundType()==ClassDef::Protocol)
7873 const ArgumentList &bmdAl = bmd->argumentList();
7874 const ArgumentList &mdAl = md->argumentList();
7875 //printf(" Base argList='%s'\n Super argList='%s'\n",
7876 // qPrint(argListToString(bmdAl)),
7877 // qPrint(argListToString(mdAl))
7880 bmd->getLanguage()==SrcLangExt_Python ||
7881 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
7882 md->getOuterScope(), md->getFileDef(), &mdAl,
7883 TRUE,bmd->getLanguage()
7887 //printf("match!\n");
7888 const MemberDef *rmd = md->reimplements();
7889 if (rmd==0) // not already assigned
7891 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
7892 md->setReimplements(bmd);
7894 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
7895 bmd->insertReimplementedBy(md);
7899 //printf("no match!\n");
7910 // do also for indirect base classes
7911 for (const auto &bbcd : bcd->classDef->baseClasses())
7913 computeMemberRelationsForBaseClass(cd,&bbcd);
7917 //----------------------------------------------------------------------
7918 // computes the relation between all members. For each member 'm'
7919 // the members that override the implementation of 'm' are searched and
7920 // the member that 'm' overrides is searched.
7922 static void computeMemberRelations()
7924 for (const auto &cd : *Doxygen::classLinkedMap)
7926 if (cd->isLinkable())
7928 for (const auto &bcd : cd->baseClasses())
7930 computeMemberRelationsForBaseClass(cd.get(),&bcd);
7936 //----------------------------------------------------------------------------
7938 static void createTemplateInstanceMembers()
7941 for (const auto &cd : *Doxygen::classLinkedMap)
7943 // that is a template
7944 for (const auto &ti : cd->getTemplateInstances())
7946 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
7949 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
7955 //----------------------------------------------------------------------------
7957 static void mergeCategories()
7959 // merge members of categories into the class they extend
7960 for (const auto &cd : *Doxygen::classLinkedMap)
7962 int i=cd->name().find('(');
7963 if (i!=-1) // it is an Objective-C category
7965 QCString baseName=cd->name().left(i);
7966 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
7969 //printf("*** merging members of category %s into %s\n",
7970 // qPrint(cd->name()),qPrint(baseClass->name()));
7971 baseClass->mergeCategory(cd.get());
7977 // builds the list of all members for each class
7979 static void buildCompleteMemberLists()
7981 // merge the member list of base classes into the inherited classes.
7982 for (const auto &cd : *Doxygen::classLinkedMap)
7984 if (// !cd->isReference() && // not an external class
7985 cd->subClasses().empty() && // is a root of the hierarchy
7986 !cd->baseClasses().empty()) // and has at least one base class
7988 ClassDefMutable *cdm = toClassDefMutable(cd.get());
7991 //printf("*** merging members for %s\n",qPrint(cd->name()));
7992 cdm->mergeMembers();
7996 // now sort the member list of all members for all classes.
7997 for (const auto &cd : *Doxygen::classLinkedMap)
7999 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8002 cdm->sortAllMembersList();
8007 //----------------------------------------------------------------------------
8009 static void generateFileSources()
8011 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8013 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8014 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8017 msg("Generating code for file %s...\n",qPrint(fd->docName()));
8018 fd->writeSourceHeader(ol);
8019 fd->writeSourceBody(ol,parser);
8020 fd->writeSourceFooter(ol);
8022 else if (parseSources)
8024 msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8025 fd->parseSource(parser);
8028 if (!Doxygen::inputNameLinkedMap->empty())
8031 if (Doxygen::clangAssistedParsing)
8033 StringUnorderedSet processedFiles;
8035 // create a dictionary with files to process
8036 StringUnorderedSet filesToProcess;
8038 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8040 for (const auto &fd : *fn)
8042 filesToProcess.insert(fd->absFilePath().str());
8045 // process source files (and their include dependencies)
8046 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8048 for (const auto &fd : *fn)
8050 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt_Cpp &&
8051 (fd->generateSourceFile() ||
8052 (!fd->isReference() && Doxygen::parseSourcesNeeded)
8056 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8057 clangParser->parse();
8058 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8060 for (auto incFile : clangParser->filesInSameTU())
8062 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8063 fd->absFilePath()!=QCString(incFile) && // not same file
8064 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8066 StringVector moreFiles;
8068 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
8069 if (ifd && !ifd->isReference())
8071 processSourceFile(ifd,*g_outputList,clangParser.get());
8072 processedFiles.insert(incFile);
8076 processedFiles.insert(fd->absFilePath().str());
8080 // process remaining files
8081 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8083 for (const auto &fd : *fn)
8085 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8087 if (fd->getLanguage()==SrcLangExt_Cpp) // C/C++ file, use clang parser
8089 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8090 clangParser->parse();
8091 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8093 else // non C/C++ file, use built-in parser
8095 processSourceFile(fd.get(),*g_outputList,nullptr);
8104 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8107 msg("Generating code files using %zu threads.\n",numThreads);
8108 struct SourceContext
8110 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8111 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8113 bool generateSourceFile;
8116 ThreadPool threadPool(numThreads);
8117 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8118 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8120 for (const auto &fd : *fn)
8122 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8123 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8124 auto processFile = [ctx]()
8126 if (ctx->generateSourceFile)
8128 msg("Generating code for file %s...\n",qPrint(ctx->fd->docName()));
8132 msg("Parsing code for file %s...\n",qPrint(ctx->fd->docName()));
8134 StringVector filesInSameTu;
8135 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8136 if (ctx->generateSourceFile) // sources need to be shown in the output
8138 ctx->fd->writeSourceHeader(ctx->ol);
8139 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8140 ctx->fd->writeSourceFooter(ctx->ol);
8142 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8143 // we needed to parse the sources even if we do not show them
8145 ctx->fd->parseSource(nullptr);
8149 results.emplace_back(threadPool.queue(processFile));
8152 for (auto &f : results)
8157 else // single threaded version
8159 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8161 for (const auto &fd : *fn)
8163 StringVector filesInSameTu;
8164 fd->getAllIncludeFilesRecursively(filesInSameTu);
8165 processSourceFile(fd.get(),*g_outputList,nullptr);
8173 //----------------------------------------------------------------------------
8175 static void generateFileDocs()
8177 if (Index::instance().numDocumentedFiles()==0) return;
8179 if (!Doxygen::inputNameLinkedMap->empty())
8181 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8182 if (numThreads>1) // multi threaded processing
8186 DocContext(FileDef *fd_,const OutputList &ol_)
8187 : fd(fd_), ol(ol_) {}
8191 ThreadPool threadPool(numThreads);
8192 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8193 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8195 for (const auto &fd : *fn)
8197 bool doc = fd->isLinkableInProject();
8200 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8201 auto processFile = [ctx]() {
8202 msg("Generating docs for file %s...\n",qPrint(ctx->fd->docName()));
8203 ctx->fd->writeDocumentation(ctx->ol);
8206 results.emplace_back(threadPool.queue(processFile));
8210 for (auto &f : results)
8215 else // single threaded processing
8217 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8219 for (const auto &fd : *fn)
8221 bool doc = fd->isLinkableInProject();
8224 msg("Generating docs for file %s...\n",qPrint(fd->docName()));
8225 fd->writeDocumentation(*g_outputList);
8233 //----------------------------------------------------------------------------
8235 static void addSourceReferences()
8237 // add source references for class definitions
8238 for (const auto &cd : *Doxygen::classLinkedMap)
8240 const FileDef *fd=cd->getBodyDef();
8241 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8243 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8246 // add source references for concept definitions
8247 for (const auto &cd : *Doxygen::conceptLinkedMap)
8249 const FileDef *fd=cd->getBodyDef();
8250 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8252 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8255 // add source references for namespace definitions
8256 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8258 const FileDef *fd=nd->getBodyDef();
8259 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8261 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),0);
8265 // add source references for member names
8266 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8268 for (const auto &md : *mn)
8270 //printf("class member %s: def=%s body=%d link?=%d\n",
8271 // qPrint(md->name()),
8272 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8273 // md->getStartBodyLine(),md->isLinkableInProject());
8274 const FileDef *fd=md->getBodyDef();
8276 md->getStartDefLine()!=-1 &&
8277 md->isLinkableInProject() &&
8278 (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8281 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8282 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8283 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8287 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8289 for (const auto &md : *mn)
8291 const FileDef *fd=md->getBodyDef();
8292 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8293 // qPrint(md->name()),
8294 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8295 // md->isLinkableInProject(),
8296 // Doxygen::parseSourcesNeeded);
8298 md->getStartDefLine()!=-1 &&
8299 md->isLinkableInProject() &&
8300 (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8303 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8304 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8305 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8311 //----------------------------------------------------------------------------
8313 // add the macro definitions found during preprocessing as file members
8314 static void buildDefineList()
8317 for (const auto &s : g_inputFiles)
8319 auto it = Doxygen::macroDefinitions.find(s);
8320 if (it!=Doxygen::macroDefinitions.end())
8322 for (const auto &def : it->second)
8324 auto md = createMemberDef(
8325 def.fileName,def.lineNr,def.columnNr,
8326 "#define",def.name,def.args,QCString(),
8327 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType_Define,
8328 ArgumentList(),ArgumentList(),"");
8329 auto mmd = toMemberDefMutable(md.get());
8331 if (!def.args.isEmpty())
8333 mmd->moveArgumentList(stringToArgumentList(SrcLangExt_Cpp, def.args));
8335 mmd->setInitializer(def.definition);
8336 mmd->setFileDef(def.fileDef);
8337 mmd->setDefinition("#define "+def.name);
8339 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8342 def.fileDef->insertMember(md.get());
8344 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8345 mn->push_back(std::move(md));
8351 //----------------------------------------------------------------------------
8353 static void sortMemberLists()
8355 // sort class member lists
8356 for (const auto &cd : *Doxygen::classLinkedMap)
8358 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8361 cdm->sortMemberLists();
8365 // sort namespace member lists
8366 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8368 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8371 ndm->sortMemberLists();
8375 // sort file member lists
8376 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8378 for (const auto &fd : *fn)
8380 fd->sortMemberLists();
8384 // sort group member lists
8385 for (const auto &gd : *Doxygen::groupLinkedMap)
8387 gd->sortMemberLists();
8390 ModuleManager::instance().sortMemberLists();
8393 //----------------------------------------------------------------------------
8395 static bool isSymbolHidden(const Definition *d)
8397 bool hidden = d->isHidden();
8398 const Definition *parent = d->getOuterScope();
8399 return parent ? hidden || isSymbolHidden(parent) : hidden;
8402 static void computeTooltipTexts()
8404 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8407 ThreadPool threadPool(numThreads);
8408 std::vector < std::future< void > > results;
8410 for (const auto &[name,symList] : *Doxygen::symbolMap)
8412 for (const auto &def : symList)
8414 DefinitionMutable *dm = toDefinitionMutable(def);
8415 if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
8417 auto processTooltip = [dm]() {
8418 dm->computeTooltip();
8420 results.emplace_back(threadPool.queue(processTooltip));
8424 // wait for the results
8425 for (auto &f : results)
8432 for (const auto &[name,symList] : *Doxygen::symbolMap)
8434 for (const auto &def : symList)
8436 DefinitionMutable *dm = toDefinitionMutable(def);
8437 if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
8439 dm->computeTooltip();
8446 //----------------------------------------------------------------------------
8448 static void setAnonymousEnumType()
8450 for (const auto &cd : *Doxygen::classLinkedMap)
8452 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8455 cdm->setAnonymousEnumType();
8460 //----------------------------------------------------------------------------
8462 static void countMembers()
8464 for (const auto &cd : *Doxygen::classLinkedMap)
8466 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8469 cdm->countMembers();
8473 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8475 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8478 ndm->countMembers();
8482 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8484 for (const auto &fd : *fn)
8490 for (const auto &gd : *Doxygen::groupLinkedMap)
8495 auto &mm = ModuleManager::instance();
8500 //----------------------------------------------------------------------------
8501 // generate the documentation for all classes
8503 static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
8505 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8506 if (numThreads>1) // multi threaded processing
8510 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
8511 : cd(cd_), ol(ol_) {}
8512 ClassDefMutable *cd;
8515 ThreadPool threadPool(numThreads);
8516 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8517 for (const auto &cd : classList)
8519 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8520 if ((cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8521 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8522 ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8525 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
8526 auto processFile = [ctx]()
8528 msg("Generating docs for compound %s...\n",qPrint(ctx->cd->name()));
8530 // skip external references, anonymous compounds and
8531 // template instances
8532 if ( ctx->cd->isLinkableInProject() && ctx->cd->templateMaster()==0)
8534 ctx->cd->writeDocumentation(ctx->ol);
8535 ctx->cd->writeMemberList(ctx->ol);
8538 // even for undocumented classes, the inner classes can be documented.
8539 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
8542 results.emplace_back(threadPool.queue(processFile));
8545 for (auto &f : results)
8550 else // single threaded processing
8552 for (const auto &cd : classList)
8554 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8555 if ((cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8556 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8557 ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8560 // skip external references, anonymous compounds and
8561 // template instances
8562 if ( cd->isLinkableInProject() && cd->templateMaster()==0)
8564 msg("Generating docs for compound %s...\n",qPrint(cd->name()));
8566 cd->writeDocumentation(*g_outputList);
8567 cd->writeMemberList(*g_outputList);
8569 // even for undocumented classes, the inner classes can be documented.
8570 cd->writeDocumentationForInnerClasses(*g_outputList);
8576 static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
8579 for (const auto &innerCdi : cd->getClasses())
8581 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
8582 if (innerCd && innerCd->isLinkableInProject() && innerCd->templateMaster()==0 &&
8583 protectionLevelVisible(innerCd->protection()) &&
8584 !innerCd->isEmbeddedInOuterScope()
8587 list.push_back(innerCd);
8588 addClassAndNestedClasses(list,innerCd);
8593 static void generateClassDocs()
8595 std::vector<ClassDefMutable*> classList;
8596 for (const auto &cdi : *Doxygen::classLinkedMap)
8598 ClassDefMutable *cd = toClassDefMutable(cdi.get());
8599 if (cd && (cd->getOuterScope()==0 ||
8600 cd->getOuterScope()->definitionType()!=Definition::TypeClass))
8602 addClassAndNestedClasses(classList,cd);
8605 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
8607 ClassDefMutable *cd = toClassDefMutable(cdi.get());
8608 if (cd && (cd->getOuterScope()==0 ||
8609 cd->getOuterScope()->definitionType()!=Definition::TypeClass))
8611 addClassAndNestedClasses(classList,cd);
8614 generateDocsForClassList(classList);
8617 //----------------------------------------------------------------------------
8619 static void generateConceptDocs()
8621 for (const auto &cdi : *Doxygen::conceptLinkedMap)
8623 ConceptDefMutable *cd=toConceptDefMutable(cdi.get());
8625 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8627 (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8628 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
8629 ) && !cd->isHidden() && cd->isLinkableInProject()
8632 msg("Generating docs for concept %s...\n",qPrint(cd->name()));
8633 cd->writeDocumentation(*g_outputList);
8638 //----------------------------------------------------------------------------
8640 static void inheritDocumentation()
8642 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8644 for (const auto &imd : *mn)
8646 MemberDefMutable *md = toMemberDefMutable(imd.get());
8647 //static int count=0;
8648 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
8649 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
8650 { // no documentation yet
8651 const MemberDef *bmd = md->reimplements();
8652 while (bmd && bmd->documentation().isEmpty() &&
8653 bmd->briefDescription().isEmpty()
8655 { // search up the inheritance tree for a documentation member
8656 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
8657 bmd = bmd->reimplements();
8659 if (bmd) // copy the documentation from the reimplemented member
8661 md->setInheritsDocsFrom(bmd);
8662 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
8663 md->setDocsForDefinition(bmd->isDocsForDefinition());
8664 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
8665 md->copyArgumentNames(bmd);
8666 md->setInbodyDocumentation(bmd->inbodyDocumentation(),bmd->inbodyFile(),bmd->inbodyLine());
8673 //----------------------------------------------------------------------------
8675 static void combineUsingRelations()
8678 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8680 for (const auto &fd : *fn)
8682 fd->combineUsingRelations();
8686 // for each namespace
8687 NamespaceDefSet visitedNamespaces;
8688 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8690 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8693 ndm->combineUsingRelations(visitedNamespaces);
8698 //----------------------------------------------------------------------------
8700 static void addMembersToMemberGroup()
8703 for (const auto &cd : *Doxygen::classLinkedMap)
8705 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8708 cdm->addMembersToMemberGroup();
8712 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8714 for (const auto &fd : *fn)
8716 fd->addMembersToMemberGroup();
8719 // for each namespace
8720 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8722 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8725 ndm->addMembersToMemberGroup();
8729 for (const auto &gd : *Doxygen::groupLinkedMap)
8731 gd->addMembersToMemberGroup();
8733 ModuleManager::instance().addMembersToMemberGroup();
8736 //----------------------------------------------------------------------------
8738 static void distributeMemberGroupDocumentation()
8741 for (const auto &cd : *Doxygen::classLinkedMap)
8743 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8746 cdm->distributeMemberGroupDocumentation();
8750 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8752 for (const auto &fd : *fn)
8754 fd->distributeMemberGroupDocumentation();
8757 // for each namespace
8758 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8760 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8763 ndm->distributeMemberGroupDocumentation();
8767 for (const auto &gd : *Doxygen::groupLinkedMap)
8769 gd->distributeMemberGroupDocumentation();
8771 ModuleManager::instance().distributeMemberGroupDocumentation();
8774 //----------------------------------------------------------------------------
8776 static void findSectionsInDocumentation()
8779 for (const auto &cd : *Doxygen::classLinkedMap)
8781 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8784 cdm->findSectionsInDocumentation();
8788 for (const auto &cd : *Doxygen::conceptLinkedMap)
8790 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
8793 cdm->findSectionsInDocumentation();
8797 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8799 for (const auto &fd : *fn)
8801 fd->findSectionsInDocumentation();
8804 // for each namespace
8805 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8807 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8810 ndm->findSectionsInDocumentation();
8814 for (const auto &gd : *Doxygen::groupLinkedMap)
8816 gd->findSectionsInDocumentation();
8819 for (const auto &pd : *Doxygen::pageLinkedMap)
8821 pd->findSectionsInDocumentation();
8823 ModuleManager::instance().findSectionsInDocumentation();
8824 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
8827 //----------------------------------------------------------------------
8830 static void flushCachedTemplateRelations()
8832 // remove all references to classes from the cache
8833 // as there can be new template instances in the inheritance path
8834 // to this class. Optimization: only remove those classes that
8835 // have inheritance instances as direct or indirect sub classes.
8836 StringVector elementsToRemove;
8837 for (const auto &ci : *Doxygen::typeLookupCache)
8839 const LookupInfo &li = ci.second;
8842 elementsToRemove.push_back(ci.first);
8845 for (const auto &k : elementsToRemove)
8847 Doxygen::typeLookupCache->remove(k);
8850 // remove all cached typedef resolutions whose target is a
8851 // template class as this may now be a template instance
8852 // for each global function name
8853 for (const auto &fn : *Doxygen::functionNameLinkedMap)
8855 // for each function with that name
8856 for (const auto &ifmd : *fn)
8858 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8859 if (fmd && fmd->isTypedefValCached())
8861 const ClassDef *cd = fmd->getCachedTypedefVal();
8862 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
8866 // for each class method name
8867 for (const auto &nm : *Doxygen::memberNameLinkedMap)
8869 // for each function with that name
8870 for (const auto &imd : *nm)
8872 MemberDefMutable *md = toMemberDefMutable(imd.get());
8873 if (md && md->isTypedefValCached())
8875 const ClassDef *cd = md->getCachedTypedefVal();
8876 if (cd->isTemplate()) md->invalidateTypedefValCache();
8882 //----------------------------------------------------------------------------
8884 static void flushUnresolvedRelations()
8886 // Remove all unresolved references to classes from the cache.
8887 // This is needed before resolving the inheritance relations, since
8888 // it would otherwise not find the inheritance relation
8889 // for C in the example below, as B::I was already found to be unresolvable
8890 // (which is correct if you ignore the inheritance relation between A and B).
8892 // class A { class I {} };
8893 // class B : public A {};
8894 // class C : public B::I {};
8896 StringVector elementsToRemove;
8897 for (const auto &ci : *Doxygen::typeLookupCache)
8899 const LookupInfo &li = ci.second;
8900 if (li.definition==0 && li.typeDef==0)
8902 elementsToRemove.push_back(ci.first);
8905 for (const auto &k : elementsToRemove)
8907 Doxygen::typeLookupCache->remove(k);
8910 // for each global function name
8911 for (const auto &fn : *Doxygen::functionNameLinkedMap)
8913 // for each function with that name
8914 for (const auto &ifmd : *fn)
8916 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8919 fmd->invalidateCachedArgumentTypes();
8923 // for each class method name
8924 for (const auto &nm : *Doxygen::memberNameLinkedMap)
8926 // for each function with that name
8927 for (const auto &imd : *nm)
8929 MemberDefMutable *md = toMemberDefMutable(imd.get());
8932 md->invalidateCachedArgumentTypes();
8939 //----------------------------------------------------------------------------
8940 // Returns TRUE if the entry and member definition have equal file names,
8943 static bool haveEqualFileNames(const Entry *root,const MemberDef *md)
8945 const FileDef *fd = md->getFileDef();
8951 return fd->absFilePath() == root->fileName;
8954 //----------------------------------------------------------------------------
8956 static void addDefineDoc(const Entry *root, MemberDefMutable *md)
8958 md->setDocumentation(root->doc,root->docFile,root->docLine);
8959 md->setDocsForDefinition(!root->proto);
8960 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8961 if (md->inbodyDocumentation().isEmpty())
8963 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
8965 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
8967 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
8968 md->setBodyDef(root->fileDef());
8970 md->addSectionsToDefinition(root->anchors);
8971 md->setMaxInitLines(root->initLines);
8972 md->setRefItems(root->sli);
8973 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
8974 addMemberToGroups(root,md);
8975 ModuleManager::instance().addMemberToModule(root,md);
8978 //----------------------------------------------------------------------------
8980 static void findDefineDocumentation(Entry *root)
8982 if ((root->section==Entry::DEFINEDOC_SEC ||
8983 root->section==Entry::DEFINE_SEC) && !root->name.isEmpty()
8986 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
8987 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
8989 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
8991 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
8992 "#define",root->name,root->args,QCString(),
8993 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType_Define,
8994 ArgumentList(),ArgumentList(),"");
8995 auto mmd = toMemberDefMutable(md.get());
8996 mmd->setTagInfo(root->tagInfo());
8997 mmd->setLanguage(root->lang);
8998 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
8999 mmd->setFileDef(root->parent()->fileDef());
9000 //printf("Adding member=%s\n",qPrint(md->name()));
9001 MemberName *mn = Doxygen::functionNameLinkedMap->add(root->name);
9002 mn->push_back(std::move(md));
9004 MemberName *mn=Doxygen::functionNameLinkedMap->find(root->name);
9008 for (const auto &md : *mn)
9010 if (md->memberType()==MemberType_Define) count++;
9014 for (const auto &imd : *mn)
9016 MemberDefMutable *md = toMemberDefMutable(imd.get());
9017 if (md && md->memberType()==MemberType_Define)
9019 addDefineDoc(root,md);
9024 (!root->doc.isEmpty() ||
9025 !root->brief.isEmpty() ||
9029 // multiple defines don't know where to add docs
9030 // but maybe they are in different files together with their documentation
9032 for (const auto &imd : *mn)
9034 MemberDefMutable *md = toMemberDefMutable(imd.get());
9035 if (md && md->memberType()==MemberType_Define)
9037 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9038 // doc and define in the same file or group assume they belong together.
9040 addDefineDoc(root,md);
9044 //warn("define %s found in the following files:\n",qPrint(root->name));
9045 //warn("Cannot determine where to add the documentation found "
9046 // "at line %d of file %s. \n",
9047 // root->startLine,qPrint(root->fileName));
9050 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9052 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9055 warn(root->fileName,root->startLine,
9056 "documentation for unknown define %s found.",
9062 warn(root->fileName,root->startLine,
9063 "found documented #define %s but ignoring it because "
9064 "ENABLE_PREPROCESSING is NO.",
9070 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9073 //----------------------------------------------------------------------------
9075 static void findDirDocumentation(const Entry *root)
9077 if (root->section == Entry::DIRDOC_SEC)
9079 QCString normalizedName = root->name;
9080 normalizedName = substitute(normalizedName,"\\","/");
9081 //printf("root->docFile=%s normalizedName=%s\n",
9082 // qPrint(root->docFile),qPrint(normalizedName));
9083 if (root->docFile==normalizedName) // current dir?
9085 int lastSlashPos=normalizedName.findRev('/');
9086 if (lastSlashPos!=-1) // strip file name
9088 normalizedName=normalizedName.left(lastSlashPos);
9091 if (normalizedName.at(normalizedName.length()-1)!='/')
9093 normalizedName+='/';
9095 DirDef *matchingDir=0;
9096 for (const auto &dir : *Doxygen::dirLinkedMap)
9098 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9099 if (dir->name().right(normalizedName.length())==normalizedName)
9103 warn(root->fileName,root->startLine,
9104 "\\dir command matches multiple directories.\n"
9105 " Applying the command for directory %s\n"
9106 " Ignoring the command for directory %s",
9107 qPrint(matchingDir->name()),qPrint(dir->name())
9112 matchingDir=dir.get();
9118 //printf("Match for with dir %s\n",qPrint(matchingDir->name()));
9119 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9120 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9121 matchingDir->setRefItems(root->sli);
9122 matchingDir->enableDirectoryGraph(root->directoryGraph);
9123 addDirToGroups(root,matchingDir);
9127 warn(root->fileName,root->startLine,"No matching "
9128 "directory found for command \\dir %s",qPrint(normalizedName));
9131 for (const auto &e : root->children()) findDirDocumentation(e.get());
9135 //----------------------------------------------------------------------------
9136 // create a (sorted) list of separate documentation pages
9138 static void buildPageList(Entry *root)
9140 if (root->section == Entry::PAGEDOC_SEC)
9142 if (!root->name.isEmpty())
9144 addRelatedPage(root);
9147 else if (root->section == Entry::MAINPAGEDOC_SEC)
9149 QCString title=root->args.stripWhiteSpace();
9150 if (title.isEmpty()) title=theTranslator->trMainPage();
9151 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9152 QCString name = "index";
9153 addRefItem(root->sli,
9161 for (const auto &e : root->children()) buildPageList(e.get());
9164 // search for the main page defined in this project
9165 static void findMainPage(Entry *root)
9167 if (root->section == Entry::MAINPAGEDOC_SEC)
9169 if (Doxygen::mainPage==0 && root->tagInfo()==0)
9171 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9172 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9173 QCString title=root->args.stripWhiteSpace();
9174 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9175 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9176 QCString indexName="index";
9177 Doxygen::mainPage = createPageDef(root->docFile,root->docLine,
9178 indexName, root->brief+root->doc+root->inbodyDocs,title);
9179 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9180 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9181 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9182 Doxygen::mainPage->setFileName(indexName);
9183 Doxygen::mainPage->setLocalToc(root->localToc);
9184 addPageToContext(Doxygen::mainPage.get(),root);
9186 const SectionInfo *si = SectionManager::instance().find(Doxygen::mainPage->name());
9189 if (!si->ref().isEmpty()) // we are from a tag file
9191 // a page name is a label as well! but should no be double either
9192 SectionManager::instance().replace(
9193 Doxygen::mainPage->name(),
9196 Doxygen::mainPage->title(),
9200 else if (si->lineNr() != -1)
9202 warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s, line %d)",
9203 qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()),si->lineNr());
9207 warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s)",
9208 qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()));
9213 // a page name is a label as well! but should no be double either
9214 SectionManager::instance().add(
9215 Doxygen::mainPage->name(),
9218 Doxygen::mainPage->title(),
9222 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9224 else if (root->tagInfo()==0)
9226 warn(root->fileName,root->startLine,
9227 "found more than one \\mainpage comment block! (first occurrence: %s, line %d), Skipping current block!",
9228 qPrint(Doxygen::mainPage->docFile()),Doxygen::mainPage->getStartBodyLine());
9231 for (const auto &e : root->children()) findMainPage(e.get());
9234 // search for the main page imported via tag files and add only the section labels
9235 static void findMainPageTagFiles(Entry *root)
9237 if (root->section == Entry::MAINPAGEDOC_SEC)
9239 if (Doxygen::mainPage && root->tagInfo())
9241 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9244 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9247 static void computePageRelations(Entry *root)
9249 if ((root->section==Entry::PAGEDOC_SEC ||
9250 root->section==Entry::MAINPAGEDOC_SEC
9252 && !root->name.isEmpty()
9255 PageDef *pd = root->section==Entry::PAGEDOC_SEC ?
9256 Doxygen::pageLinkedMap->find(root->name) :
9257 Doxygen::mainPage.get();
9260 for (const BaseInfo &bi : root->extends)
9262 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9265 term("page defined %s with label %s is a direct "
9266 "subpage of itself! Please remove this cyclic dependency.\n",
9267 qPrint(warn_line(pd->docFile(),pd->docLine())),qPrint(pd->name()));
9271 pd->addInnerCompound(subPd);
9272 //printf("*** Added subpage relation: %s->%s\n",
9273 // qPrint(pd->name()),qPrint(subPd->name()));
9278 for (const auto &e : root->children()) computePageRelations(e.get());
9281 static void checkPageRelations()
9283 for (const auto &pd : *Doxygen::pageLinkedMap)
9285 Definition *ppd = pd->getOuterScope();
9290 term("page defined %s with label %s is a subpage "
9291 "of itself! Please remove this cyclic dependency.\n",
9292 qPrint(warn_line(pd->docFile(),pd->docLine())),qPrint(pd->name()));
9294 ppd=ppd->getOuterScope();
9299 //----------------------------------------------------------------------------
9301 static void resolveUserReferences()
9303 for (const auto &si : SectionManager::instance())
9305 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9306 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9307 // qPrint(si->fileName));
9310 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9311 // different files, so the resulting section's all have the wrong file
9312 // name (not from the todo/test/bug/deprecated list, but from the file in
9313 // which they are defined). We correct this here by looking at the
9314 // generated section labels!
9315 for (const RefListManager::Ptr &rl : RefListManager::instance())
9317 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9318 if (si->label().left(label.length())==label)
9320 si->setFileName(rl->listName());
9321 si->setGenerated(TRUE);
9326 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9327 if (!si->generated())
9329 // if this section is in a page and the page is in a group, then we
9330 // have to adjust the link file name to point to the group.
9331 if (!si->fileName().isEmpty() &&
9332 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9335 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9338 if (si->definition())
9340 // TODO: there should be one function in Definition that returns
9341 // the file to link to, so we can avoid the following tests.
9342 const GroupDef *gd=0;
9343 if (si->definition()->definitionType()==Definition::TypeMember)
9345 gd = (toMemberDef(si->definition()))->getGroupDef();
9350 si->setFileName(gd->getOutputFileBase());
9354 //si->fileName=si->definition->getOutputFileBase();
9355 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9359 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9365 //----------------------------------------------------------------------------
9366 // generate all separate documentation pages
9369 static void generatePageDocs()
9371 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9372 if (Index::instance().numDocumentedPages()==0) return;
9373 for (const auto &pd : *Doxygen::pageLinkedMap)
9375 if (!pd->getGroupDef() && !pd->isReference())
9377 msg("Generating docs for page %s...\n",qPrint(pd->name()));
9378 pd->writeDocumentation(*g_outputList);
9383 //----------------------------------------------------------------------------
9384 // create a (sorted) list & dictionary of example pages
9386 static void buildExampleList(Entry *root)
9388 if ((root->section==Entry::EXAMPLE_SEC || root->section==Entry::EXAMPLE_LINENO_SEC) && !root->name.isEmpty())
9390 if (Doxygen::exampleLinkedMap->find(root->name))
9392 warn(root->fileName,root->startLine,
9393 "Example %s was already documented. Ignoring "
9394 "documentation found here.",
9400 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9401 createPageDef(root->fileName,root->startLine,
9402 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9403 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9404 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9405 pd->addSectionsToDefinition(root->anchors);
9406 pd->setLanguage(root->lang);
9407 pd->setShowLineNo(root->section==Entry::EXAMPLE_LINENO_SEC);
9409 //we don't add example to groups
9410 //addExampleToGroups(root,pd);
9413 for (const auto &e : root->children()) buildExampleList(e.get());
9416 //----------------------------------------------------------------------------
9417 // prints the Entry tree (for debugging)
9419 void printNavTree(Entry *root,int indent)
9421 if (Debug::isFlagSet(Debug::Entries))
9424 indentStr.fill(' ',indent);
9425 Debug::print(Debug::Entries,0,"%s%s at %s:%d (sec=0x%x, spec=%" PRIx64 ")\n",
9426 indentStr.isEmpty()?"":qPrint(indentStr),
9427 root->name.isEmpty()?"<empty>":qPrint(root->name),
9428 qPrint(root->fileName),root->startLine,
9431 for (const auto &e : root->children())
9433 printNavTree(e.get(),indent+2);
9439 //----------------------------------------------------------------------------
9440 // generate the example documentation
9442 static void generateExampleDocs()
9444 g_outputList->disable(OutputType::Man);
9445 for (const auto &pd : *Doxygen::exampleLinkedMap)
9447 msg("Generating docs for example %s...\n",qPrint(pd->name()));
9448 auto intf = Doxygen::parserManager->getCodeParser(".c"); // TODO: do this on code type
9449 intf->resetCodeParserState();
9450 QCString n=pd->getOutputFileBase();
9451 startFile(*g_outputList,n,n,pd->name());
9452 startTitle(*g_outputList,n);
9453 g_outputList->docify(pd->name());
9454 endTitle(*g_outputList,n,QCString());
9455 g_outputList->startContents();
9456 QCString lineNoOptStr;
9457 if (pd->showLineNo())
9459 lineNoOptStr="{lineno}";
9461 g_outputList->generateDoc(pd->docFile(), // file
9462 pd->docLine(), // startLine
9463 pd.get(), // context
9465 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9466 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9467 TRUE, // index words
9472 Config_getBool(MARKDOWN_SUPPORT)
9474 endFile(*g_outputList); // contains g_outputList->endContents()
9476 g_outputList->enable(OutputType::Man);
9479 //----------------------------------------------------------------------------
9480 // generate module pages
9482 static void generateGroupDocs()
9484 for (const auto &gd : *Doxygen::groupLinkedMap)
9486 if (!gd->isReference())
9488 gd->writeDocumentation(*g_outputList);
9493 //----------------------------------------------------------------------------
9494 // generate module pages
9496 static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
9498 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9499 if (numThreads>1) // multi threaded processing
9503 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
9504 : cdm(cdm_), ol(ol_) {}
9505 ClassDefMutable *cdm;
9508 ThreadPool threadPool(numThreads);
9509 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9510 // for each class in the namespace...
9511 for (const auto &cd : classList)
9513 ClassDefMutable *cdm = toClassDefMutable(cd);
9516 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
9517 auto processFile = [ctx]()
9519 if ( ( ctx->cdm->isLinkableInProject() &&
9520 ctx->cdm->templateMaster()==0
9521 ) // skip external references, anonymous compounds and
9522 // template instances and nested classes
9523 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
9526 msg("Generating docs for compound %s...\n",qPrint(ctx->cdm->name()));
9527 ctx->cdm->writeDocumentation(ctx->ol);
9528 ctx->cdm->writeMemberList(ctx->ol);
9530 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
9533 results.emplace_back(threadPool.queue(processFile));
9536 // wait for the results
9537 for (auto &f : results)
9542 else // single threaded processing
9544 // for each class in the namespace...
9545 for (const auto &cd : classList)
9547 ClassDefMutable *cdm = toClassDefMutable(cd);
9550 if ( ( cd->isLinkableInProject() &&
9551 cd->templateMaster()==0
9552 ) // skip external references, anonymous compounds and
9553 // template instances and nested classes
9554 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
9557 msg("Generating docs for compound %s...\n",qPrint(cd->name()));
9559 cdm->writeDocumentation(*g_outputList);
9560 cdm->writeMemberList(*g_outputList);
9562 cdm->writeDocumentationForInnerClasses(*g_outputList);
9568 static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
9570 // for each concept in the namespace...
9571 for (const auto &cd : conceptList)
9573 ConceptDefMutable *cdm = toConceptDefMutable(cd);
9574 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
9576 msg("Generating docs for concept %s...\n",qPrint(cd->name()));
9577 cdm->writeDocumentation(*g_outputList);
9582 static void generateNamespaceDocs()
9584 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
9586 //writeNamespaceIndex(*g_outputList);
9588 // for each namespace...
9589 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9591 if (nd->isLinkableInProject())
9593 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
9596 msg("Generating docs for namespace %s\n",qPrint(nd->name()));
9597 ndm->writeDocumentation(*g_outputList);
9601 generateNamespaceClassDocs(nd->getClasses());
9604 generateNamespaceClassDocs(nd->getInterfaces());
9605 generateNamespaceClassDocs(nd->getStructs());
9606 generateNamespaceClassDocs(nd->getExceptions());
9608 generateNamespaceConceptDocs(nd->getConcepts());
9612 static void runHtmlHelpCompiler()
9614 std::string oldDir = Dir::currentDirPath();
9615 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
9616 Portable::setShortDir();
9617 if (Portable::system(Config_getString(HHC_LOCATION).data(), qPrint(HtmlHelp::hhpFileName), Debug::isFlagSet(Debug::ExtCmd))!=1)
9619 err("failed to run html help compiler on %s\n", qPrint(HtmlHelp::hhpFileName));
9621 Dir::setCurrent(oldDir);
9624 static void runQHelpGenerator()
9626 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
9627 std::string oldDir = Dir::currentDirPath();
9628 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
9630 QCString qhgLocation=Config_getString(QHG_LOCATION);
9631 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
9633 // run qhelpgenerator -v and extract the Qt version used
9634 QCString cmd=qhgLocation+ " -v 2>&1";
9635 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",qPrint(cmd));
9636 FILE *f=Portable::popen(cmd,"r");
9639 err("could not execute %s\n",qPrint(qhgLocation));
9643 const size_t bufSize = 1024;
9644 char inBuf[bufSize+1];
9645 size_t numRead=fread(inBuf,1,bufSize,f);
9646 inBuf[numRead] = '\0';
9647 Debug::print(Debug::Qhp,0,inBuf);
9648 Portable::pclose(f);
9651 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
9653 std::string s = inBuf;
9654 if (reg::search(inBuf,match,versionReg))
9656 qtVersion = 10000*QCString(match[1].str()).toInt() +
9657 100*QCString(match[2].str()).toInt() +
9658 QCString(match[3].str()).toInt();
9660 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
9662 // dump the output of qhelpgenerator -c file.qhp
9663 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
9664 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
9665 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",qPrint(cmd));
9666 f=Portable::popen(cmd,"r");
9669 err("could not execute %s\n",qPrint(qhgLocation));
9674 while ((numRead=fread(inBuf,1,bufSize,f))>0)
9676 inBuf[numRead] = '\0';
9679 Portable::pclose(f);
9680 Debug::print(Debug::Qhp,0,output.c_str());
9686 if (Portable::system(qhgLocation, args, FALSE))
9688 err("failed to run qhelpgenerator on %s\n",qPrint(Qhp::qhpFileName));
9690 Dir::setCurrent(oldDir);
9693 //----------------------------------------------------------------------------
9695 static void computeVerifiedDotPath()
9698 QCString dotPath = Config_getString(DOT_PATH);
9699 if (!dotPath.isEmpty())
9701 FileInfo fi(dotPath.str());
9702 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
9704 dotPath = dotPath+"/dot"+Portable::commandExtension();
9705 FileInfo dp(dotPath.str());
9706 if (!dp.exists() || !dp.isFile())
9708 warn_uncond("the dot tool could not be found as '%s'\n",qPrint(dotPath));
9710 dotPath += Portable::commandExtension();
9713 #if defined(_WIN32) // convert slashes
9714 uint32_t i=0,l=dotPath.length();
9715 for (i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
9721 dotPath += Portable::commandExtension();
9723 Doxygen::verifiedDotPath = dotPath;
9724 TRACE("{}",Doxygen::verifiedDotPath);
9727 //----------------------------------------------------------------------------
9729 /*! Generate a template version of the configuration file.
9730 * If the \a shortList parameter is TRUE a configuration file without
9731 * comments will be generated.
9733 static void generateConfigFile(const QCString &configFile,bool shortList,
9734 bool updateOnly=FALSE)
9737 bool fileOpened=openOutputFile(configFile,f);
9738 bool writeToStdout=configFile=="-";
9742 Config::writeTemplate(t,shortList,updateOnly);
9747 msg("\n\nConfiguration file '%s' created.\n\n",qPrint(configFile));
9748 msg("Now edit the configuration file and enter\n\n");
9749 if (configFile!="Doxyfile" && configFile!="doxyfile")
9750 msg(" doxygen %s\n\n",qPrint(configFile));
9752 msg(" doxygen\n\n");
9753 msg("to generate the documentation for your project\n\n");
9757 msg("\n\nConfiguration file '%s' updated.\n\n",qPrint(configFile));
9763 term("Cannot open file %s for writing\n",qPrint(configFile));
9767 static void compareDoxyfile(Config::CompareMode diffList)
9770 bool fileOpened=openOutputFile("-",f);
9774 Config::compareDoxyfile(t,diffList);
9778 term("Cannot open stdout for writing\n");
9782 //----------------------------------------------------------------------------
9783 // read and parse a tag file
9785 static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
9789 int eqPos = tagLine.find('=');
9790 if (eqPos!=-1) // tag command contains a destination
9792 fileName = tagLine.left(eqPos).stripWhiteSpace();
9793 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
9794 if (fileName.isEmpty() || destName.isEmpty()) return;
9795 FileInfo fi(fileName.str());
9796 Doxygen::tagDestinationMap.insert(
9797 std::make_pair(fi.absFilePath(), destName.str()));
9798 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
9805 FileInfo fi(fileName.str());
9806 if (!fi.exists() || !fi.isFile())
9808 err("Tag file '%s' does not exist or is not a file. Skipping it...\n",
9813 if (!destName.isEmpty())
9814 msg("Reading tag file '%s', location '%s'...\n",qPrint(fileName),qPrint(destName));
9816 msg("Reading tag file '%s'...\n",qPrint(fileName));
9818 parseTagFile(root,fi.absFilePath().c_str());
9821 //----------------------------------------------------------------------------
9822 static void copyLatexStyleSheet()
9824 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
9825 for (const auto &sheet : latexExtraStyleSheet)
9827 std::string fileName = sheet;
9828 if (!fileName.empty())
9830 FileInfo fi(fileName);
9833 err("Style sheet '%s' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName));
9837 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
9838 if (!checkExtension(fi.fileName().c_str(), LATEX_STYLE_EXTENSION))
9840 destFileName += LATEX_STYLE_EXTENSION;
9842 copyFile(QCString(fileName), destFileName);
9848 //----------------------------------------------------------------------------
9849 static void copyStyleSheet()
9851 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
9852 if (!htmlStyleSheet.isEmpty())
9854 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
9856 FileInfo fi(htmlStyleSheet.str());
9859 err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",qPrint(htmlStyleSheet));
9860 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
9864 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9865 copyFile(htmlStyleSheet,destFileName);
9869 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
9870 for (const auto &sheet : htmlExtraStyleSheet)
9872 QCString fileName(sheet);
9873 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
9875 FileInfo fi(fileName.str());
9878 err("Style sheet '%s' specified by HTML_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName));
9880 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
9882 err("Style sheet %s specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",qPrint(fi.fileName()));
9886 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9887 copyFile(QCString(fileName), destFileName);
9893 static void copyLogo(const QCString &outputOption)
9895 QCString projectLogo = Config_getString(PROJECT_LOGO);
9896 if (!projectLogo.isEmpty())
9898 FileInfo fi(projectLogo.str());
9901 err("Project logo '%s' specified by PROJECT_LOGO does not exist!\n",qPrint(projectLogo));
9902 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
9906 QCString destFileName = outputOption+"/"+fi.fileName();
9907 copyFile(projectLogo,destFileName);
9908 Doxygen::indexList->addImageFile(fi.fileName().c_str());
9913 static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
9915 for (const auto &fileName : files)
9917 if (!fileName.empty())
9919 FileInfo fi(fileName);
9922 err("Extra file '%s' specified in %s does not exist!\n", fileName.c_str(),qPrint(filesOption));
9926 QCString destFileName = outputOption+"/"+fi.fileName();
9927 Doxygen::indexList->addImageFile(fi.fileName().c_str());
9928 copyFile(QCString(fileName), destFileName);
9934 //----------------------------------------------------------------------------
9936 static void generateDiskNames()
9938 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9942 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
9947 // collect the entry for which to compute the longest common prefix (LCP) of the path
9948 std::vector<FileEntry> fileEntries;
9949 for (const auto &fd : *fn)
9951 if (!fd->isReference()) // skip external references
9953 fileEntries.emplace_back(fd->getPath(),fd.get());
9957 size_t size = fileEntries.size();
9959 if (size==1) // name if unique, so diskname is simply the name
9961 FileDef *fd = fileEntries[0].fileDef;
9962 fd->setDiskName(fn->fileName());
9964 else if (size>1) // multiple occurrences of the same file name
9967 std::sort(fileEntries.begin(),
9969 [](const FileEntry &fe1,const FileEntry &fe2)
9970 { return fe1.path < fe2.path; }
9973 // since the entries are sorted, the common prefix of the whole array is same
9974 // as the common prefix between the first and last entry
9975 const FileEntry &first = fileEntries[0];
9976 const FileEntry &last = fileEntries[size-1];
9977 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
9978 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
9981 for (i=0;i<first_path_size && i<last_path_size;i++)
9983 if (first.path[i]=='/') j=i;
9984 if (first.path[i]!=last.path[i]) break;
9986 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
9988 // case first='some/path' and last='some/path/more' => match is 'some/path'
9991 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
9993 // case first='some/path/more' and last='some/path' => match is 'some/path'
9997 // add non-common part of the path to the name
9998 for (auto &fileEntry : fileEntries)
10000 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10001 fileEntry.fileDef->setName(prefix+fn->fileName());
10002 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10003 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10011 //----------------------------------------------------------------------------
10013 static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10015 QCString fileName=fn;
10016 QCString extension;
10017 int sep = fileName.findRev('/');
10018 int ei = fileName.findRev('.');
10019 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10021 extension=fileName.right(fileName.length()-ei);
10025 extension = ".no_extension";
10028 return Doxygen::parserManager->getOutlineParser(extension);
10031 static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10032 FileDef *fd,const QCString &fn,
10033 ClangTUParser *clangParser,bool newTU)
10035 QCString fileName=fn;
10036 AUTO_TRACE("fileName={}",fileName);
10037 QCString extension;
10038 int ei = fileName.findRev('.');
10041 extension=fileName.right(fileName.length()-ei);
10045 extension = ".no_extension";
10048 FileInfo fi(fileName.str());
10049 BufStr preBuf(fi.size()+4096);
10051 if (Config_getBool(ENABLE_PREPROCESSING) &&
10052 parser.needsPreprocessing(extension))
10054 Preprocessor preprocessor;
10055 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10056 for (const auto &s : includePath)
10058 std::string absPath = FileInfo(s).absFilePath();
10059 preprocessor.addSearchDir(absPath.c_str());
10061 BufStr inBuf(fi.size()+4096);
10062 msg("Preprocessing %s...\n",qPrint(fn));
10063 readInputFile(fileName,inBuf);
10064 inBuf.addTerminalCharIfMissing('\n');
10065 preprocessor.processFile(fileName,inBuf,preBuf);
10067 else // no preprocessing
10069 msg("Reading %s...\n",qPrint(fn));
10070 readInputFile(fileName,preBuf);
10071 preBuf.addTerminalCharIfMissing('\n');
10074 BufStr convBuf(preBuf.curPos()+1024);
10076 // convert multi-line C++ comments to C style comments
10077 convertCppComments(preBuf,convBuf,fileName);
10079 convBuf.addChar('\0');
10081 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10082 // use language parse to parse the file
10085 if (newTU) clangParser->parse();
10086 clangParser->switchToFile(fd);
10088 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10089 fileRoot->setFileDef(fd);
10093 //! parse the list of input files
10094 static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10098 if (Doxygen::clangAssistedParsing)
10100 StringUnorderedSet processedFiles;
10102 // create a dictionary with files to process
10103 StringUnorderedSet filesToProcess;
10104 for (const auto &s : g_inputFiles)
10106 filesToProcess.insert(s);
10109 std::mutex processedFilesLock;
10110 // process source files (and their include dependencies)
10111 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10112 msg("Processing input using %zu threads.\n",numThreads);
10113 ThreadPool threadPool(numThreads);
10114 using FutureType = std::vector< std::shared_ptr<Entry> >;
10115 std::vector< std::future< FutureType > > results;
10116 for (const auto &s : g_inputFiles)
10119 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10121 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt_Cpp) // this is a source file
10123 // lambda representing the work to executed by a thread
10124 auto processFile = [s,&filesToProcess,&processedFilesLock,&processedFiles]() {
10126 std::vector< std::shared_ptr<Entry> > roots;
10127 FileDef *fd_l = findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig_l);
10128 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10129 auto parser = getParserForFile(s.c_str());
10130 auto fileRoot { parseFile(*parser.get(),fd_l,s.c_str(),clangParser.get(),true) };
10131 roots.push_back(fileRoot);
10133 // Now process any include files in the same translation unit
10134 // first. When libclang is used this is much more efficient.
10135 for (auto incFile : clangParser->filesInSameTU())
10137 if (filesToProcess.find(incFile)!=filesToProcess.end())
10139 bool needsToBeProcessed;
10141 std::lock_guard<std::mutex> lock(processedFilesLock);
10142 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10143 if (needsToBeProcessed) processedFiles.insert(incFile);
10145 if (incFile!=s && needsToBeProcessed)
10147 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig_l);
10148 if (ifd && !ifd->isReference())
10150 //printf(" Processing %s in same translation unit as %s\n",incFile,s->c_str());
10151 fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10152 roots.push_back(fileRoot);
10159 // dispatch the work and collect the future results
10160 results.emplace_back(threadPool.queue(processFile));
10163 // synchronise with the Entry result lists produced and add them to the root
10164 for (auto &f : results)
10169 root->moveToSubEntryAndKeep(e);
10172 // process remaining files
10174 for (const auto &s : g_inputFiles)
10176 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10178 // lambda representing the work to executed by a thread
10179 auto processFile = [s]() {
10181 std::vector< std::shared_ptr<Entry> > roots;
10182 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10183 auto parser { getParserForFile(s.c_str()) };
10184 bool useClang = getLanguageFromFileName(s.c_str())==SrcLangExt_Cpp;
10187 auto clangParser = ClangParser::instance()->createTUParser(fd);
10188 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10189 roots.push_back(fileRoot);
10193 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10194 roots.push_back(fileRoot);
10198 results.emplace_back(threadPool.queue(processFile));
10201 // synchronise with the Entry result lists produced and add them to the root
10202 for (auto &f : results)
10207 root->moveToSubEntryAndKeep(e);
10211 else // normal processing
10214 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10215 msg("Processing input using %zu threads.\n",numThreads);
10216 ThreadPool threadPool(numThreads);
10217 using FutureType = std::shared_ptr<Entry>;
10218 std::vector< std::future< FutureType > > results;
10219 for (const auto &s : g_inputFiles)
10221 // lambda representing the work to executed by a thread
10222 auto processFile = [s]() {
10224 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10225 auto parser = getParserForFile(s.c_str());
10226 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10229 // dispatch the work and collect the future results
10230 results.emplace_back(threadPool.queue(processFile));
10232 // synchronise with the Entry results produced and add them to the root
10233 for (auto &f : results)
10235 root->moveToSubEntryAndKeep(f.get());
10240 //! parse the list of input files
10241 static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10245 if (Doxygen::clangAssistedParsing)
10247 StringUnorderedSet processedFiles;
10249 // create a dictionary with files to process
10250 StringUnorderedSet filesToProcess;
10251 for (const auto &s : g_inputFiles)
10253 filesToProcess.insert(s);
10256 // process source files (and their include dependencies)
10257 for (const auto &s : g_inputFiles)
10260 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10262 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(s.c_str())==SrcLangExt_Cpp) // this is a source file
10264 auto clangParser = ClangParser::instance()->createTUParser(fd);
10265 auto parser { getParserForFile(s.c_str()) };
10266 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10267 root->moveToSubEntryAndKeep(fileRoot);
10268 processedFiles.insert(s);
10270 // Now process any include files in the same translation unit
10271 // first. When libclang is used this is much more efficient.
10272 for (auto incFile : clangParser->filesInSameTU())
10274 //printf(" file %s\n",incFile.c_str());
10275 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10276 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10278 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
10279 if (ifd && !ifd->isReference())
10281 //printf(" Processing %s in same translation unit as %s\n",incFile.c_str(),s.c_str());
10282 fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10283 root->moveToSubEntryAndKeep(fileRoot);
10284 processedFiles.insert(incFile);
10290 // process remaining files
10291 for (const auto &s : g_inputFiles)
10293 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10296 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10297 if (getLanguageFromFileName(s.c_str())==SrcLangExt_Cpp) // not yet processed
10299 auto clangParser = ClangParser::instance()->createTUParser(fd);
10300 auto parser { getParserForFile(s.c_str()) };
10301 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10302 root->moveToSubEntryAndKeep(fileRoot);
10306 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10307 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10308 root->moveToSubEntryAndKeep(fileRoot);
10310 processedFiles.insert(s);
10314 else // normal processing
10317 for (const auto &s : g_inputFiles)
10320 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10322 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10323 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10324 root->moveToSubEntryAndKeep(std::move(fileRoot));
10329 // resolves a path that may include symlinks, if a recursive symlink is
10330 // found an empty string is returned.
10331 static std::string resolveSymlink(const std::string &path)
10335 StringSet nonSymlinks;
10337 QCString result(path);
10338 QCString oldPrefix = "/";
10341 #if defined(_WIN32)
10342 // UNC path, skip server and share name
10343 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
10344 sepPos = result.find('/',2);
10346 sepPos = result.find('/',sepPos+1);
10348 sepPos = result.find('/',sepPos+1);
10350 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10351 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10353 FileInfo fi(prefix.str());
10354 if (fi.isSymLink())
10356 QCString target = fi.readLink();
10357 bool isRelative = FileInfo(target.str()).isRelative();
10360 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10364 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10368 target+=result.mid(sepPos);
10370 result = Dir::cleanDirPath(target.str());
10371 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10372 known.insert(result.str());
10377 else // link to absolute path
10385 nonSymlinks.insert(prefix.str());
10386 oldPrefix = prefix;
10391 while (sepPos!=-1);
10392 return Dir::cleanDirPath(result.str());
10395 static StringUnorderedSet g_pathsVisited(1009);
10397 //----------------------------------------------------------------------------
10398 // Read all files matching at least one pattern in 'patList' in the
10399 // directory represented by 'fi'.
10400 // The directory is read iff the recursiveFlag is set.
10401 // The contents of all files is append to the input string
10403 static void readDir(FileInfo *fi,
10404 FileNameLinkedMap *fnMap,
10405 StringUnorderedSet *exclSet,
10406 const StringVector *patList,
10407 const StringVector *exclPatList,
10408 StringVector *resultList,
10409 StringUnorderedSet *resultSet,
10410 bool errorIfNotExist,
10412 StringUnorderedSet *killSet,
10416 std::string dirName = fi->absFilePath();
10417 if (paths && !dirName.empty())
10419 paths->insert(dirName);
10421 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
10422 if (fi->isSymLink())
10424 dirName = resolveSymlink(dirName);
10425 if (dirName.empty())
10427 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
10428 return; // recursive symlink
10432 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
10434 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
10435 return; // already visited path
10437 g_pathsVisited.insert(dirName);
10440 msg("Searching for files in directory %s\n", qPrint(fi->absFilePath()));
10441 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
10443 StringVector dirResultList;
10445 for (const auto &dirEntry : dir.iterator())
10447 FileInfo cfi(dirEntry.path());
10448 if (exclSet==0 || exclSet->find(cfi.absFilePath())==exclSet->end())
10449 { // file should not be excluded
10450 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
10451 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
10454 else if (!cfi.exists() || !cfi.isReadable())
10456 if (errorIfNotExist)
10458 warn_uncond("source '%s' is not a readable file or directory... skipping.\n",cfi.absFilePath().c_str());
10461 else if (cfi.isFile() &&
10462 (patList==0 || patternMatch(cfi,*patList)) &&
10463 (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10464 (killSet==0 || killSet->find(cfi.absFilePath())==killSet->end())
10467 std::string name=cfi.fileName();
10468 std::string path=cfi.dirPath()+"/";
10469 std::string fullName=path+name;
10472 auto fd = createFileDef(QCString(path),QCString(name));
10476 fn = fnMap->add(QCString(name),QCString(fullName));
10477 fn->push_back(std::move(fd));
10480 dirResultList.push_back(fullName);
10481 if (resultSet) resultSet->insert(fullName);
10482 if (killSet) killSet->insert(fullName);
10484 else if (recursive &&
10486 (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10487 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
10489 FileInfo acfi(cfi.absFilePath());
10490 readDir(&acfi,fnMap,exclSet,
10491 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
10492 recursive,killSet,paths);
10496 if (resultList && !dirResultList.empty())
10498 // sort the resulting list to make the order platform independent.
10499 std::sort(dirResultList.begin(),
10500 dirResultList.end(),
10501 [](const auto &f1,const auto &f2) { return qstricmp(f1.c_str(),f2.c_str())<0; });
10503 // append the sorted results to resultList
10504 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
10509 //----------------------------------------------------------------------------
10510 // read a file or all files in a directory and append their contents to the
10511 // input string. The names of the files are appended to the 'fiList' list.
10513 void readFileOrDirectory(const QCString &s,
10514 FileNameLinkedMap *fnMap,
10515 StringUnorderedSet *exclSet,
10516 const StringVector *patList,
10517 const StringVector *exclPatList,
10518 StringVector *resultList,
10519 StringUnorderedSet *resultSet,
10521 bool errorIfNotExist,
10522 StringUnorderedSet *killSet,
10526 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
10527 // strip trailing slashes
10528 if (s.isEmpty()) return;
10530 g_pathsVisited.clear();
10532 FileInfo fi(s.str());
10533 //printf("readFileOrDirectory(%s)\n",s);
10535 if (exclSet==0 || exclSet->find(fi.absFilePath())==exclSet->end())
10537 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
10540 else if (!fi.exists() || !fi.isReadable())
10542 if (errorIfNotExist)
10544 warn_uncond("source '%s' is not a readable file or directory... skipping.\n",qPrint(s));
10547 else if (fi.isFile())
10549 std::string dirPath = fi.dirPath(true);
10550 std::string filePath = fi.absFilePath();
10551 if (paths && !dirPath.empty())
10553 paths->insert(dirPath);
10555 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
10556 if (killSet==0 || killSet->find(filePath)==killSet->end())
10558 std::string name=fi.fileName();
10561 auto fd = createFileDef(QCString(dirPath+"/"),QCString(name));
10564 FileName *fn = fnMap->add(QCString(name),QCString(filePath));
10565 fn->push_back(std::move(fd));
10568 if (resultList || resultSet)
10570 if (resultList) resultList->push_back(filePath);
10571 if (resultSet) resultSet->insert(filePath);
10574 if (killSet) killSet->insert(fi.absFilePath());
10577 else if (fi.isDir()) // readable dir
10579 readDir(&fi,fnMap,exclSet,patList,
10580 exclPatList,resultList,resultSet,errorIfNotExist,
10581 recursive,killSet,paths);
10587 //----------------------------------------------------------------------------
10589 static void dumpSymbol(TextStream &t,Definition *d)
10592 if (d->definitionType()==Definition::TypeMember)
10594 MemberDef *md = toMemberDef(d);
10595 anchor=":"+md->anchor();
10598 QCString fn = d->getOutputFileBase();
10599 addHtmlExtensionIfMissing(fn);
10600 if (d->getOuterScope() && d->getOuterScope()!=Doxygen::globalScope)
10604 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
10605 << fn+anchor << "','"
10607 << d->name() << "','"
10608 << d->getDefFileName() << "','"
10613 static void dumpSymbolMap()
10615 std::ofstream f = Portable::openOutputStream("symbols.sql");
10619 for (const auto &[name,symList] : *Doxygen::symbolMap)
10621 for (const auto &def : symList)
10629 // print developer options of doxygen
10630 static void devUsage()
10632 msg("Developer parameters:\n");
10633 msg(" -m dump symbol map\n");
10634 msg(" -b making messages output unbuffered\n");
10636 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
10638 msg(" -T activates output generation via Django like template\n");
10639 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
10640 Debug::printFlags();
10644 //----------------------------------------------------------------------------
10645 // print the version of doxygen
10647 static void version(const bool extended)
10649 QCString versionString = getFullVersion();
10650 msg("%s\n",qPrint(versionString));
10654 if (!extVers.isEmpty()) extVers+= ", ";
10655 extVers += "sqlite3 ";
10656 extVers += sqlite3_libversion();
10658 if (!extVers.isEmpty()) extVers+= ", ";
10659 extVers += "clang support ";
10660 extVers += CLANG_VERSION_STRING;
10662 if (!extVers.isEmpty())
10664 int lastComma = extVers.findRev(',');
10665 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
10666 msg(" with %s.\n",qPrint(extVers));
10671 //----------------------------------------------------------------------------
10672 // print the usage of doxygen
10674 static void usage(const QCString &name,const QCString &versionString)
10676 msg("Doxygen version %s\nCopyright Dimitri van Heesch 1997-2021\n\n",qPrint(versionString));
10677 msg("You can use doxygen in a number of ways:\n\n");
10678 msg("1) Use doxygen to generate a template configuration file*:\n");
10679 msg(" %s [-s] -g [configName]\n\n",qPrint(name));
10680 msg("2) Use doxygen to update an old configuration file*:\n");
10681 msg(" %s [-s] -u [configName]\n\n",qPrint(name));
10682 msg("3) Use doxygen to generate documentation using an existing ");
10683 msg("configuration file*:\n");
10684 msg(" %s [configName]\n\n",qPrint(name));
10685 msg("4) Use doxygen to generate a template file controlling the layout of the\n");
10686 msg(" generated documentation:\n");
10687 msg(" %s -l [layoutFileName]\n\n",qPrint(name));
10688 msg(" In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n");
10689 msg(" If - is used for layoutFileName doxygen will write to standard output.\n\n");
10690 msg("5) Use doxygen to generate a template style sheet file for RTF, HTML or Latex.\n");
10691 msg(" RTF: %s -w rtf styleSheetFile\n",qPrint(name));
10692 msg(" HTML: %s -w html headerFile footerFile styleSheetFile [configFile]\n",qPrint(name));
10693 msg(" LaTeX: %s -w latex headerFile footerFile styleSheetFile [configFile]\n\n",qPrint(name));
10694 msg("6) Use doxygen to generate a rtf extensions file\n");
10695 msg(" %s -e rtf extensionsFile\n\n",qPrint(name));
10696 msg(" If - is used for extensionsFile doxygen will write to standard output.\n\n");
10697 msg("7) Use doxygen to compare the used configuration file with the template configuration file\n");
10698 msg(" %s -x [configFile]\n\n",qPrint(name));
10699 msg(" Use doxygen to compare the used configuration file with the template configuration file\n");
10700 msg(" without replacing the environment variables or CMake type replacement variables\n");
10701 msg(" %s -x_noenv [configFile]\n\n",qPrint(name));
10702 msg("8) Use doxygen to show a list of built-in emojis.\n");
10703 msg(" %s -f emoji outputFileName\n\n",qPrint(name));
10704 msg(" If - is used for outputFileName doxygen will write to standard output.\n\n");
10705 msg("*) If -s is specified the comments of the configuration items in the config file will be omitted.\n");
10706 msg(" If configName is omitted 'Doxyfile' will be used as a default.\n");
10707 msg(" If - is used for configFile doxygen will write / read the configuration to /from standard output / input.\n\n");
10708 msg("If -q is used for a doxygen documentation run, doxygen will see this as if QUIET=YES has been set.\n\n");
10709 msg("-v print version string, -V print extended version information\n");
10710 msg("-h,-? prints usage help information\n");
10711 msg("%s -d prints additional usage flags for debugging purposes\n",qPrint(name));
10714 //----------------------------------------------------------------------------
10715 // read the argument of option 'c' from the comment argument list and
10716 // update the option index 'optInd'.
10718 static const char *getArg(int argc,char **argv,int &optInd)
10721 if (qstrlen(&argv[optInd][2])>0)
10722 s=&argv[optInd][2];
10723 else if (optInd+1<argc && argv[optInd+1][0]!='-')
10728 //----------------------------------------------------------------------------
10730 /** @brief /dev/null outline parser */
10731 class NullOutlineParser : public OutlineParserInterface
10734 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) {}
10735 bool needsPreprocessing(const QCString &) const { return FALSE; }
10736 void parsePrototype(const QCString &) {}
10740 template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
10742 return []() { return std::make_unique<T>(); };
10748 QCString lang = Portable::getenv("LC_ALL");
10749 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
10750 std::setlocale(LC_ALL,"");
10751 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
10752 std::setlocale(LC_NUMERIC,"C");
10754 Doxygen::symbolMap = new SymbolMap<Definition>;
10756 Portable::correct_path();
10758 Debug::startTimer();
10759 Doxygen::parserManager = new ParserManager( make_parser_factory<NullOutlineParser>(),
10760 make_parser_factory<FileCodeParser>());
10761 Doxygen::parserManager->registerParser("c", make_parser_factory<COutlineParser>(),
10762 make_parser_factory<CCodeParser>());
10763 Doxygen::parserManager->registerParser("python", make_parser_factory<PythonOutlineParser>(),
10764 make_parser_factory<PythonCodeParser>());
10765 Doxygen::parserManager->registerParser("fortran", make_parser_factory<FortranOutlineParser>(),
10766 make_parser_factory<FortranCodeParser>());
10767 Doxygen::parserManager->registerParser("fortranfree", make_parser_factory<FortranOutlineParserFree>(),
10768 make_parser_factory<FortranCodeParserFree>());
10769 Doxygen::parserManager->registerParser("fortranfixed", make_parser_factory<FortranOutlineParserFixed>(),
10770 make_parser_factory<FortranCodeParserFixed>());
10771 Doxygen::parserManager->registerParser("vhdl", make_parser_factory<VHDLOutlineParser>(),
10772 make_parser_factory<VHDLCodeParser>());
10773 Doxygen::parserManager->registerParser("xml", make_parser_factory<NullOutlineParser>(),
10774 make_parser_factory<XMLCodeParser>());
10775 Doxygen::parserManager->registerParser("sql", make_parser_factory<NullOutlineParser>(),
10776 make_parser_factory<SQLCodeParser>());
10777 Doxygen::parserManager->registerParser("md", make_parser_factory<MarkdownOutlineParser>(),
10778 make_parser_factory<FileCodeParser>());
10779 Doxygen::parserManager->registerParser("lex", make_parser_factory<LexOutlineParser>(),
10780 make_parser_factory<LexCodeParser>());
10782 // register any additional parsers here...
10784 initDefaultExtensionMapping();
10787 Doxygen::clangUsrMap = new ClangUsrMap;
10789 Doxygen::memberNameLinkedMap = new MemberNameLinkedMap;
10790 Doxygen::functionNameLinkedMap = new MemberNameLinkedMap;
10791 Doxygen::groupLinkedMap = new GroupLinkedMap;
10792 Doxygen::namespaceLinkedMap = new NamespaceLinkedMap;
10793 Doxygen::classLinkedMap = new ClassLinkedMap;
10794 Doxygen::hiddenClassLinkedMap = new ClassLinkedMap;
10795 Doxygen::conceptLinkedMap = new ConceptLinkedMap;
10796 Doxygen::dirLinkedMap = new DirLinkedMap;
10797 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
10798 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
10799 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
10800 Doxygen::indexList = new IndexList;
10802 // initialisation of these globals depends on
10803 // configuration switches so we need to postpone these
10804 Doxygen::globalScope = 0;
10805 Doxygen::inputNameLinkedMap = 0;
10806 Doxygen::includeNameLinkedMap = 0;
10807 Doxygen::exampleNameLinkedMap = 0;
10808 Doxygen::imageNameLinkedMap = 0;
10809 Doxygen::dotFileNameLinkedMap = 0;
10810 Doxygen::mscFileNameLinkedMap = 0;
10811 Doxygen::diaFileNameLinkedMap = 0;
10813 /**************************************************************************
10814 * Initialize some global constants
10815 **************************************************************************/
10817 g_compoundKeywords.insert("template class");
10818 g_compoundKeywords.insert("template struct");
10819 g_compoundKeywords.insert("class");
10820 g_compoundKeywords.insert("struct");
10821 g_compoundKeywords.insert("union");
10822 g_compoundKeywords.insert("interface");
10823 g_compoundKeywords.insert("exception");
10826 void cleanUpDoxygen()
10828 FormulaManager::instance().clear();
10829 SectionManager::instance().clear();
10830 ModuleManager::instance().clear();
10832 delete Doxygen::indexList;
10833 delete Doxygen::inputNameLinkedMap;
10834 delete Doxygen::includeNameLinkedMap;
10835 delete Doxygen::exampleNameLinkedMap;
10836 delete Doxygen::imageNameLinkedMap;
10837 delete Doxygen::dotFileNameLinkedMap;
10838 delete Doxygen::mscFileNameLinkedMap;
10839 delete Doxygen::diaFileNameLinkedMap;
10840 Doxygen::mainPage.reset();
10841 delete Doxygen::pageLinkedMap;
10842 delete Doxygen::exampleLinkedMap;
10843 Doxygen::globalNamespaceDef.reset();
10844 Doxygen::globalScope = 0;
10845 delete Doxygen::parserManager;
10846 delete theTranslator;
10847 delete g_outputList;
10849 delete Doxygen::memberNameLinkedMap;
10850 delete Doxygen::functionNameLinkedMap;
10851 delete Doxygen::groupLinkedMap;
10852 delete Doxygen::namespaceLinkedMap;
10853 delete Doxygen::dirLinkedMap;
10854 delete Doxygen::symbolMap;
10857 static int computeIdealCacheParam(size_t v)
10859 //printf("computeIdealCacheParam(v=%u)\n",v);
10862 while (v!=0) v>>=1,r++;
10865 // convert to a valid cache size value
10866 return std::max(0,std::min(r-16,9));
10869 void readConfiguration(int argc, char **argv)
10871 QCString versionString = getFullVersion();
10873 // helper that calls \a func to write to file \a fileName via a TextStream
10874 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
10877 if (openOutputFile(fileName,f))
10887 /**************************************************************************
10888 * Handle arguments *
10889 **************************************************************************/
10892 QCString configName;
10893 QCString layoutName;
10894 QCString debugLabel;
10895 QCString formatName;
10897 QCString traceName;
10898 bool genConfig=FALSE;
10899 bool shortList=FALSE;
10900 Config::CompareMode diffList=Config::CompareMode::Full;
10901 bool updateConfig=FALSE;
10903 bool quiet = false;
10904 while (optInd<argc && argv[optInd][0]=='-' &&
10905 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
10906 argv[optInd][1]=='-')
10909 switch(argv[optInd][1])
10915 if (optInd+1>=argc)
10917 layoutName="DoxygenLayout.xml";
10921 layoutName=argv[optInd+1];
10923 writeDefaultLayoutFile(layoutName);
10928 debugLabel=getArg(argc,argv,optInd);
10929 if (debugLabel.isEmpty())
10935 retVal = Debug::setFlagStr(debugLabel);
10938 err("option \"-d\" has unknown debug specifier: \"%s\".\n",qPrint(debugLabel));
10947 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
10949 traceName="stdout";
10953 traceName=argv[optInd+1];
10957 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
10964 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
10965 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
10968 err("option should be \"-x\" or \"-x_noenv\", found: \"%s\".\n",argv[optInd]);
10980 formatName=getArg(argc,argv,optInd);
10981 if (formatName.isEmpty())
10983 err("option \"-e\" is missing format specifier rtf.\n");
10987 if (qstricmp(formatName.data(),"rtf")==0)
10989 if (optInd+1>=argc)
10991 err("option \"-e rtf\" is missing an extensions file name\n");
10995 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
10999 err("option \"-e\" has invalid format specifier.\n");
11004 listName=getArg(argc,argv,optInd);
11005 if (listName.isEmpty())
11007 err("option \"-f\" is missing list specifier.\n");
11011 if (qstricmp(listName.data(),"emoji")==0)
11013 if (optInd+1>=argc)
11015 err("option \"-f emoji\" is missing an output file name\n");
11019 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11023 err("option \"-f\" has invalid list specifier.\n");
11028 formatName=getArg(argc,argv,optInd);
11029 if (formatName.isEmpty())
11031 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11035 if (qstricmp(formatName.data(),"rtf")==0)
11037 if (optInd+1>=argc)
11039 err("option \"-w rtf\" is missing a style sheet file name\n");
11043 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11045 err("error opening RTF style sheet file %s!\n",argv[optInd+1]);
11052 else if (qstricmp(formatName.data(),"html")==0)
11055 if (optInd+4<argc || FileInfo("Doxyfile").exists())
11056 // explicit config file mentioned or default found on disk
11058 QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
11059 if (!Config::parse(df)) // parse the config file
11061 err("error opening or reading configuration file %s!\n",argv[optInd+4]);
11066 if (optInd+3>=argc)
11068 err("option \"-w html\" does not have enough arguments\n");
11072 Config::postProcess(TRUE);
11073 Config::updateObsolete();
11074 Config::checkAndCorrect(Config_getBool(QUIET), false);
11075 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11076 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11077 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11078 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11082 else if (qstricmp(formatName.data(),"latex")==0)
11085 if (optInd+4<argc || FileInfo("Doxyfile").exists())
11087 QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
11088 if (!Config::parse(df))
11090 err("error opening or reading configuration file %s!\n",argv[optInd+4]);
11095 if (optInd+3>=argc)
11097 err("option \"-w latex\" does not have enough arguments\n");
11101 Config::postProcess(TRUE);
11102 Config::updateObsolete();
11103 Config::checkAndCorrect(Config_getBool(QUIET), false);
11104 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11105 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11106 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11107 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11113 err("Illegal format specifier \"%s\": should be one of rtf, html or latex\n",qPrint(formatName));
11119 g_dumpSymbolMap = TRUE;
11132 if (qstrcmp(&argv[optInd][2],"help")==0)
11134 usage(argv[0],versionString);
11137 else if (qstrcmp(&argv[optInd][2],"version")==0)
11143 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11144 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11152 err("Unknown option \"-%s\"\n",&argv[optInd][1]);
11153 usage(argv[0],versionString);
11158 setvbuf(stdout,NULL,_IONBF,0);
11165 usage(argv[0],versionString);
11169 err("Unknown option \"-%c\"\n",argv[optInd][1]);
11170 usage(argv[0],versionString);
11176 /**************************************************************************
11177 * Parse or generate the config file *
11178 **************************************************************************/
11180 initTracing(traceName.data());
11181 TRACE("Doxygen version used: {}",getFullVersion());
11184 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11187 if (configFileInfo1.exists())
11189 configName="Doxyfile";
11191 else if (configFileInfo2.exists())
11193 configName="doxyfile";
11195 else if (genConfig)
11197 configName="Doxyfile";
11201 err("Doxyfile not found and no input file specified!\n");
11202 usage(argv[0],versionString);
11208 FileInfo fi(argv[optInd]);
11209 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11211 configName=argv[optInd];
11215 err("configuration file %s not found!\n",argv[optInd]);
11216 usage(argv[0],versionString);
11223 generateConfigFile(configName,shortList);
11228 if (!Config::parse(configName,updateConfig,diffList))
11230 err("could not open or read configuration file %s!\n",qPrint(configName));
11235 if (diffList!=Config::CompareMode::Full)
11237 Config::updateObsolete();
11238 compareDoxyfile(diffList);
11245 Config::updateObsolete();
11246 generateConfigFile(configName,shortList,TRUE);
11251 /* Perlmod wants to know the path to the config file.*/
11252 FileInfo configFileInfo(configName.str());
11253 setPerlModDoxyfile(configFileInfo.absFilePath());
11255 /* handle -q option */
11256 if (quiet) Config_updateBool(QUIET,TRUE);
11259 /** check and resolve config options */
11260 void checkConfiguration()
11264 Config::postProcess(FALSE);
11265 Config::updateObsolete();
11266 Config::checkAndCorrect(Config_getBool(QUIET), true);
11267 initWarningFormat();
11270 /** adjust globals that depend on configuration settings. */
11271 void adjustConfiguration()
11274 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11275 Doxygen::globalScope = toNamespaceDefMutable(Doxygen::globalNamespaceDef.get());
11276 Doxygen::inputNameLinkedMap = new FileNameLinkedMap;
11277 Doxygen::includeNameLinkedMap = new FileNameLinkedMap;
11278 Doxygen::exampleNameLinkedMap = new FileNameLinkedMap;
11279 Doxygen::imageNameLinkedMap = new FileNameLinkedMap;
11280 Doxygen::dotFileNameLinkedMap = new FileNameLinkedMap;
11281 Doxygen::mscFileNameLinkedMap = new FileNameLinkedMap;
11282 Doxygen::diaFileNameLinkedMap = new FileNameLinkedMap;
11284 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11286 /* Set the global html file extension. */
11287 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11290 Doxygen::parseSourcesNeeded = Config_getBool(CALL_GRAPH) ||
11291 Config_getBool(CALLER_GRAPH) ||
11292 Config_getBool(REFERENCES_RELATION) ||
11293 Config_getBool(REFERENCED_BY_RELATION);
11295 /**************************************************************************
11296 * Add custom extension mappings
11297 **************************************************************************/
11299 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11300 for (const auto &mapping : extMaps)
11302 QCString mapStr = mapping.c_str();
11303 int i=mapStr.find('=');
11310 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11311 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11312 if (ext.isEmpty() || language.isEmpty())
11317 if (!updateLanguageMapping(ext,language))
11319 err("Failed to map file extension '%s' to unsupported language '%s'.\n"
11320 "Check the EXTENSION_MAPPING setting in the config file.\n",
11321 qPrint(ext),qPrint(language));
11325 msg("Adding custom extension mapping: '%s' will be treated as language '%s'\n",
11326 qPrint(ext),qPrint(language));
11330 // create input file exncodings
11332 // check INPUT_ENCODING
11333 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
11334 if (cd==reinterpret_cast<void *>(-1))
11336 term("unsupported character conversion: '%s'->'%s': %s\n"
11337 "Check the 'INPUT_ENCODING' setting in the config file!\n",
11338 qPrint(Config_getString(INPUT_ENCODING)),qPrint("UTF-8"),strerror(errno));
11342 portable_iconv_close(cd);
11345 // check and split INPUT_FILE_ENCODING
11346 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
11347 for (const auto &mapping : fileEncod)
11349 QCString mapStr = mapping.c_str();
11350 int i=mapStr.find('=');
11357 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
11358 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
11359 if (pattern.isEmpty() || encoding.isEmpty())
11363 cd = portable_iconv_open("UTF-8",encoding.data());
11364 if (cd==reinterpret_cast<void *>(-1))
11366 term("unsupported character conversion: '%s'->'%s': %s\n"
11367 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
11368 qPrint(encoding),qPrint("UTF-8"),strerror(errno));
11372 portable_iconv_close(cd);
11375 Doxygen::inputFileEncodingList.push_back(InputFileEncoding(pattern, encoding));
11379 // add predefined macro name to a dictionary
11380 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11381 for (const auto &s : expandAsDefinedList)
11383 Doxygen::expandAsDefinedSet.insert(s.c_str());
11386 // read aliases and store them in a dictionary
11389 // store number of spaces in a tab into Doxygen::spaces
11390 int tabSize = Config_getInt(TAB_SIZE);
11391 Doxygen::spaces.resize(tabSize+1);
11392 int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' ';
11393 Doxygen::spaces.at(tabSize)='\0';
11397 static void stopDoxygen(int)
11399 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
11401 msg("Cleaning up...\n");
11402 if (!Doxygen::filterDBFileName.isEmpty())
11404 thisDir.remove(Doxygen::filterDBFileName.str());
11408 Doxygen::terminating=true;
11414 static void writeTagFile()
11416 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
11417 if (generateTagFile.isEmpty()) return;
11419 std::ofstream f = Portable::openOutputStream(generateTagFile);
11422 err("cannot open tag file %s for writing\n",
11423 qPrint(generateTagFile)
11427 TextStream tagFile(&f);
11428 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
11429 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
11430 if (strlen(getGitVersion())>0)
11432 tagFile << " doxygen_gitid=\"" << getGitVersion() << "\"";
11437 for (const auto &fn : *Doxygen::inputNameLinkedMap)
11439 for (const auto &fd : *fn)
11441 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
11445 for (const auto &cd : *Doxygen::classLinkedMap)
11447 ClassDefMutable *cdm = toClassDefMutable(cd.get());
11448 if (cdm && cdm->isLinkableInProject())
11450 cdm->writeTagFile(tagFile);
11453 // for each concept
11454 for (const auto &cd : *Doxygen::conceptLinkedMap)
11456 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
11457 if (cdm && cdm->isLinkableInProject())
11459 cdm->writeTagFile(tagFile);
11462 // for each namespace
11463 for (const auto &nd : *Doxygen::namespaceLinkedMap)
11465 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
11466 if (ndm && nd->isLinkableInProject())
11468 ndm->writeTagFile(tagFile);
11472 for (const auto &gd : *Doxygen::groupLinkedMap)
11474 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
11477 for (const auto &mod : ModuleManager::instance().modules())
11479 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
11482 for (const auto &pd : *Doxygen::pageLinkedMap)
11484 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
11486 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
11488 tagFile << "</tagfile>\n";
11491 static void exitDoxygen() noexcept
11493 if (!g_successfulRun) // premature exit
11496 msg("Exiting...\n");
11497 if (!Doxygen::filterDBFileName.isEmpty())
11499 thisDir.remove(Doxygen::filterDBFileName.str());
11504 static QCString createOutputDirectory(const QCString &baseDirName,
11505 const QCString &formatDirName,
11506 const char *defaultDirName)
11508 QCString result = formatDirName;
11509 if (result.isEmpty())
11511 result = baseDirName + defaultDirName;
11513 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
11515 result.prepend(baseDirName+"/");
11517 Dir formatDir(result.str());
11518 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
11520 err("Could not create output directory %s\n", qPrint(result));
11527 void searchInputFiles()
11529 StringUnorderedSet killSet;
11531 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11532 bool alwaysRecursive = Config_getBool(RECURSIVE);
11533 StringUnorderedSet excludeNameSet;
11535 // gather names of all files in the include path
11536 g_s.begin("Searching for include files...\n");
11538 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
11539 for (const auto &s : includePathList)
11541 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
11542 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
11543 Config_getList(INCLUDE_FILE_PATTERNS);
11544 readFileOrDirectory(s.c_str(), // s
11545 Doxygen::includeNameLinkedMap, // fnDict
11548 &exclPatterns, // exclPatList
11551 false, // INCLUDE_PATH isn't recursive
11552 TRUE, // errorIfNotExist
11553 &killSet); // killSet
11557 g_s.begin("Searching for example files...\n");
11559 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
11560 for (const auto &s : examplePathList)
11562 readFileOrDirectory(s.c_str(), // s
11563 Doxygen::exampleNameLinkedMap, // fnDict
11565 &Config_getList(EXAMPLE_PATTERNS), // patList
11569 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
11570 TRUE, // errorIfNotExist
11571 &killSet); // killSet
11575 g_s.begin("Searching for images...\n");
11577 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
11578 for (const auto &s : imagePathList)
11580 readFileOrDirectory(s.c_str(), // s
11581 Doxygen::imageNameLinkedMap, // fnDict
11587 alwaysRecursive, // recursive
11588 TRUE, // errorIfNotExist
11589 &killSet); // killSet
11593 g_s.begin("Searching for dot files...\n");
11595 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
11596 for (const auto &s : dotFileList)
11598 readFileOrDirectory(s.c_str(), // s
11599 Doxygen::dotFileNameLinkedMap, // fnDict
11605 alwaysRecursive, // recursive
11606 TRUE, // errorIfNotExist
11607 &killSet); // killSet
11611 g_s.begin("Searching for msc files...\n");
11613 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
11614 for (const auto &s : mscFileList)
11616 readFileOrDirectory(s.c_str(), // s
11617 Doxygen::mscFileNameLinkedMap, // fnDict
11623 alwaysRecursive, // recursive
11624 TRUE, // errorIfNotExist
11625 &killSet); // killSet
11629 g_s.begin("Searching for dia files...\n");
11631 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
11632 for (const auto &s : diaFileList)
11634 readFileOrDirectory(s.c_str(), // s
11635 Doxygen::diaFileNameLinkedMap, // fnDict
11641 alwaysRecursive, // recursive
11642 TRUE, // errorIfNotExist
11643 &killSet); // killSet
11647 g_s.begin("Searching for files to exclude\n");
11648 const StringVector &excludeList = Config_getList(EXCLUDE);
11649 for (const auto &s : excludeList)
11651 readFileOrDirectory(s.c_str(), // s
11654 &Config_getList(FILE_PATTERNS), // patList
11657 &excludeNameSet, // resultSet
11658 alwaysRecursive, // recursive
11659 FALSE); // errorIfNotExist
11663 /**************************************************************************
11664 * Determine Input Files *
11665 **************************************************************************/
11667 g_s.begin("Searching INPUT for files to process...\n");
11669 Doxygen::inputPaths.clear();
11670 const StringVector &inputList=Config_getList(INPUT);
11671 for (const auto &s : inputList)
11673 QCString path=s.c_str();
11674 uint32_t l = path.length();
11677 // strip trailing slashes
11678 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
11680 readFileOrDirectory(
11682 Doxygen::inputNameLinkedMap, // fnDict
11683 &excludeNameSet, // exclSet
11684 &Config_getList(FILE_PATTERNS), // patList
11685 &exclPatterns, // exclPatList
11686 &g_inputFiles, // resultList
11688 alwaysRecursive, // recursive
11689 TRUE, // errorIfNotExist
11690 &killSet, // killSet
11691 &Doxygen::inputPaths); // paths
11695 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
11696 std::sort(Doxygen::inputNameLinkedMap->begin(),
11697 Doxygen::inputNameLinkedMap->end(),
11698 [](const auto &f1,const auto &f2)
11700 return qstricmp(f1->fullName(),f2->fullName())<0;
11702 for (auto &fileName : *Doxygen::inputNameLinkedMap)
11704 if (fileName->size()>1)
11706 std::sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
11708 return qstricmp(f1->absFilePath(),f2->absFilePath())<0;
11712 if (Doxygen::inputNameLinkedMap->empty())
11714 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
11720 static void checkMarkdownMainfile()
11722 if (Config_getBool(MARKDOWN_SUPPORT))
11724 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
11725 if (mdfileAsMainPage.isEmpty()) return;
11726 FileInfo fi(mdfileAsMainPage.data());
11729 warn_uncond("Specified markdown mainpage '%s' does not exist\n",qPrint(mdfileAsMainPage));
11732 bool ambig = false;
11733 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==0)
11735 warn_uncond("Specified markdown mainpage '%s' has not been defined as input file\n",qPrint(mdfileAsMainPage));
11744 std::atexit(exitDoxygen);
11747 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
11750 // we would like to show the versionString earlier, but we first have to handle the configuration file
11751 // to know the value of the QUIET setting.
11752 QCString versionString = getFullVersion();
11753 msg("Doxygen version used: %s\n",qPrint(versionString));
11755 computeVerifiedDotPath();
11757 /**************************************************************************
11758 * Make sure the output directory exists
11759 **************************************************************************/
11760 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
11761 if (outputDirectory.isEmpty())
11763 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath().c_str());
11767 Dir dir(outputDirectory.str());
11770 dir.setPath(Dir::currentDirPath());
11771 if (!dir.mkdir(outputDirectory.str()))
11773 err("tag OUTPUT_DIRECTORY: Output directory '%s' does not "
11774 "exist and cannot be created\n",qPrint(outputDirectory));
11780 msg("Notice: Output directory '%s' does not exist. "
11781 "I have created it for you.\n", qPrint(outputDirectory));
11783 dir.setPath(outputDirectory.str());
11785 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath().c_str());
11787 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
11789 /**************************************************************************
11790 * Initialize global lists and dictionaries
11791 **************************************************************************/
11793 // also scale lookup cache with SYMBOL_CACHE_SIZE
11794 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
11795 if (cacheSize<0) cacheSize=0;
11796 if (cacheSize>9) cacheSize=9;
11797 uint32_t lookupSize = 65536 << cacheSize;
11798 Doxygen::typeLookupCache = new Cache<std::string,LookupInfo>(lookupSize);
11799 Doxygen::symbolLookupCache = new Cache<std::string,LookupInfo>(lookupSize);
11802 signal(SIGINT, stopDoxygen);
11805 uint32_t pid = Portable::pid();
11806 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
11807 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
11809 /**************************************************************************
11810 * Check/create output directories *
11811 **************************************************************************/
11813 QCString htmlOutput;
11814 bool generateHtml = Config_getBool(GENERATE_HTML);
11817 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
11818 Config_updateString(HTML_OUTPUT,htmlOutput);
11820 QCString sitemapUrl = Config_getString(SITEMAP_URL);
11821 bool generateSitemap = !sitemapUrl.isEmpty();
11822 if (generateSitemap && !sitemapUrl.endsWith("/"))
11824 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
11827 // add HTML indexers that are enabled
11828 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
11829 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
11830 bool generateQhp = Config_getBool(GENERATE_QHP);
11831 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
11832 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
11833 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
11834 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
11835 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
11836 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
11837 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
11838 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
11839 Doxygen::indexList->initialize();
11842 QCString docbookOutput;
11843 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
11844 if (generateDocbook)
11846 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
11847 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
11850 QCString xmlOutput;
11851 bool generateXml = Config_getBool(GENERATE_XML);
11854 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
11855 Config_updateString(XML_OUTPUT,xmlOutput);
11858 QCString latexOutput;
11859 bool generateLatex = Config_getBool(GENERATE_LATEX);
11862 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
11863 Config_updateString(LATEX_OUTPUT,latexOutput);
11866 QCString rtfOutput;
11867 bool generateRtf = Config_getBool(GENERATE_RTF);
11870 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
11871 Config_updateString(RTF_OUTPUT,rtfOutput);
11874 QCString manOutput;
11875 bool generateMan = Config_getBool(GENERATE_MAN);
11878 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
11879 Config_updateString(MAN_OUTPUT,manOutput);
11882 QCString sqlOutput;
11883 bool generateSql = Config_getBool(GENERATE_SQLITE3);
11886 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
11887 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
11890 if (Config_getBool(HAVE_DOT))
11892 QCString curFontPath = Config_getString(DOT_FONTPATH);
11893 if (curFontPath.isEmpty())
11895 Portable::getenv("DOTFONTPATH");
11896 QCString newFontPath = ".";
11897 if (!curFontPath.isEmpty())
11899 newFontPath+=Portable::pathListSeparator();
11900 newFontPath+=curFontPath;
11902 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
11906 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
11912 /**************************************************************************
11913 * Handle layout file *
11914 **************************************************************************/
11916 LayoutDocManager::instance().init();
11917 QCString layoutFileName = Config_getString(LAYOUT_FILE);
11918 bool defaultLayoutUsed = FALSE;
11919 if (layoutFileName.isEmpty())
11921 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
11922 defaultLayoutUsed = TRUE;
11924 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
11926 FileInfo fi(layoutFileName.str());
11929 msg("Parsing layout file %s...\n",qPrint(layoutFileName));
11930 LayoutDocManager::instance().parse(layoutFileName);
11932 else if (!defaultLayoutUsed)
11934 warn_uncond("failed to open layout file '%s' for reading! Using default settings.\n",qPrint(layoutFileName));
11937 /**************************************************************************
11938 * Read and preprocess input *
11939 **************************************************************************/
11941 // prevent search in the output directories
11942 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11943 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
11944 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
11945 if (generateXml) exclPatterns.push_back(xmlOutput.str());
11946 if (generateLatex) exclPatterns.push_back(latexOutput.str());
11947 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
11948 if (generateMan) exclPatterns.push_back(manOutput.str());
11949 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
11951 searchInputFiles();
11953 checkMarkdownMainfile();
11955 // Notice: the order of the function calls below is very important!
11957 if (Config_getBool(GENERATE_HTML) && !Config_getBool(USE_MATHJAX))
11959 FormulaManager::instance().initFromRepository(Config_getString(HTML_OUTPUT));
11961 if (Config_getBool(GENERATE_RTF))
11963 FormulaManager::instance().initFromRepository(Config_getString(RTF_OUTPUT));
11965 if (Config_getBool(GENERATE_DOCBOOK))
11967 FormulaManager::instance().initFromRepository(Config_getString(DOCBOOK_OUTPUT));
11970 FormulaManager::instance().checkRepositories();
11972 /**************************************************************************
11973 * Handle Tag Files *
11974 **************************************************************************/
11976 std::shared_ptr<Entry> root = std::make_shared<Entry>();
11977 msg("Reading and parsing tag files\n");
11979 const StringVector &tagFileList = Config_getList(TAGFILES);
11980 for (const auto &s : tagFileList)
11982 readTagFile(root,s.c_str());
11985 /**************************************************************************
11986 * Parse source files *
11987 **************************************************************************/
11989 addSTLSupport(root);
11991 g_s.begin("Parsing files\n");
11992 if (Config_getInt(NUM_PROC_THREADS)==1)
11994 parseFilesSingleThreading(root);
11998 parseFilesMultiThreading(root);
12002 /**************************************************************************
12003 * Gather information *
12004 **************************************************************************/
12006 g_s.begin("Building macro definition list...\n");
12010 g_s.begin("Building group list...\n");
12011 buildGroupList(root.get());
12012 organizeSubGroups(root.get());
12015 g_s.begin("Building directory list...\n");
12016 buildDirectories();
12017 findDirDocumentation(root.get());
12020 g_s.begin("Building namespace list...\n");
12021 buildNamespaceList(root.get());
12022 findUsingDirectives(root.get());
12025 g_s.begin("Building file list...\n");
12026 buildFileList(root.get());
12029 g_s.begin("Building class list...\n");
12030 buildClassList(root.get());
12033 g_s.begin("Building concept list...\n");
12034 buildConceptList(root.get());
12037 // build list of using declarations here (global list)
12038 buildListOfUsingDecls(root.get());
12041 g_s.begin("Computing nesting relations for classes...\n");
12042 resolveClassNestingRelations();
12044 // 1.8.2-20121111: no longer add nested classes to the group as well
12045 //distributeClassGroupRelations();
12047 // calling buildClassList may result in cached relations that
12048 // become invalid after resolveClassNestingRelations(), that's why
12049 // we need to clear the cache here
12050 Doxygen::typeLookupCache->clear();
12051 // we don't need the list of using declaration anymore
12052 g_usingDeclarations.clear();
12054 g_s.begin("Associating documentation with classes...\n");
12055 buildClassDocList(root.get());
12058 g_s.begin("Associating documentation with concepts...\n");
12059 buildConceptDocList(root.get());
12060 distributeConceptGroups();
12063 g_s.begin("Associating documentation with modules...\n");
12064 findModuleDocumentation(root.get());
12067 g_s.begin("Building example list...\n");
12068 buildExampleList(root.get());
12071 g_s.begin("Searching for enumerations...\n");
12072 findEnums(root.get());
12075 // Since buildVarList calls isVarWithConstructor
12076 // and this calls getResolvedClass we need to process
12077 // typedefs first so the relations between classes via typedefs
12078 // are properly resolved. See bug 536385 for an example.
12079 g_s.begin("Searching for documented typedefs...\n");
12080 buildTypedefList(root.get());
12083 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12085 g_s.begin("Searching for documented sequences...\n");
12086 buildSequenceList(root.get());
12089 g_s.begin("Searching for documented dictionaries...\n");
12090 buildDictionaryList(root.get());
12094 g_s.begin("Searching for members imported via using declarations...\n");
12095 // this should be after buildTypedefList in order to properly import
12097 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12098 findUsingDeclarations(root.get(),FALSE); // then the rest
12101 g_s.begin("Searching for included using directives...\n");
12102 findIncludedUsingDirectives();
12105 g_s.begin("Searching for documented variables...\n");
12106 buildVarList(root.get());
12109 g_s.begin("Building interface member list...\n");
12110 buildInterfaceAndServiceList(root.get()); // UNO IDL
12112 g_s.begin("Building member list...\n"); // using class info only !
12113 buildFunctionList(root.get());
12116 g_s.begin("Searching for friends...\n");
12120 g_s.begin("Searching for documented defines...\n");
12121 findDefineDocumentation(root.get());
12124 g_s.begin("Computing class inheritance relations...\n");
12125 findClassEntries(root.get());
12126 findInheritedTemplateInstances();
12129 g_s.begin("Computing class usage relations...\n");
12130 findUsedTemplateInstances();
12133 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12135 g_s.begin("Searching for tag less structs...\n");
12136 findTagLessClasses();
12140 g_s.begin("Flushing cached template relations that have become invalid...\n");
12141 flushCachedTemplateRelations();
12144 g_s.begin("Computing class relations...\n");
12145 computeTemplateClassRelations();
12146 flushUnresolvedRelations();
12147 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12149 VhdlDocGen::computeVhdlComponentRelations();
12151 computeClassRelations();
12152 g_classEntries.clear();
12155 g_s.begin("Add enum values to enums...\n");
12156 addEnumValuesToEnums(root.get());
12157 findEnumDocumentation(root.get());
12160 g_s.begin("Searching for member function documentation...\n");
12161 findObjCMethodDefinitions(root.get());
12162 findMemberDocumentation(root.get()); // may introduce new members !
12163 findUsingDeclImports(root.get()); // may introduce new members !
12164 transferRelatedFunctionDocumentation();
12165 transferFunctionDocumentation();
12166 transferStaticInstanceInitializers();
12169 // moved to after finding and copying documentation,
12170 // as this introduces new members see bug 722654
12171 g_s.begin("Creating members for template instances...\n");
12172 createTemplateInstanceMembers();
12175 g_s.begin("Building page list...\n");
12176 buildPageList(root.get());
12179 g_s.begin("Search for main page...\n");
12180 findMainPage(root.get());
12181 findMainPageTagFiles(root.get());
12184 g_s.begin("Computing page relations...\n");
12185 computePageRelations(root.get());
12186 checkPageRelations();
12189 g_s.begin("Determining the scope of groups...\n");
12190 findGroupScope(root.get());
12193 g_s.begin("Computing module relations...\n");
12194 auto &mm = ModuleManager::instance();
12195 mm.resolvePartitions();
12196 mm.resolveImports();
12197 mm.collectExportedSymbols();
12200 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12202 return qstricmp(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12203 n2->memberName().data()+getPrefixIndex(n2->memberName())
12207 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12209 if (Config_getBool(SORT_BY_SCOPE_NAME))
12211 return qstricmp(c1->name(), c2->name())<0;
12215 int i = qstricmp(c1->className(), c2->className());
12216 return i==0 ? qstricmp(c1->name(), c2->name())<0 : i<0;
12220 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12222 return qstricmp(n1->name(),n2->name())<0;
12225 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12227 return qstricmp(c1->name(),c2->name())<0;
12230 g_s.begin("Sorting lists...\n");
12231 std::sort(Doxygen::memberNameLinkedMap->begin(),
12232 Doxygen::memberNameLinkedMap->end(),
12234 std::sort(Doxygen::functionNameLinkedMap->begin(),
12235 Doxygen::functionNameLinkedMap->end(),
12237 std::sort(Doxygen::hiddenClassLinkedMap->begin(),
12238 Doxygen::hiddenClassLinkedMap->end(),
12240 std::sort(Doxygen::classLinkedMap->begin(),
12241 Doxygen::classLinkedMap->end(),
12243 std::sort(Doxygen::conceptLinkedMap->begin(),
12244 Doxygen::conceptLinkedMap->end(),
12246 std::sort(Doxygen::namespaceLinkedMap->begin(),
12247 Doxygen::namespaceLinkedMap->end(),
12251 g_s.begin("Determining which enums are documented\n");
12252 findDocumentedEnumValues();
12255 g_s.begin("Computing member relations...\n");
12257 computeMemberRelations();
12260 g_s.begin("Building full member lists recursively...\n");
12261 buildCompleteMemberLists();
12264 g_s.begin("Adding members to member groups.\n");
12265 addMembersToMemberGroup();
12268 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12270 g_s.begin("Distributing member group documentation.\n");
12271 distributeMemberGroupDocumentation();
12275 g_s.begin("Computing member references...\n");
12276 computeMemberReferences();
12279 if (Config_getBool(INHERIT_DOCS))
12281 g_s.begin("Inheriting documentation...\n");
12282 inheritDocumentation();
12287 // compute the shortest possible names of all files
12288 // without losing the uniqueness of the file names.
12289 g_s.begin("Generating disk names...\n");
12290 generateDiskNames();
12293 g_s.begin("Adding source references...\n");
12294 addSourceReferences();
12297 g_s.begin("Adding xrefitems...\n");
12298 addListReferences();
12299 generateXRefPages();
12302 g_s.begin("Sorting member lists...\n");
12306 g_s.begin("Setting anonymous enum type...\n");
12307 setAnonymousEnumType();
12310 g_s.begin("Computing dependencies between directories...\n");
12311 computeDirDependencies();
12314 g_s.begin("Generating citations page...\n");
12315 CitationManager::instance().generatePage();
12318 g_s.begin("Counting members...\n");
12322 g_s.begin("Counting data structures...\n");
12323 Index::instance().countDataStructures();
12326 g_s.begin("Resolving user defined references...\n");
12327 resolveUserReferences();
12330 g_s.begin("Finding anchors and sections in the documentation...\n");
12331 findSectionsInDocumentation();
12334 g_s.begin("Transferring function references...\n");
12335 transferFunctionReferences();
12338 g_s.begin("Combining using relations...\n");
12339 combineUsingRelations();
12342 initSearchIndexer();
12343 g_s.begin("Adding members to index pages...\n");
12344 addMembersToIndex();
12348 g_s.begin("Correcting members for VHDL...\n");
12349 vhdlCorrectMemberProperties();
12352 g_s.begin("Computing tooltip texts...\n");
12353 computeTooltipTexts();
12356 if (Config_getBool(SORT_GROUP_NAMES))
12358 std::sort(Doxygen::groupLinkedMap->begin(),
12359 Doxygen::groupLinkedMap->end(),
12360 [](const auto &g1,const auto &g2)
12361 { return g1->groupTitle() < g2->groupTitle(); });
12363 for (const auto &gd : *Doxygen::groupLinkedMap)
12365 gd->sortSubGroups();
12369 printNavTree(root.get(),0);
12372 void generateOutput()
12375 /**************************************************************************
12376 * Initialize output generators *
12377 **************************************************************************/
12379 /// add extra languages for which we can only produce syntax highlighted code
12380 addCodeOnlyMappings();
12382 //// dump all symbols
12383 if (g_dumpSymbolMap)
12389 bool generateHtml = Config_getBool(GENERATE_HTML);
12390 bool generateLatex = Config_getBool(GENERATE_LATEX);
12391 bool generateMan = Config_getBool(GENERATE_MAN);
12392 bool generateRtf = Config_getBool(GENERATE_RTF);
12393 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12396 g_outputList = new OutputList;
12399 g_outputList->add<HtmlGenerator>();
12400 HtmlGenerator::init();
12401 HtmlGenerator::writeTabData();
12405 g_outputList->add<LatexGenerator>();
12406 LatexGenerator::init();
12408 if (generateDocbook)
12410 g_outputList->add<DocbookGenerator>();
12411 DocbookGenerator::init();
12415 g_outputList->add<ManGenerator>();
12416 ManGenerator::init();
12420 g_outputList->add<RTFGenerator>();
12421 RTFGenerator::init();
12423 if (Config_getBool(USE_HTAGS))
12425 Htags::useHtags = TRUE;
12426 QCString htmldir = Config_getString(HTML_OUTPUT);
12427 if (!Htags::execute(htmldir))
12428 err("USE_HTAGS is YES but htags(1) failed. \n");
12429 else if (!Htags::loadFilemap(htmldir))
12430 err("htags(1) ended normally but failed to load the filemap. \n");
12433 /**************************************************************************
12434 * Generate documentation *
12435 **************************************************************************/
12437 g_s.begin("Generating style sheet...\n");
12438 //printf("writing style info\n");
12439 g_outputList->writeStyleInfo(0); // write first part
12442 bool searchEngine = Config_getBool(SEARCHENGINE);
12443 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
12445 g_s.begin("Generating search indices...\n");
12446 if (searchEngine && !serverBasedSearch && generateHtml)
12448 createJavaScriptSearchIndex();
12451 // generate search indices (need to do this before writing other HTML
12452 // pages as these contain a drop down menu with options depending on
12453 // what categories we find in this function.
12454 if (generateHtml && searchEngine)
12456 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
12457 Dir searchDir(searchDirName.str());
12458 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
12460 term("Could not create search results directory '%s' $PWD='%s'\n",
12461 qPrint(searchDirName),Dir::currentDirPath().c_str());
12463 HtmlGenerator::writeSearchData(searchDirName);
12464 if (!serverBasedSearch) // client side search index
12466 writeJavaScriptSearchIndex();
12471 // copy static stuff
12474 FTVHelp::generateTreeViewImages();
12476 copyLogo(Config_getString(HTML_OUTPUT));
12477 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
12481 copyLatexStyleSheet();
12482 copyLogo(Config_getString(LATEX_OUTPUT));
12483 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
12485 if (generateDocbook)
12487 copyLogo(Config_getString(DOCBOOK_OUTPUT));
12491 copyLogo(Config_getString(RTF_OUTPUT));
12494 FormulaManager &fm = FormulaManager::instance();
12495 if (fm.hasFormulas() && generateHtml
12496 && !Config_getBool(USE_MATHJAX))
12498 g_s.begin("Generating images for formulas in HTML...\n");
12499 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
12500 FormulaManager::Format::Vector : FormulaManager::Format::Bitmap, FormulaManager::HighDPI::On);
12503 if (fm.hasFormulas() && generateRtf)
12505 g_s.begin("Generating images for formulas in RTF...\n");
12506 fm.generateImages(Config_getString(RTF_OUTPUT),FormulaManager::Format::Bitmap);
12510 if (fm.hasFormulas() && generateDocbook)
12512 g_s.begin("Generating images for formulas in Docbook...\n");
12513 fm.generateImages(Config_getString(DOCBOOK_OUTPUT),FormulaManager::Format::Bitmap);
12517 g_s.begin("Generating example documentation...\n");
12518 generateExampleDocs();
12521 g_s.begin("Generating file sources...\n");
12522 generateFileSources();
12525 g_s.begin("Generating file documentation...\n");
12526 generateFileDocs();
12529 g_s.begin("Generating page documentation...\n");
12530 generatePageDocs();
12533 g_s.begin("Generating group documentation...\n");
12534 generateGroupDocs();
12537 g_s.begin("Generating class documentation...\n");
12538 generateClassDocs();
12541 g_s.begin("Generating concept documentation...\n");
12542 generateConceptDocs();
12545 g_s.begin("Generating module documentation...\n");
12546 ModuleManager::instance().writeDocumentation(*g_outputList);
12549 g_s.begin("Generating namespace documentation...\n");
12550 generateNamespaceDocs();
12553 if (Config_getBool(GENERATE_LEGEND))
12555 g_s.begin("Generating graph info page...\n");
12556 writeGraphInfo(*g_outputList);
12560 g_s.begin("Generating directory documentation...\n");
12561 generateDirDocs(*g_outputList);
12564 if (g_outputList->size()>0)
12566 writeIndexHierarchy(*g_outputList);
12569 g_s.begin("finalizing index lists...\n");
12570 Doxygen::indexList->finalize();
12573 g_s.begin("writing tag file...\n");
12577 if (Config_getBool(GENERATE_XML))
12579 g_s.begin("Generating XML output...\n");
12580 Doxygen::generatingXmlOutput=TRUE;
12582 Doxygen::generatingXmlOutput=FALSE;
12585 if (Config_getBool(GENERATE_SQLITE3))
12587 g_s.begin("Generating SQLITE3 output...\n");
12592 if (Config_getBool(GENERATE_AUTOGEN_DEF))
12594 g_s.begin("Generating AutoGen DEF output...\n");
12598 if (Config_getBool(GENERATE_PERLMOD))
12600 g_s.begin("Generating Perl module output...\n");
12604 if (generateHtml && searchEngine && serverBasedSearch)
12606 g_s.begin("Generating search index\n");
12607 if (Doxygen::searchIndex->kind()==SearchIndexIntf::Internal) // write own search index
12609 HtmlGenerator::writeSearchPage();
12610 Doxygen::searchIndex->write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
12612 else // write data for external search index
12614 HtmlGenerator::writeExternalSearchPage();
12615 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
12616 if (searchDataFile.isEmpty())
12618 searchDataFile="searchdata.xml";
12620 if (!Portable::isAbsolutePath(searchDataFile.data()))
12622 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
12624 Doxygen::searchIndex->write(searchDataFile);
12631 g_s.begin("Combining RTF output...\n");
12632 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
12634 err("An error occurred during post-processing the RTF files!\n");
12639 g_s.begin("Running plantuml with JAVA...\n");
12640 PlantumlManager::instance().run();
12643 if (Config_getBool(HAVE_DOT))
12645 g_s.begin("Running dot...\n");
12646 DotManager::instance()->run();
12650 if (generateHtml &&
12651 Config_getBool(GENERATE_HTMLHELP) &&
12652 !Config_getString(HHC_LOCATION).isEmpty())
12654 g_s.begin("Running html help compiler...\n");
12655 runHtmlHelpCompiler();
12659 if ( generateHtml &&
12660 Config_getBool(GENERATE_QHP) &&
12661 !Config_getString(QHG_LOCATION).isEmpty())
12663 g_s.begin("Running qhelpgenerator...\n");
12664 runQHelpGenerator();
12668 g_outputList->cleanup();
12670 msg("type lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n",
12671 Doxygen::typeLookupCache->size(),
12672 Doxygen::typeLookupCache->capacity(),
12673 Doxygen::typeLookupCache->hits(),
12674 Doxygen::typeLookupCache->misses());
12675 msg("symbol lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n",
12676 Doxygen::symbolLookupCache->size(),
12677 Doxygen::symbolLookupCache->capacity(),
12678 Doxygen::symbolLookupCache->hits(),
12679 Doxygen::symbolLookupCache->misses());
12680 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
12681 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
12682 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
12683 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
12685 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is %d at the cost of higher memory usage.\n",cacheParam);
12688 if (Debug::isFlagSet(Debug::Time))
12691 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
12692 if (numThreads<1) numThreads=1;
12693 msg("Total elapsed time: %.6f seconds\n(of which an average of %.6f seconds per thread waiting for external tools to finish)\n",
12694 (static_cast<double>(Debug::elapsedTime())),
12695 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
12699 Debug::clearFlag(Debug::Time);
12700 msg("finished...\n");
12701 Debug::setFlag(Debug::Time);
12705 msg("finished...\n");
12709 /**************************************************************************
12710 * Start cleaning up *
12711 **************************************************************************/
12715 finalizeSearchIndexer();
12717 thisDir.remove(Doxygen::filterDBFileName.str());
12721 delete Doxygen::clangUsrMap;
12722 g_successfulRun=TRUE;
12724 //dumpDocNodeSizes();