Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <cerrno>
19#include <sys/stat.h>
20
21#include <algorithm>
22#include <unordered_map>
23#include <memory>
24#include <cinttypes>
25#include <chrono>
26#include <clocale>
27#include <locale>
28
29#include "aliases.h"
30#include "arguments.h"
31#include "cite.h"
32#include "clangparser.h"
33#include "classlist.h"
34#include "cmdmapper.h"
35#include "code.h"
36#include "commentcnv.h"
37#include "conceptdef.h"
38#include "config.h"
39#include "debug.h"
40#include "declinfo.h"
41#include "defargs.h"
42#include "defgen.h"
43#include "dir.h"
44#include "dirdef.h"
45#include "docbookgen.h"
46#include "docparser.h"
47#include "docsets.h"
48#include "dot.h"
49#include "doxygen.h"
50#include "eclipsehelp.h"
51#include "emoji.h"
52#include "entry.h"
53#include "fileinfo.h"
54#include "filename.h"
55#include "fileparser.h"
56#include "formula.h"
57#include "fortrancode.h"
58#include "fortranscanner.h"
59#include "ftvhelp.h"
60#include "groupdef.h"
61#include "htags.h"
62#include "htmlgen.h"
63#include "htmlhelp.h"
64#include "index.h"
65#include "indexlist.h"
66#include "language.h"
67#include "latexgen.h"
68#include "layout.h"
69#include "lexcode.h"
70#include "lexscanner.h"
71#include "mangen.h"
72#include "markdown.h"
73#include "membergroup.h"
74#include "memberlist.h"
75#include "membername.h"
76#include "mermaid.h"
77#include "message.h"
78#include "moduledef.h"
79#include "msc.h"
80#include "namespacedef.h"
81#include "outputlist.h"
82#include "pagedef.h"
83#include "parserintf.h"
84#include "perlmodgen.h"
85#include "plantuml.h"
86#include "portable.h"
87#include "pre.h"
88#include "pycode.h"
89#include "pyscanner.h"
90#include "qhp.h"
91#include "reflist.h"
92#include "regex.h"
93#include "requirement.h"
94#include "rtfgen.h"
95#include "scanner.h"
96#include "searchindex_js.h"
97#include "settings.h"
98#include "singlecomment.h"
99#include "sitemap.h"
100#include "sqlcode.h"
101#include "sqlite3gen.h"
102#include "stlsupport.h"
103#include "stringutil.h"
104#include "symbolresolver.h"
105#include "tagreader.h"
106#include "threadpool.h"
107#include "trace.h"
108#include "util.h"
109#include "version.h"
110#include "vhdlcode.h"
111#include "vhdldocgen.h"
112#include "vhdljjparser.h"
113#include "xmlcode.h"
114#include "xmlgen.h"
115
116#include <sqlite3.h>
117
118#if USE_LIBCLANG
119#if defined(__GNUC__)
120#pragma GCC diagnostic push
121#pragma GCC diagnostic ignored "-Wshadow"
122#endif
123#include <clang/Basic/Version.h>
124#if defined(__GNUC__)
125#pragma GCC diagnostic pop
126#endif
127#endif
128
129// provided by the generated file resources.cpp
130extern void initResources();
131
132#if !defined(_WIN32) || defined(__CYGWIN__)
133#include <signal.h>
134#define HAS_SIGNALS
135#endif
136
137// globally accessible variables
149FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
155FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
156FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
158StringMap Doxygen::tagDestinationMap; // all tag locations
159StringUnorderedSet Doxygen::tagFileSet; // all tag file names
160StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
161MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
162std::unique_ptr<PageDef> Doxygen::mainPage;
163std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
183std::mutex Doxygen::addExampleMutex;
185
186// locally accessible globals
187static std::multimap< std::string, const Entry* > g_classEntries;
189static OutputList *g_outputList = nullptr; // list of output generating objects
190static StringSet g_usingDeclarations; // used classes
191static bool g_successfulRun = FALSE;
192static bool g_dumpSymbolMap = FALSE;
194static bool g_singleComment=false;
195
196
197
198// keywords recognized as compounds
200{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
201
203{
204 g_inputFiles.clear();
205 //g_excludeNameDict.clear();
206 //delete g_outputList; g_outputList=nullptr;
207
212 Doxygen::pageLinkedMap->clear();
226 Doxygen::mainPage.reset();
228}
229
231{
232 public:
234 void begin(const char *name)
235 {
236 msg("{}", name);
237 stats.emplace_back(name,0);
238 startTime = std::chrono::steady_clock::now();
239 }
240 void end()
241 {
242 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
243 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
244 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
245 warn_flush();
246 }
247 void print()
248 {
249 bool restore=FALSE;
251 {
253 restore=TRUE;
254 }
255 msg("----------------------\n");
256 for (const auto &s : stats)
257 {
258 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
259 }
260 if (restore) Debug::setFlag(Debug::Time);
261 }
262 private:
263 struct stat
264 {
265 const char *name;
266 double elapsed;
267 //stat() : name(nullptr),elapsed(0) {}
268 stat(const char *n, double el) : name(n),elapsed(el) {}
269 };
270 std::vector<stat> stats;
271 std::chrono::steady_clock::time_point startTime;
273
274
275static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
276 const ArgumentList *al,bool over_load,TypeSpecifier spec);
277static void findMember(const Entry *root,
278 const QCString &relates,
279 const QCString &type,
280 const QCString &args,
281 QCString funcDecl,
282 bool overloaded,
283 bool isFunc
284 );
285
292
293
294static bool findClassRelation(
295 const Entry *root,
296 Definition *context,
297 ClassDefMutable *cd,
298 const BaseInfo *bi,
299 const TemplateNameMap &templateNames,
300 /*bool insertUndocumented*/
302 bool isArtificial
303 );
304
305//----------------------------------------------------------------------------
306
308 FileDef *fileScope,const TagInfo *tagInfo);
309static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
310
311static void addPageToContext(PageDef *pd,Entry *root)
312{
313 if (root->parent()) // add the page to it's scope
314 {
315 QCString scope = root->parent()->name;
316 if (root->parent()->section.isPackageDoc())
317 {
318 scope=substitute(scope,".","::");
319 }
320 scope = stripAnonymousNamespaceScope(scope);
321 scope+="::"+pd->name();
323 if (d)
324 {
325 pd->setPageScope(d);
326 }
327 }
328}
329
330static void addRelatedPage(Entry *root)
331{
332 GroupDef *gd=nullptr;
333 for (const Grouping &g : root->groups)
334 {
335 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
336 }
337 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
338 QCString doc=root->doc+root->inbodyDocs;
339
340 PageDef *pd = addRelatedPage(root->name, // name
341 root->args, // ptitle
342 doc, // doc
343 root->docFile, // fileName
344 root->docLine, // docLine
345 root->startLine, // startLine
346 root->sli, // sli
347 gd, // gd
348 root->tagInfo(), // tagInfo
349 FALSE, // xref
350 root->lang // lang
351 );
352 if (pd)
353 {
354 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
356 pd->setLocalToc(root->localToc);
357 addPageToContext(pd,root);
358 }
359}
360
361static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
362{
363 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
364 ((!includeExternal && root->tagInfo()==nullptr) ||
365 ( includeExternal && root->tagInfo()!=nullptr))
366 )
367 {
368 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
369 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
370 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
371 {
372 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
373 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
374
375 if (gd)
376 {
377 if ( !gd->hasGroupTitle() )
378 {
379 gd->setGroupTitle( root->type );
380 }
381 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
382 {
383 warn( root->fileName,root->startLine,
384 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
385 root->name, root->type, gd->groupTitle() );
386 }
387 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
388 gd->setDocumentation( root->doc, root->docFile, root->docLine );
389 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
391 gd->setRefItems(root->sli);
393 gd->setLanguage(root->lang);
395 {
396 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
397 }
398 }
399 else
400 {
401 if (root->tagInfo())
402 {
403 gd = Doxygen::groupLinkedMap->add(root->name,
404 std::unique_ptr<GroupDef>(
405 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
406 gd->setReference(root->tagInfo()->tagName);
407 }
408 else
409 {
410 gd = Doxygen::groupLinkedMap->add(root->name,
411 std::unique_ptr<GroupDef>(
412 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
413 }
414 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
415 // allow empty docs for group
416 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
417 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
419 gd->setRefItems(root->sli);
421 gd->setLanguage(root->lang);
423 {
424 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
425 }
426 }
427 }
428 }
429 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
430}
431
432static void buildGroupList(const Entry *root)
433{
434 // --- first process only local groups
435 // first process the @defgroups blocks
437 // then process the @addtogroup, @weakgroup blocks
439
440 // --- then also process external groups
441 // first process the @defgroups blocks
443 // then process the @addtogroup, @weakgroup blocks
445}
446
447static void findGroupScope(const Entry *root)
448{
449 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
450 root->parent() && !root->parent()->name.isEmpty())
451 {
452 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
453 if (gd)
454 {
455 QCString scope = root->parent()->name;
456 if (root->parent()->section.isPackageDoc())
457 {
458 scope=substitute(scope,".","::");
459 }
460 scope = stripAnonymousNamespaceScope(scope);
461 scope+="::"+gd->name();
463 if (d)
464 {
465 gd->setGroupScope(d);
466 }
467 }
468 }
469 for (const auto &e : root->children()) findGroupScope(e.get());
470}
471
472static void organizeSubGroupsFiltered(const Entry *root,bool additional)
473{
474 if (root->section.isGroupDoc() && !root->name.isEmpty())
475 {
476 AUTO_TRACE("additional={}",additional);
477 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
478 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
479 {
480 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
481 if (gd)
482 {
483 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
484 addGroupToGroups(root,gd);
485 }
486 }
487 }
488 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
489}
490
491static void organizeSubGroups(const Entry *root)
492{
493 //printf("Defining groups\n");
494 // first process the @defgroups blocks
496 //printf("Additional groups\n");
497 // then process the @addtogroup, @weakgroup blocks
499}
500
501//----------------------------------------------------------------------
502
503static void buildFileList(const Entry *root)
504{
505 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
506 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
507 )
508 {
509 bool ambig = false;
511 if (!fd || ambig)
512 {
513 bool save_ambig = ambig;
514 // use the directory of the file to see if the described file is in the same
515 // directory as the describing file.
516 QCString fn = root->fileName;
517 int newIndex=fn.findRev('/');
518 if (newIndex<0)
519 {
520 fn = root->name;
521 }
522 else
523 {
524 fn = fn.left(newIndex)+"/"+root->name;
525 }
527 if (!fd) ambig = save_ambig;
528 }
529 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
530 if (fd && !ambig)
531 {
532 //printf("Adding documentation!\n");
533 // using FALSE in setDocumentation is small hack to make sure a file
534 // is documented even if a \file command is used without further
535 // documentation
536 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
537 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
539 fd->setRefItems(root->sli);
541 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
542 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
543 for (const Grouping &g : root->groups)
544 {
545 GroupDef *gd=nullptr;
546 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
547 {
548 if (!gd->containsFile(fd))
549 {
550 gd->addFile(fd);
551 fd->makePartOfGroup(gd);
552 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
553 }
554 }
555 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
556 {
557 warn(root->fileName, root->startLine,
558 "Found non-existing group '{}' for the command '{}', ignoring command",
560 );
561 }
562 }
563 }
564 else
565 {
567 text.sprintf("the name '%s' supplied as "
568 "the argument in the \\file statement ",
569 qPrint(root->name));
570 if (ambig) // name is ambiguous
571 {
572 text+="matches the following input files:\n";
574 text+="\n";
575 text+="Please use a more specific name by "
576 "including a (larger) part of the path!";
577 }
578 else // name is not an input file
579 {
580 text+="is not an input file";
581 }
582 warn(root->fileName,root->startLine,"{}", text);
583 }
584 }
585 for (const auto &e : root->children()) buildFileList(e.get());
586}
587
588template<class DefMutable>
589static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
590{
591 if (
592 (!root->doc.stripWhiteSpace().isEmpty() ||
593 !root->brief.stripWhiteSpace().isEmpty() ||
594 Config_getBool(EXTRACT_ALL)
595 ) && root->protection!=Protection::Private
596 )
597 {
598 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
599
600 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
601 QCString includeFile = root->includeFile;
602 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
603 {
604 local = TRUE;
605 includeFile=includeFile.mid(1,includeFile.length()-2);
606 }
607 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
608 {
609 local = FALSE;
610 includeFile=includeFile.mid(1,includeFile.length()-2);
611 }
612
613 bool ambig = false;
614 FileDef *fd=nullptr;
615 // see if we need to include a verbatim copy of the header file
616 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
617 if (!includeFile.isEmpty() &&
618 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
619 )
620 { // explicit request
621 QCString text;
622 text.sprintf("the name '%s' supplied as "
623 "the argument of the \\class, \\struct, \\union, or \\include command ",
624 qPrint(includeFile)
625 );
626 if (ambig) // name is ambiguous
627 {
628 text+="matches the following input files:\n";
630 text+="\n";
631 text+="Please use a more specific name by "
632 "including a (larger) part of the path!";
633 }
634 else // name is not an input file
635 {
636 text+="is not an input file";
637 }
638 warn(root->fileName,root->startLine, "{}", text);
639 }
640 else if (includeFile.isEmpty() && ifd &&
641 // see if the file extension makes sense
642 guessSection(ifd->name()).isHeader())
643 { // implicit assumption
644 fd=ifd;
645 }
646
647 // if a file is found, we mark it as a source file.
648 if (fd)
649 {
650 QCString iName = !root->includeName.isEmpty() ?
651 root->includeName : includeFile;
652 if (!iName.isEmpty()) // user specified include file
653 {
654 if (iName.at(0)=='<') local=FALSE; // explicit override
655 else if (iName.at(0)=='"') local=TRUE;
656 if (iName.at(0)=='"' || iName.at(0)=='<')
657 {
658 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
659 }
660 if (iName.isEmpty())
661 {
662 iName=fd->name();
663 }
664 }
665 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
666 {
668 }
669 else // use name of the file containing the class definition
670 {
671 iName=fd->name();
672 }
673 if (fd->generateSourceFile()) // generate code for header
674 {
675 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
676 }
677 else // put #include in the class documentation without link
678 {
679 cd->setIncludeFile(nullptr,iName,local,TRUE);
680 }
681 }
682 }
683}
684
685
687{
688 size_t l = s.length();
689 int count=0;
690 int round=0;
691 QCString result;
692 for (size_t i=0;i<l;i++)
693 {
694 char c=s.at(i);
695 if (c=='(') round++;
696 else if (c==')' && round>0) round--;
697 else if (c=='<' && round==0) count++;
698 if (count==0)
699 {
700 result+=c;
701 }
702 if (c=='>' && round==0 && count>0) count--;
703 }
704 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
705 return result;
706}
707
708/*! returns the Definition object belonging to the first \a level levels of
709 * full qualified name \a name. Creates an artificial scope if the scope is
710 * not found and set the parent/child scope relation if the scope is found.
711 */
712[[maybe_unused]]
713static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
714{
715 QCString name = stripTemplateSpecifiers(name_);
716 name.stripPrefix("::");
717 int level = name.contains("::");
718 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
719 int i=0, p=0, l=0;
721 QCString fullScope;
722 while (i<level)
723 {
724 int idx=getScopeFragment(name,p,&l);
725 if (idx==-1) return prevScope;
726 QCString nsName = name.mid(idx,l);
727 if (nsName.isEmpty()) return prevScope;
728 if (!fullScope.isEmpty()) fullScope+="::";
729 fullScope+=nsName;
730 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
731 DefinitionMutable *innerScope = toDefinitionMutable(nd);
732 ClassDef *cd=nullptr;
733 if (nd==nullptr) cd = getClass(fullScope);
734 if (nd==nullptr && cd) // scope is a class
735 {
736 innerScope = toDefinitionMutable(cd);
737 }
738 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
739 {
740 // introduce bogus namespace
741 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
742 NamespaceDefMutable *newNd=
744 Doxygen::namespaceLinkedMap->add(fullScope,
746 "[generated]",1,1,fullScope,
747 tagInfo?tagInfo->tagName:QCString(),
748 tagInfo?tagInfo->fileName:QCString())));
749 if (newNd)
750 {
751 newNd->setLanguage(lang);
752 newNd->setArtificial(TRUE);
753 // add namespace to the list
754 innerScope = newNd;
755 }
756 }
757 else // scope is a namespace
758 {
759 }
760 if (innerScope)
761 {
762 // make the parent/child scope relation
763 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
764 if (prevScopeMutable)
765 {
766 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
767 }
768 innerScope->setOuterScope(prevScope);
769 }
770 else // current scope is a class, so return only the namespace part...
771 {
772 return prevScope;
773 }
774 // proceed to the next scope fragment
775 p=idx+l+2;
776 prevScope=toDefinition(innerScope);
777 i++;
778 }
779 return prevScope;
780}
781
783 FileDef *fileScope,const TagInfo *tagInfo)
784{
785 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
786 Definition *resultScope=toDefinition(startScope);
787 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
789 int l1 = 0;
790 int i1 = getScopeFragment(scope,0,&l1);
791 if (i1==-1)
792 {
793 //printf(">no fragments!\n");
794 return resultScope;
795 }
796 int p=i1+l1,l2=0,i2=0;
797 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
798 {
799 QCString nestedNameSpecifier = scope.mid(i1,l1);
800 Definition *orgScope = resultScope;
801 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
802 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
803 //printf(" resultScope=%p\n",resultScope);
804 if (resultScope==nullptr)
805 {
806 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
807 // also search for used namespaces
808 {
809 for (const auto &nd : fileScope->getUsedNamespaces())
810 {
812 if (mnd)
813 {
814 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
815 if (resultScope!=nullptr) break;
816 }
817 }
818 if (resultScope)
819 {
820 // for a nested class A::I in used namespace N, we get
821 // N::A::I while looking for A, so we should compare
822 // resultScope->name() against scope.left(i2+l2)
823 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
824 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
825 {
826 break;
827 }
828 goto nextFragment;
829 }
830 }
831
832 // also search for used classes. Complication: we haven't been able
833 // to put them in the right scope yet, because we are still resolving
834 // the scope relations!
835 // Therefore loop through all used classes and see if there is a right
836 // scope match between the used class and nestedNameSpecifier.
837 for (const auto &usedName : g_usingDeclarations)
838 {
839 //printf("Checking using class %s\n",qPrint(usedName));
840 if (rightScopeMatch(usedName,nestedNameSpecifier))
841 {
842 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
843 // so use this instead.
844 QCString fqn = usedName + scope.right(scope.length()-p);
845 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
846 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
847 if (resultScope)
848 {
849 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
850 return resultScope;
851 }
852 }
853 }
854
855 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
856 return nullptr;
857 }
858 nextFragment:
859 i1=i2;
860 l1=l2;
861 p=i2+l2;
862 }
863 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
864 return resultScope;
865}
866
867std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
868 const QCString &name,
869 const ArgumentLists &tArgLists)
870{
871 // for each scope fragment, check if it is a template and advance through
872 // the list if so.
873 int i=0, p=0;
874 auto alIt = tArgLists.begin();
875 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
876 {
877 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
878 if (nd==nullptr)
879 {
880 ClassDef *cd = getClass(name.left(i));
881 if (cd)
882 {
883 if (!cd->templateArguments().empty())
884 {
885 ++alIt;
886 }
887 }
888 }
889 p=i+2;
890 }
891 return alIt!=tArgLists.end() ?
892 std::make_unique<ArgumentList>(*alIt) :
893 std::unique_ptr<ArgumentList>();
894}
895
896static
898{
900
901 if (specifier.isStruct())
903 else if (specifier.isUnion())
904 sec=ClassDef::Union;
905 else if (specifier.isCategory())
907 else if (specifier.isInterface())
909 else if (specifier.isProtocol())
911 else if (specifier.isException())
913 else if (specifier.isService())
915 else if (specifier.isSingleton())
917
918 if (section.isUnionDoc())
919 sec=ClassDef::Union;
920 else if (section.isStructDoc())
922 else if (section.isInterfaceDoc())
924 else if (section.isProtocolDoc())
926 else if (section.isCategoryDoc())
928 else if (section.isExceptionDoc())
930 else if (section.isServiceDoc())
932 else if (section.isSingletonDoc())
934
935 return sec;
936}
937
938
939static void addClassToContext(const Entry *root)
940{
941 AUTO_TRACE("name={}",root->name);
942 FileDef *fd = root->fileDef();
943
944 QCString scName;
945 if (root->parent()->section.isScope())
946 {
947 scName=root->parent()->name;
948 }
949 // name without parent's scope
950 QCString fullName = root->name;
951
952 // strip off any template parameters (but not those for specializations)
953 int idx=fullName.find('>');
954 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
955 {
956 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
957 }
958 fullName=stripTemplateSpecifiersFromScope(fullName);
959
960 // name with scope (if not present already)
961 QCString qualifiedName = fullName;
962 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
963 {
964 qualifiedName.prepend(scName+"::");
965 }
966
967 // see if we already found the class before
968 ClassDefMutable *cd = getClassMutable(qualifiedName);
969
970 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
971
972 if (cd)
973 {
974 fullName=cd->name();
975 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
976 //if (cd->templateArguments()==0)
977 //{
978 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
979 // cd->setTemplateArguments(tArgList);
980 //}
981
982 cd->setDocumentation(root->doc,root->docFile,root->docLine);
983 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
984 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
985 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
986
987 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
988 {
989 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
990 if (root->bodyLine!=-1)
991 {
992 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
993 cd->setBodyDef(fd);
994 }
995 }
996
997 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
998 {
999 // this happens if a template class declared with @class is found
1000 // before the actual definition or if a forward declaration has different template
1001 // parameter names.
1002 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1003 if (tArgList)
1004 {
1005 cd->setTemplateArguments(*tArgList);
1006 }
1007 }
1008 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1009 {
1010 cd->setRequiresClause(root->req);
1011 }
1012
1014
1015 cd->setMetaData(root->metaData);
1016 }
1017 else // new class
1018 {
1020
1021 QCString className;
1022 QCString namespaceName;
1023 extractNamespaceName(fullName,className,namespaceName);
1024
1025 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1026 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1027
1028 QCString tagName;
1029 QCString refFileName;
1030 const TagInfo *tagInfo = root->tagInfo();
1031 if (tagInfo)
1032 {
1033 tagName = tagInfo->tagName;
1034 refFileName = tagInfo->fileName;
1035 if (fullName.find("::")!=-1)
1036 // symbols imported via tag files may come without the parent scope,
1037 // so we artificially create it here
1038 {
1039 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1040 }
1041 }
1042 std::unique_ptr<ArgumentList> tArgList;
1043 int i=0;
1044 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1045 (i=fullName.find('<'))!=-1)
1046 {
1047 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1048 // name and template arguments here
1049 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1050 if (i!=-1 && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1051 // A -> A
1052 // A<T> -> A-1-g
1053 // A<T,S> -> A-2-g
1054 {
1055 fullName=mangleCSharpGenericName(fullName);
1056 }
1057 else
1058 {
1059 fullName=fullName.left(i);
1060 }
1061 }
1062 else
1063 {
1064 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1065 }
1066 // add class to the list
1067 cd = toClassDefMutable(
1068 Doxygen::classLinkedMap->add(fullName,
1069 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1070 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1071 if (cd)
1072 {
1073 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1074 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1075 fmt::ptr(tagInfo),root->hidden,root->artificial);
1076 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1077 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1078 cd->setLanguage(root->lang);
1079 cd->setId(root->id);
1080 cd->setHidden(root->hidden);
1081 cd->setArtificial(root->artificial);
1082 cd->setClassSpecifier(root->spec);
1083 if (root->lang==SrcLangExt::CSharp && !root->args.isEmpty())
1084 {
1086 }
1087 cd->addQualifiers(root->qualifiers);
1088 cd->setTypeConstraints(root->typeConstr);
1089 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1090 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1091
1092 if (tArgList)
1093 {
1094 cd->setTemplateArguments(*tArgList);
1095 }
1096 cd->setRequiresClause(root->req);
1097 cd->setProtection(root->protection);
1098 cd->setIsStatic(root->isStatic);
1099
1100 // file definition containing the class cd
1101 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1102 cd->setBodyDef(fd);
1103
1104 cd->setMetaData(root->metaData);
1105
1106 cd->insertUsedFile(fd);
1107 }
1108 else
1109 {
1110 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1111 }
1112 }
1113
1114 if (cd)
1115 {
1117 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1118 if (!root->spec.isForwardDecl())
1119 {
1120 if (cd->hasDocumentation())
1121 {
1122 addIncludeFile(cd,fd,root);
1123 }
1124 if (fd && root->section.isCompound())
1125 {
1126 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1127 cd->setFileDef(fd);
1128 fd->insertClass(cd);
1129 }
1130 }
1131 addClassToGroups(root,cd);
1133 cd->setRefItems(root->sli);
1134 cd->setRequirementReferences(root->rqli);
1135 }
1136}
1137
1138//----------------------------------------------------------------------
1139// build a list of all classes mentioned in the documentation
1140// and all classes that have a documentation block before their definition.
1141static void buildClassList(const Entry *root)
1142{
1143 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1144 {
1145 AUTO_TRACE();
1146 addClassToContext(root);
1147 }
1148 for (const auto &e : root->children()) buildClassList(e.get());
1149}
1150
1151static void buildClassDocList(const Entry *root)
1152{
1153 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1154 {
1155 AUTO_TRACE();
1156 addClassToContext(root);
1157 }
1158 for (const auto &e : root->children()) buildClassDocList(e.get());
1159}
1160
1161//----------------------------------------------------------------------
1162// build a list of all classes mentioned in the documentation
1163// and all classes that have a documentation block before their definition.
1164
1165static void addConceptToContext(const Entry *root)
1166{
1167 AUTO_TRACE();
1168 FileDef *fd = root->fileDef();
1169
1170 QCString scName;
1171 if (root->parent()->section.isScope())
1172 {
1173 scName=root->parent()->name;
1174 }
1175
1176 // name with scope (if not present already)
1177 QCString qualifiedName = root->name;
1178 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1179 {
1180 qualifiedName.prepend(scName+"::");
1181 }
1182
1183 // see if we already found the concept before
1184 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1185
1186 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1187
1188 if (cd)
1189 {
1190 qualifiedName=cd->name();
1191 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1192
1193 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1194 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1195
1196 addIncludeFile(cd,fd,root);
1197 }
1198 else // new concept
1199 {
1200 QCString className;
1201 QCString namespaceName;
1202 extractNamespaceName(qualifiedName,className,namespaceName);
1203
1204 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1205 qualifiedName,namespaceName,className,root->brief,root->doc);
1206
1207 QCString tagName;
1208 QCString refFileName;
1209 const TagInfo *tagInfo = root->tagInfo();
1210 if (tagInfo)
1211 {
1212 tagName = tagInfo->tagName;
1213 refFileName = tagInfo->fileName;
1214 if (qualifiedName.find("::")!=-1)
1215 // symbols imported via tag files may come without the parent scope,
1216 // so we artificially create it here
1217 {
1218 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1219 }
1220 }
1221 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1222 // add concept to the list
1224 Doxygen::conceptLinkedMap->add(qualifiedName,
1225 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1226 qualifiedName,tagName,refFileName)));
1227 if (cd)
1228 {
1229 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1230 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1231 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1232 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1233 cd->setLanguage(root->lang);
1234 cd->setId(root->id);
1235 cd->setHidden(root->hidden);
1236 cd->setGroupId(root->mGrpId);
1237 if (tArgList)
1238 {
1239 cd->setTemplateArguments(*tArgList);
1240 }
1241 cd->setInitializer(root->initializer.str());
1242 // file definition containing the class cd
1243 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1244 cd->setBodyDef(fd);
1246 cd->setRefItems(root->sli);
1247 cd->setRequirementReferences(root->rqli);
1248 addIncludeFile(cd,fd,root);
1249
1250 // also add namespace to the correct structural context
1251 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1253 {
1255 if (dm)
1256 {
1257 dm->addInnerCompound(cd);
1258 }
1259 cd->setOuterScope(d);
1260 }
1261 for (const auto &ce : root->children())
1262 {
1263 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1264 if (ce->section.isConceptDocPart())
1265 {
1266 cd->addSectionsToDefinition(ce->anchors);
1267 cd->setRefItems(ce->sli);
1268 cd->setRequirementReferences(ce->rqli);
1269 if (!ce->brief.isEmpty())
1270 {
1271 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1272 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1273 }
1274 if (!ce->doc.isEmpty())
1275 {
1276 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1277 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1278 }
1279 }
1280 else if (ce->section.isConceptCodePart())
1281 {
1282 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1283 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1284 }
1285 }
1286 }
1287 else
1288 {
1289 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1290 }
1291 }
1292
1293 if (cd)
1294 {
1296 for (const auto &ce : root->children())
1297 {
1298 if (ce->section.isConceptDocPart())
1299 {
1300 cd->addSectionsToDefinition(ce->anchors);
1301 }
1302 }
1303 if (fd)
1304 {
1305 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1306 cd->setFileDef(fd);
1307 fd->insertConcept(cd);
1308 }
1309 addConceptToGroups(root,cd);
1311 cd->setRefItems(root->sli);
1312 cd->setRequirementReferences(root->rqli);
1313 }
1314}
1315
1316static void findModuleDocumentation(const Entry *root)
1317{
1318 if (root->section.isModuleDoc())
1319 {
1320 AUTO_TRACE();
1322 }
1323 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1324}
1325
1326static void buildConceptList(const Entry *root)
1327{
1328 if (root->section.isConcept())
1329 {
1330 AUTO_TRACE();
1331 addConceptToContext(root);
1332 }
1333 for (const auto &e : root->children()) buildConceptList(e.get());
1334}
1335
1336static void buildConceptDocList(const Entry *root)
1337{
1338 if (root->section.isConceptDoc())
1339 {
1340 AUTO_TRACE();
1341 addConceptToContext(root);
1342 }
1343 for (const auto &e : root->children()) buildConceptDocList(e.get());
1344}
1345
1346// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1347// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1349{
1350 AUTO_TRACE();
1351 for (const auto &cd : *Doxygen::conceptLinkedMap)
1352 {
1353 if (cd->groupId()!=DOX_NOGROUP)
1354 {
1355 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1356 {
1357 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1358 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1359 {
1360 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1361 if (ocdm)
1362 {
1363 for (const auto &gd : cd->partOfGroups())
1364 {
1365 if (gd)
1366 {
1367 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1368 ocdm->makePartOfGroup(gd);
1369 gd->addConcept(ocd.get());
1370 }
1371 }
1372 }
1373 }
1374 }
1375 }
1376 }
1377}
1378
1379//----------------------------------------------------------------------
1380
1382{
1383 ClassDefSet visitedClasses;
1384
1385 bool done=FALSE;
1386 //int iteration=0;
1387 while (!done)
1388 {
1389 done=TRUE;
1390 //++iteration;
1391 struct ClassAlias
1392 {
1393 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1394 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1395 QCString aliasFullName;
1396 std::unique_ptr<ClassDef> aliasCd;
1397 DefinitionMutable *aliasContext;
1398 };
1399 std::vector<ClassAlias> aliases;
1400 for (const auto &icd : *Doxygen::classLinkedMap)
1401 {
1402 ClassDefMutable *cd = toClassDefMutable(icd.get());
1403 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1404 {
1405 QCString name = stripAnonymousNamespaceScope(icd->name());
1406 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1407 // also add class to the correct structural context
1409 name,icd->getFileDef(),nullptr);
1410 if (d)
1411 {
1412 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1414 if (dm)
1415 {
1416 dm->addInnerCompound(cd);
1417 }
1418 cd->setOuterScope(d);
1419
1420 // for inline namespace add an alias of the class to the outer scope
1422 {
1424 //printf("nd->isInline()=%d\n",nd->isInline());
1425 if (nd && nd->isInline())
1426 {
1427 d = d->getOuterScope();
1428 if (d)
1429 {
1430 dm = toDefinitionMutable(d);
1431 if (dm)
1432 {
1433 auto aliasCd = createClassDefAlias(d,cd);
1434 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1435 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1436 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1437 }
1438 }
1439 }
1440 else
1441 {
1442 break;
1443 }
1444 }
1445
1446 visitedClasses.insert(icd.get());
1447 done=FALSE;
1448 }
1449 //else
1450 //{
1451 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1452 //}
1453 }
1454 }
1455 // add aliases
1456 for (auto &alias : aliases)
1457 {
1458 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1459 if (aliasCd)
1460 {
1461 alias.aliasContext->addInnerCompound(aliasCd);
1462 }
1463 }
1464 }
1465
1466 //give warnings for unresolved compounds
1467 for (const auto &icd : *Doxygen::classLinkedMap)
1468 {
1469 ClassDefMutable *cd = toClassDefMutable(icd.get());
1470 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1471 {
1473 /// create the scope artificially
1474 // anyway, so we can at least relate scopes properly.
1475 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1476 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1477 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1478 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1479 // also avoid warning for stuff imported via a tagfile.
1480 {
1482 if (dm)
1483 {
1484 dm->addInnerCompound(cd);
1485 }
1486 cd->setOuterScope(d);
1487 warn(cd->getDefFileName(),cd->getDefLine(),
1488 "Incomplete input: scope for class {} not found!{}",name,
1489 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1490 );
1491 }
1492 }
1493 }
1494}
1495
1497{
1498 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1499 //if (!inlineGroupedClasses) return;
1500 //printf("** distributeClassGroupRelations()\n");
1501
1502 ClassDefSet visitedClasses;
1503 for (const auto &cd : *Doxygen::classLinkedMap)
1504 {
1505 //printf("Checking %s\n",qPrint(cd->name()));
1506 // distribute the group to nested classes as well
1507 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1508 {
1509 //printf(" Candidate for merging\n");
1510 GroupDef *gd = cd->partOfGroups().front();
1511 for (auto &ncd : cd->getClasses())
1512 {
1514 if (ncdm && ncdm->partOfGroups().empty())
1515 {
1516 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1517 // gd->groupTitle());
1518 ncdm->makePartOfGroup(gd);
1519 gd->addClass(ncdm);
1520 }
1521 }
1522 visitedClasses.insert(cd.get()); // only visit every class once
1523 }
1524 }
1525}
1526
1527//----------------------------------------------------------------------
1528
1529template<typename Container>
1531 const Container *cd,
1532 const MemberDef *enumTypeMember,
1533 MemberListType mlFilter)
1534{
1535 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1536 {
1537 MemberList *eiml = cd->getMemberList(mlFilter);
1538 if (eiml)
1539 {
1540 for (const auto &eimd : *eiml)
1541 {
1542 QCString vtype = eimd->typeString();
1543 if (vtype.find(md->name())!=-1)
1544 {
1546 if (mimd)
1547 {
1548 mimd->setAnonymousEnumType(enumTypeMember);
1549 break;
1550 }
1551 }
1552 }
1553 }
1554 }
1555}
1556
1557static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const QCString &fieldName)
1558{
1559 QCString n = templ->name();
1560 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1561 int sn = n.find('@');
1562 if (sn!=-1)
1563 {
1564 const char *p = n.data()+sn;
1565 char c;
1566 while ((c=*p))
1567 {
1568 if (!isdigit(c) && c!='@' && c!=':') break;
1569 p++;
1570 }
1571 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1572 }
1573 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1574 QCString fullName = n+"."+fieldName;
1575
1576 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1578 Doxygen::classLinkedMap->add(fullName,
1580 templ->getDefLine(),
1581 templ->getDefColumn(),
1582 fullName,
1583 templ->compoundType())));
1584 if (cd)
1585 {
1586 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1587 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1588 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1589 cd->setLanguage(templ->getLanguage());
1590 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1591 cd->setBodyDef(templ->getBodyDef());
1592
1593 if (root!=Doxygen::globalScope)
1594 {
1595 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1596 if (root && root->definitionType()==Definition::TypeFile)
1597 {
1598 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1599 fd->insertClass(cd);
1600 cd->setFileDef(fd);
1602 }
1603 else if (outerScope)
1604 {
1605 outerScope->addInnerCompound(cd);
1606 cd->setOuterScope(const_cast<Definition*>(root));
1607 }
1608 }
1609
1610 for (auto &gd : root->partOfGroups())
1611 {
1612 cd->makePartOfGroup(gd);
1613 gd->addClass(cd);
1614 }
1615
1616 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1617 {
1618 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1619 md->typeString(),md->name(),md->argsString(),md->excpString(),
1620 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1621 md->memberType(),
1622 ArgumentList(),ArgumentList(),"");
1623 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1624 imd->setMemberClass(cd);
1625 imd->setDefinition(md->definition());
1626 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1627 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1628 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1629 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1630 imd->setId(md->id());
1631 imd->addQualifiers(md->getQualifiers());
1632 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1633 imd->setMemberGroupId(md->getMemberGroupId());
1634 imd->setInitializer(md->initializer());
1635 imd->setRequiresClause(md->requiresClause());
1636 imd->setMaxInitLines(md->initializerLines());
1637 imd->setBitfields(md->bitfieldString());
1638 imd->setLanguage(md->getLanguage());
1640 cd->insertMember(imd);
1641 associateVariableWithAnonymousEnumType(md,cd,imd,MemberListType::PubAttribs());
1642 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1643 mn->push_back(std::move(newMd));
1644 return imd;
1645
1646 };
1647
1648 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1649 if (ml)
1650 {
1651 for (const auto &md : *ml)
1652 {
1653 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1654 addMember(md);
1655 }
1656 }
1657 ml = templ->getMemberList(MemberListType::PubTypes());
1658 if (ml)
1659 {
1660 for (const auto &md : *ml)
1661 {
1662 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1663 MemberDefMutable *mdm = addMember(md);
1664 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1665 {
1666 for (const auto &emd : md->enumFieldList())
1667 {
1668 //printf(" enum field %s\n",qPrint(emd->name()));
1669 MemberDefMutable *emdm = addMember(emd);
1670 mdm->insertEnumField(emdm);
1671 emdm->setEnumScope(md);
1672 }
1673 }
1674 }
1675 }
1676 }
1677 return cd;
1678}
1679
1680/** Look through the members of class \a cd and its public members.
1681 * If there is a member m of a tag less struct/union,
1682 * then we create a duplicate of the struct/union with the name of the
1683 * member to identify it.
1684 * So if cd has name S, then the tag less struct/union will get name S.m
1685 * Since tag less structs can be nested we need to call this function
1686 * recursively. Later on we need to patch the member types so we keep
1687 * track of the hierarchy of classes we create.
1688 */
1689template<typename Container, typename TagContainer>
1690static void processTagLessClasses(const Definition *root,
1691 const Container *cd,
1692 const TagContainer *tagParent,
1693 MemberListType varFilter,
1694 MemberListType typeFilter,
1695 const QCString &prefix,int count)
1696{
1697 AUTO_TRACE("count={} name={}\n",count,cd->name());
1698 if (tagParent /*&& !cd->getClasses().empty()*/)
1699 {
1700 MemberList *ml = cd->getMemberList(varFilter);
1701 if (ml)
1702 {
1703 int pos=0;
1704 for (const auto &md : *ml)
1705 {
1706 QCString type = md->typeString();
1707 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1708 if ((cd->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // part namespace members only if cd is a namespace
1709 (type.find("::@")!=-1 || type.find(" @")!=-1)) // member of tag less struct/union
1710 {
1711 std::vector<const ClassDef *> candidates;
1712 for (const auto &icd : cd->getClasses())
1713 {
1714 candidates.push_back(icd);
1715 }
1716 for (const auto &icd : candidates)
1717 {
1718 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1719 if (type.find(icd->name())!=-1) // matching tag less struct/union
1720 {
1721 QCString name = md->name();
1722 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1723 if (!prefix.isEmpty()) name.prepend(prefix+".");
1724 //printf(" found %s in scope %s\n",qPrint(name),qPrint(cd->name()));
1725 ClassDefMutable *ncd = createTagLessInstance(root,icd,name);
1726 if (ncd)
1727 {
1728 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1729 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1730 ncd->setTagLessReference(icd);
1731
1732 // associate the variable of the anonymous type with the type member
1733 MemberList *pml = tagParent->getMemberList(varFilter);
1734 if (pml)
1735 {
1736 for (const auto &pmd : *pml)
1737 {
1739 if (pmdm && pmd->name()==md->name())
1740 {
1741 pmdm->setClassDefOfAnonymousType(ncd);
1742 }
1743 }
1744 }
1745 }
1746 }
1747 else
1748 {
1749 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1750 }
1751 }
1752 }
1753 }
1754 }
1755 // associate the variable of the anonymous enum type with the type member
1756 ml = cd->getMemberList(typeFilter);
1757 if (ml)
1758 {
1759 for (const auto &md : *ml)
1760 {
1761 MemberListType mlFilter = cd->definitionType()==Definition::TypeClass ? MemberListType::PubAttribs() : MemberListType::DecVarMembers();
1762 associateVariableWithAnonymousEnumType(md,cd,md,mlFilter);
1763 }
1764 }
1765 }
1766}
1767
1768template<typename Container>
1769static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1770{
1771 for (const auto &icd : cd->getClasses())
1772 {
1773 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1774 {
1775 findTagLessClasses(candidates,icd);
1776 }
1777 }
1778
1779 candidates.insert(cd);
1780}
1781
1783{
1784 std::set<const Definition *> candidates;
1785 for (auto &cd : *Doxygen::classLinkedMap)
1786 {
1787 Definition *scope = cd->getOuterScope();
1788 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1789 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1790 {
1791 const NamespaceDef *nd = toNamespaceDef(scope);
1792 if (nd && nd==Doxygen::globalScope) // class at global namespace
1793 {
1794 const FileDef *fd = cd->getFileDef();
1795 if (fd)
1796 {
1797 findTagLessClasses(candidates,fd);
1798 }
1799 }
1800 else if (nd) // class in a namespace
1801 {
1802 findTagLessClasses(candidates,nd);
1803 }
1804 }
1805 }
1806
1807 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1808 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1809 for (const auto &d : candidates)
1810 {
1811 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1812 if (d->definitionType()==Definition::TypeNamespace)
1813 {
1814 const NamespaceDef *nd = toNamespaceDef(d);
1815 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1816 }
1817 else if (d->definitionType()==Definition::TypeFile)
1818 {
1819 const FileDef *fd = toFileDef(d);
1820 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1821 }
1822 else if (d->definitionType()==Definition::TypeClass)
1823 {
1824 const ClassDef *cd = toClassDef(d);
1825 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1826 }
1827 }
1828}
1829
1830
1831//----------------------------------------------------------------------
1832// build a list of all namespaces mentioned in the documentation
1833// and all namespaces that have a documentation block before their definition.
1834static void buildNamespaceList(const Entry *root)
1835{
1836 if (
1837 (root->section.isNamespace() ||
1838 root->section.isNamespaceDoc() ||
1839 root->section.isPackageDoc()
1840 ) &&
1841 !root->name.isEmpty()
1842 )
1843 {
1844 AUTO_TRACE("name={}",root->name);
1845
1846 QCString fName = root->name;
1847 if (root->section.isPackageDoc())
1848 {
1849 fName=substitute(fName,".","::");
1850 }
1851
1852 QCString fullName = stripAnonymousNamespaceScope(fName);
1853 if (!fullName.isEmpty())
1854 {
1855 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1856 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1857 if (ndi) // existing namespace
1858 {
1860 if (nd) // non-inline namespace
1861 {
1862 AUTO_TRACE_ADD("Existing namespace");
1863 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1864 nd->setName(fullName); // change name to match docs
1866 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1867 if (nd->getLanguage()==SrcLangExt::Unknown)
1868 {
1869 nd->setLanguage(root->lang);
1870 }
1871 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1872 // if we previously found namespace nd in a tag file and now we find a
1873 // documented namespace with the same name in the project, then remove
1874 // the tag file reference
1875 {
1876 nd->setReference("");
1877 nd->setFileName(fullName);
1878 }
1879 nd->setMetaData(root->metaData);
1880
1881 // file definition containing the namespace nd
1882 FileDef *fd=root->fileDef();
1883 if (nd->isArtificial())
1884 {
1885 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1886 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1887 }
1888 // insert the namespace in the file definition
1889 if (fd) fd->insertNamespace(nd);
1890 addNamespaceToGroups(root,nd);
1891 nd->setRefItems(root->sli);
1892 nd->setRequirementReferences(root->rqli);
1893 }
1894 }
1895 else // fresh namespace
1896 {
1897 QCString tagName;
1898 QCString tagFileName;
1899 const TagInfo *tagInfo = root->tagInfo();
1900 if (tagInfo)
1901 {
1902 tagName = tagInfo->tagName;
1903 tagFileName = tagInfo->fileName;
1904 }
1905 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1906 // add namespace to the list
1908 Doxygen::namespaceLinkedMap->add(fullName,
1909 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1910 root->startColumn,fullName,tagName,tagFileName,
1911 root->type,root->spec.isPublished())));
1912 if (nd)
1913 {
1914 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1915 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1917 nd->setHidden(root->hidden);
1918 nd->setArtificial(root->artificial);
1919 nd->setLanguage(root->lang);
1920 nd->setId(root->id);
1921 nd->setMetaData(root->metaData);
1922 nd->setInline(root->spec.isInline());
1923 nd->setExported(root->exported);
1924
1925 addNamespaceToGroups(root,nd);
1926 nd->setRefItems(root->sli);
1927 nd->setRequirementReferences(root->rqli);
1928
1929 // file definition containing the namespace nd
1930 FileDef *fd=root->fileDef();
1931 // insert the namespace in the file definition
1932 if (fd) fd->insertNamespace(nd);
1933
1934 // the empty string test is needed for extract all case
1935 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1936 nd->insertUsedFile(fd);
1937 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1938 nd->setBodyDef(fd);
1939
1940 // also add namespace to the correct structural context
1941 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1942 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1943 if (d==nullptr) // we didn't find anything, create the scope artificially
1944 // anyway, so we can at least relate scopes properly.
1945 {
1946 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1948 if (dm)
1949 {
1950 dm->addInnerCompound(nd);
1951 }
1952 nd->setOuterScope(d);
1953 // TODO: Due to the order in which the tag file is written
1954 // a nested class can be found before its parent!
1955 }
1956 else
1957 {
1959 if (dm)
1960 {
1961 dm->addInnerCompound(nd);
1962 }
1963 nd->setOuterScope(d);
1964 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1966 {
1967 NamespaceDef *pnd = toNamespaceDef(d);
1968 if (pnd && pnd->isInline())
1969 {
1970 d = d->getOuterScope();
1971 if (d)
1972 {
1973 dm = toDefinitionMutable(d);
1974 if (dm)
1975 {
1976 auto aliasNd = createNamespaceDefAlias(d,nd);
1977 dm->addInnerCompound(aliasNd.get());
1978 QCString aliasName = aliasNd->name();
1979 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1980 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1981 }
1982 }
1983 else
1984 {
1985 break;
1986 }
1987 }
1988 else
1989 {
1990 break;
1991 }
1992 }
1993 }
1994 }
1995 }
1996 }
1997 }
1998 for (const auto &e : root->children()) buildNamespaceList(e.get());
1999}
2000
2001//----------------------------------------------------------------------
2002
2004 const QCString &name)
2005{
2006 NamespaceDef *usingNd =nullptr;
2007 for (auto &und : unl)
2008 {
2009 QCString uScope=und->name()+"::";
2010 usingNd = getResolvedNamespace(uScope+name);
2011 if (usingNd!=nullptr) break;
2012 }
2013 return usingNd;
2014}
2015
2016static void findUsingDirectives(const Entry *root)
2017{
2018 if (root->section.isUsingDir())
2019 {
2020 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2021 QCString name=substitute(root->name,".","::");
2022 if (name.endsWith("::"))
2023 {
2024 name=name.left(name.length()-2);
2025 }
2026 if (!name.isEmpty())
2027 {
2028 NamespaceDef *usingNd = nullptr;
2029 NamespaceDefMutable *nd = nullptr;
2030 FileDef *fd = root->fileDef();
2031 QCString nsName;
2032
2033 // see if the using statement was found inside a namespace or inside
2034 // the global file scope.
2035 if (root->parent() && root->parent()->section.isNamespace() &&
2036 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2037 )
2038 {
2039 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2040 if (!nsName.isEmpty())
2041 {
2042 nd = getResolvedNamespaceMutable(nsName);
2043 }
2044 }
2045
2046 // find the scope in which the 'using' namespace is defined by prepending
2047 // the possible scopes in which the using statement was found, starting
2048 // with the most inner scope and going to the most outer scope (i.e.
2049 // file scope).
2050 int scopeOffset = static_cast<int>(nsName.length());
2051 do
2052 {
2053 QCString scope=scopeOffset>0 ?
2054 nsName.left(scopeOffset)+"::" : QCString();
2055 usingNd = getResolvedNamespace(scope+name);
2056 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2057 if (scopeOffset==0)
2058 {
2059 scopeOffset=-1;
2060 }
2061 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
2062 {
2063 scopeOffset=0;
2064 }
2065 } while (scopeOffset>=0 && usingNd==nullptr);
2066
2067 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2068 // or in one of the parent namespace scopes
2069 {
2070 const NamespaceDefMutable *pnd = nd;
2071 while (pnd && usingNd==nullptr)
2072 {
2073 // also try with one of the used namespaces found earlier
2075
2076 // goto the parent
2077 Definition *s = pnd->getOuterScope();
2079 {
2081 }
2082 else
2083 {
2084 pnd = nullptr;
2085 }
2086 }
2087 }
2088 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2089 // global scope
2090 {
2091 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2092 }
2093
2094 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2095
2096 // add the namespace the correct scope
2097 if (usingNd)
2098 {
2099 //printf("using fd=%p nd=%p\n",fd,nd);
2100 if (nd)
2101 {
2102 //printf("Inside namespace %s\n",qPrint(nd->name()));
2103 nd->addUsingDirective(usingNd);
2104 }
2105 else if (fd)
2106 {
2107 //printf("Inside file %s\n",qPrint(fd->name()));
2108 fd->addUsingDirective(usingNd);
2109 }
2110 }
2111 else // unknown namespace, but add it anyway.
2112 {
2113 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
2114 // add namespace to the list
2117 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2118 if (nd)
2119 {
2120 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2121 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2123 nd->setHidden(root->hidden);
2124 nd->setArtificial(TRUE);
2125 nd->setLanguage(root->lang);
2126 nd->setId(root->id);
2127 nd->setMetaData(root->metaData);
2128 nd->setInline(root->spec.isInline());
2129 nd->setExported(root->exported);
2130
2131 for (const Grouping &g : root->groups)
2132 {
2133 GroupDef *gd=nullptr;
2134 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
2135 gd->addNamespace(nd);
2136 }
2137
2138 // insert the namespace in the file definition
2139 if (fd)
2140 {
2141 fd->insertNamespace(nd);
2142 fd->addUsingDirective(nd);
2143 }
2144
2145 // the empty string test is needed for extract all case
2146 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2147 nd->insertUsedFile(fd);
2148 nd->setRefItems(root->sli);
2149 nd->setRequirementReferences(root->rqli);
2150 }
2151 }
2152 }
2153 }
2154 for (const auto &e : root->children()) findUsingDirectives(e.get());
2155}
2156
2157//----------------------------------------------------------------------
2158
2159static void buildListOfUsingDecls(const Entry *root)
2160{
2161 if (root->section.isUsingDecl() &&
2162 !root->parent()->section.isCompound() // not a class/struct member
2163 )
2164 {
2165 QCString name = substitute(root->name,".","::");
2166 g_usingDeclarations.insert(name.str());
2167 }
2168 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2169}
2170
2171
2172static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2173{
2174 if (root->section.isUsingDecl() &&
2175 !root->parent()->section.isCompound() && // not a class/struct member
2176 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2177 )
2178 {
2179 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2180 root->name,root->startLine,root->fileName,root->parent()->section);
2181 if (!root->name.isEmpty())
2182 {
2183 const Definition *usingDef = nullptr;
2184 NamespaceDefMutable *nd = nullptr;
2185 FileDef *fd = root->fileDef();
2186 QCString scName;
2187
2188 // see if the using statement was found inside a namespace or inside
2189 // the global file scope.
2190 if (root->parent()->section.isNamespace())
2191 {
2192 scName=root->parent()->name;
2193 if (!scName.isEmpty())
2194 {
2195 nd = getResolvedNamespaceMutable(scName);
2196 }
2197 }
2198
2199 // Assume the using statement was used to import a class.
2200 // Find the scope in which the 'using' namespace is defined by prepending
2201 // the possible scopes in which the using statement was found, starting
2202 // with the most inner scope and going to the most outer scope (i.e.
2203 // file scope).
2204
2205 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2206
2207 SymbolResolver resolver;
2208 const Definition *scope = nd;
2209 if (nd==nullptr) scope = fd;
2210 usingDef = resolver.resolveSymbol(scope,name);
2211
2212 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2213
2214 if (!usingDef)
2215 {
2216 usingDef = getClass(name); // try direct lookup, this is needed to get
2217 // builtin STL classes to properly resolve, e.g.
2218 // vector -> std::vector
2219 }
2220 if (!usingDef)
2221 {
2222 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2223 }
2224#if 0
2225 if (!usingDef)
2226 {
2227 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2228 name,root->section,root->tArgLists.size());
2231 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2232 if (usingCd)
2233 {
2234 usingCd->setArtificial(TRUE);
2235 usingCd->setLanguage(root->lang);
2236 usingDef = usingCd;
2237 }
2238 }
2239#endif
2240 else
2241 {
2242 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2243 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2244 }
2245
2246 if (usingDef)
2247 {
2248 if (nd)
2249 {
2250 nd->addUsingDeclaration(usingDef);
2251 }
2252 else if (fd)
2253 {
2254 fd->addUsingDeclaration(usingDef);
2255 }
2256 }
2257 }
2258 }
2259 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2260}
2261
2262//----------------------------------------------------------------------
2263
2265{
2266 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2267 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2268 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2269 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2270 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2271 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2272}
2273
2274//----------------------------------------------------------------------
2275
2277 const QCString &fileName,const QCString &memName)
2278{
2279 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2280 const ArgumentList &templAl = md->templateArguments();
2281 const ArgumentList &al = md->argumentList();
2282 auto newMd = createMemberDef(
2283 fileName,root->startLine,root->startColumn,
2284 md->typeString(),memName,md->argsString(),
2285 md->excpString(),root->protection,root->virt,
2286 md->isStatic(),Relationship::Member,md->memberType(),
2287 templAl,al,root->metaData
2288 );
2289 auto newMmd = toMemberDefMutable(newMd.get());
2290 newMmd->setMemberClass(cd);
2291 cd->insertMember(newMd.get());
2292 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2293 {
2294 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2295 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2296 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2297 }
2298 else
2299 {
2300 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2301 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2302 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2303 }
2304 newMmd->setDefinition(md->definition());
2305 applyMemberOverrideOptions(root,newMmd);
2306 newMmd->addQualifiers(root->qualifiers);
2307 newMmd->setBitfields(md->bitfieldString());
2308 newMmd->addSectionsToDefinition(root->anchors);
2309 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2310 newMmd->setBodyDef(md->getBodyDef());
2311 newMmd->setInitializer(md->initializer());
2312 newMmd->setRequiresClause(md->requiresClause());
2313 newMmd->setMaxInitLines(md->initializerLines());
2314 newMmd->setMemberGroupId(root->mGrpId);
2315 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2316 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2317 newMmd->setLanguage(root->lang);
2318 newMmd->setId(root->id);
2319 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2320 mn->push_back(std::move(newMd));
2321}
2322
2323static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2324
2325static void findUsingDeclImports(const Entry *root)
2326{
2327 if (root->section.isUsingDecl() &&
2328 root->parent()->section.isCompound() // in a class/struct member
2329 )
2330 {
2331 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2332 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2333 fullName=stripAnonymousNamespaceScope(fullName);
2334 fullName=stripTemplateSpecifiersFromScope(fullName);
2335 ClassDefMutable *cd = getClassMutable(fullName);
2336 if (cd)
2337 {
2338 AUTO_TRACE_ADD("found class '{}'",cd->name());
2339 int i=root->name.findRev("::");
2340 if (i!=-1)
2341 {
2342 QCString scope=root->name.left(i);
2343 QCString memName=root->name.right(root->name.length()-i-2);
2344 SymbolResolver resolver;
2345 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2346 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2347 if (bcd && bcd!=cd)
2348 {
2349 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2351 const MemberNameInfo *mni = mnlm.find(memName);
2352 if (mni)
2353 {
2354 for (auto &mi : *mni)
2355 {
2356 const MemberDef *md = mi->memberDef();
2357 if (md && md->protection()!=Protection::Private)
2358 {
2359 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2360 QCString fileName = root->fileName;
2361 if (fileName.isEmpty() && root->tagInfo())
2362 {
2363 fileName = root->tagInfo()->tagName;
2364 }
2365 if (!cd->containsOverload(md))
2366 {
2367 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2368 // also insert the member into copies of the class
2369 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2370 if (it != g_usingClassMap.end())
2371 {
2372 for (const auto &copyCd : it->second)
2373 {
2374 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381 }
2382 }
2383 }
2384 }
2385 else if (root->section.isUsingDecl() &&
2386 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2387 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2388 )
2389 {
2390 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2391 Definition *scope = nullptr;
2392 NamespaceDefMutable *nd = nullptr;
2393 FileDef *fd = root->parent()->fileDef();
2394 if (!root->parent()->name.isEmpty())
2395 {
2396 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2397 fullName=stripAnonymousNamespaceScope(fullName);
2399 scope = nd;
2400 }
2401 else
2402 {
2403 scope = fd;
2404 }
2405 if (scope)
2406 {
2407 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2408 SymbolResolver resolver;
2409 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2410 if (def && def->definitionType()==Definition::TypeMember)
2411 {
2412 int i=root->name.findRev("::");
2413 QCString memName;
2414 if (i!=-1)
2415 {
2416 memName = root->name.right(root->name.length()-i-2);
2417 }
2418 else
2419 {
2420 memName = root->name;
2421 }
2422 const MemberDef *md = toMemberDef(def);
2423 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2424 QCString fileName = root->fileName;
2425 if (fileName.isEmpty() && root->tagInfo())
2426 {
2427 fileName = root->tagInfo()->tagName;
2428 }
2429 const ArgumentList &templAl = md->templateArguments();
2430 const ArgumentList &al = md->argumentList();
2431
2432 auto newMd = createMemberDef(
2433 fileName,root->startLine,root->startColumn,
2434 md->typeString(),memName,md->argsString(),
2435 md->excpString(),root->protection,root->virt,
2436 md->isStatic(),Relationship::Member,md->memberType(),
2437 templAl,al,root->metaData
2438 );
2439 auto newMmd = toMemberDefMutable(newMd.get());
2440 if (nd)
2441 {
2442 newMmd->setNamespace(nd);
2443 nd->insertMember(newMd.get());
2444 }
2445 if (fd)
2446 {
2447 newMmd->setFileDef(fd);
2448 fd->insertMember(newMd.get());
2449 }
2450 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2451 {
2452 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2453 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2454 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2455 }
2456 else
2457 {
2458 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2459 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2460 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2461 }
2462 newMmd->setDefinition(md->definition());
2463 applyMemberOverrideOptions(root,newMmd);
2464 newMmd->addQualifiers(root->qualifiers);
2465 newMmd->setBitfields(md->bitfieldString());
2466 newMmd->addSectionsToDefinition(root->anchors);
2467 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2468 newMmd->setBodyDef(md->getBodyDef());
2469 newMmd->setInitializer(md->initializer());
2470 newMmd->setRequiresClause(md->requiresClause());
2471 newMmd->setMaxInitLines(md->initializerLines());
2472 newMmd->setMemberGroupId(root->mGrpId);
2473 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2474 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2475 newMmd->setLanguage(root->lang);
2476 newMmd->setId(root->id);
2477 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2478 mn->push_back(std::move(newMd));
2479#if 0 // insert an alias instead of a copy
2480 const MemberDef *md = toMemberDef(def);
2481 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2482 auto aliasMd = createMemberDefAlias(nd,md);
2483 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2484 if (nd && aliasMd.get())
2485 {
2486 nd->insertMember(aliasMd.get());
2487 }
2488 if (fd && aliasMd.get())
2489 {
2490 fd->insertMember(aliasMd.get());
2491 }
2492 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2493 mn->push_back(std::move(aliasMd));
2494#endif
2495 }
2496 else if (def && def->definitionType()==Definition::TypeClass)
2497 {
2498 const ClassDef *cd = toClassDef(def);
2499 QCString copyFullName;
2500 if (nd==nullptr)
2501 {
2502 copyFullName = cd->localName();
2503 }
2504 else
2505 {
2506 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2507 }
2508 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2509 {
2511 Doxygen::classLinkedMap->add(copyFullName,
2512 cd->deepCopy(copyFullName)));
2513 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2514 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2515 if (ncdm)
2516 {
2517 if (nd) ncdm->moveTo(nd);
2518 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2519 {
2520 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2521 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2522 }
2523 else // use docs from used class
2524 {
2525 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2527 }
2528 if (nd)
2529 {
2530 nd->addInnerCompound(ncdm);
2531 nd->addUsingDeclaration(ncdm);
2532 }
2533 if (fd)
2534 {
2535 if (ncdm) ncdm->setFileDef(fd);
2536 fd->insertClass(ncdm);
2537 fd->addUsingDeclaration(ncdm);
2538 }
2539 }
2540 }
2541#if 0 // insert an alias instead of a copy
2542 auto aliasCd = createClassDefAlias(nd,cd);
2543 QCString aliasFullName;
2544 if (nd==nullptr)
2545 {
2546 aliasFullName = aliasCd->localName();
2547 }
2548 else
2549 {
2550 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2551 }
2552 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2553 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2554 if (nd && acd)
2555 {
2556 nd->addInnerCompound(acd);
2557 }
2558 if (fd && acd)
2559 {
2560 fd->insertClass(acd);
2561 }
2562#endif
2563 }
2564 else if (scope)
2565 {
2566 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2567 }
2568 }
2569 }
2570 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2571}
2572
2573//----------------------------------------------------------------------
2574
2576{
2577 FileDefSet visitedFiles;
2578 // then recursively add using directives found in #include files
2579 // to files that have not been visited.
2580 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2581 {
2582 for (const auto &fd : *fn)
2583 {
2584 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2585 fd->addIncludedUsingDirectives(visitedFiles);
2586 }
2587 }
2588}
2589
2590//----------------------------------------------------------------------
2591
2593 const Entry *root,
2594 ClassDefMutable *cd,
2595 MemberType mtype,
2596 const QCString &type,
2597 const QCString &name,
2598 const QCString &args,
2599 bool fromAnnScope,
2600 MemberDef *fromAnnMemb,
2601 Protection prot,
2602 Relationship related)
2603{
2605 QCString scopeSeparator="::";
2606 SrcLangExt lang = cd->getLanguage();
2607 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2608 {
2609 qualScope = substitute(qualScope,"::",".");
2610 scopeSeparator=".";
2611 }
2612 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2613 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2614
2615 QCString def;
2616 if (!type.isEmpty())
2617 {
2618 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2619 {
2620 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2621 {
2622 if (lang==SrcLangExt::Python)
2623 {
2624 def="type "+name+args;
2625 }
2626 else
2627 {
2628 def="using "+name;
2629 }
2630 }
2631 else
2632 {
2633 def=type+" "+name+args;
2634 }
2635 }
2636 else
2637 {
2638 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2639 {
2640 if (lang==SrcLangExt::Python)
2641 {
2642 def="type "+qualScope+scopeSeparator+name+args;
2643 }
2644 else
2645 {
2646 def="using "+qualScope+scopeSeparator+name;
2647 }
2648 }
2649 else
2650 {
2651 def=type+" "+qualScope+scopeSeparator+name+args;
2652 }
2653 }
2654 }
2655 else
2656 {
2657 if (Config_getBool(HIDE_SCOPE_NAMES))
2658 {
2659 def=name+args;
2660 }
2661 else
2662 {
2663 def=qualScope+scopeSeparator+name+args;
2664 }
2665 }
2666 def.stripPrefix("static ");
2667
2668 // see if the member is already found in the same scope
2669 // (this may be the case for a static member that is initialized
2670 // outside the class)
2672 if (mn)
2673 {
2674 for (const auto &imd : *mn)
2675 {
2676 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2677 // md->getClassDef(),cd,qPrint(type),md->typeString());
2678 MemberDefMutable *md = toMemberDefMutable(imd.get());
2679 if (md &&
2680 md->getClassDef()==cd &&
2681 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2683 // member already in the scope
2684 {
2685
2686 if (root->lang==SrcLangExt::ObjC &&
2687 root->mtype==MethodTypes::Property &&
2688 md->memberType()==MemberType::Variable)
2689 { // Objective-C 2.0 property
2690 // turn variable into a property
2691 md->setProtection(root->protection);
2692 cd->reclassifyMember(md,MemberType::Property);
2693 }
2694 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2695 AUTO_TRACE_ADD("Member already found!");
2696 return md;
2697 }
2698 }
2699 }
2700
2701 QCString fileName = root->fileName;
2702 if (fileName.isEmpty() && root->tagInfo())
2703 {
2704 fileName = root->tagInfo()->tagName;
2705 }
2706
2707 // new member variable, typedef or enum value
2708 auto md = createMemberDef(
2709 fileName,root->startLine,root->startColumn,
2710 type,name,args,root->exception,
2711 prot,Specifier::Normal,root->isStatic,related,
2712 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2713 ArgumentList(), root->metaData);
2714 auto mmd = toMemberDefMutable(md.get());
2715 mmd->setTagInfo(root->tagInfo());
2716 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2717 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2718 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2719 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2720 mmd->setDefinition(def);
2721 mmd->setBitfields(root->bitfields);
2722 mmd->addSectionsToDefinition(root->anchors);
2723 mmd->setFromAnonymousScope(fromAnnScope);
2724 mmd->setFromAnonymousMember(fromAnnMemb);
2725 if (fromAnnMemb)
2726 {
2727 MemberDefMutable *fromAnnMmd = toMemberDefMutable(fromAnnMemb);
2728 if (fromAnnMmd)
2729 {
2730 fromAnnMmd->setToAnonymousMember(mmd);
2731 }
2732 }
2733 //md->setIndentDepth(indentDepth);
2734 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2735 mmd->setInitializer(root->initializer.str());
2736 mmd->setMaxInitLines(root->initLines);
2737 mmd->setMemberGroupId(root->mGrpId);
2738 mmd->setMemberSpecifiers(root->spec);
2739 mmd->setVhdlSpecifiers(root->vhdlSpec);
2740 mmd->setReadAccessor(root->read);
2741 mmd->setWriteAccessor(root->write);
2743 mmd->setHidden(root->hidden);
2744 mmd->setArtificial(root->artificial);
2745 mmd->setLanguage(root->lang);
2746 mmd->setId(root->id);
2747 addMemberToGroups(root,md.get());
2749 mmd->setBodyDef(root->fileDef());
2750 mmd->addQualifiers(root->qualifiers);
2751
2752 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2753 cd->insertMember(md.get());
2754 mmd->setRefItems(root->sli);
2755 mmd->setRequirementReferences(root->rqli);
2756
2757 cd->insertUsedFile(root->fileDef());
2758 root->markAsProcessed();
2759
2760 if (mtype==MemberType::Typedef)
2761 {
2762 resolveTemplateInstanceInType(root,cd,md.get());
2763 }
2764
2765 // add the member to the global list
2766 MemberDef *result = md.get();
2767 mn = Doxygen::memberNameLinkedMap->add(name);
2768 mn->push_back(std::move(md));
2769
2770 return result;
2771}
2772
2773//----------------------------------------------------------------------
2774
2776 const Entry *root,
2777 MemberType mtype,
2778 const QCString &scope,
2779 const QCString &type,
2780 const QCString &name,
2781 const QCString &args,
2782 bool fromAnnScope,
2783 MemberDef *fromAnnMemb)
2784{
2785 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2786 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2787
2788 FileDef *fd = root->fileDef();
2789
2790 // see if we have a typedef that should hide a struct or union
2791 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2792 {
2793 QCString ttype = type;
2794 ttype.stripPrefix("typedef ");
2795 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2796 {
2797 static const reg::Ex re(R"(\a\w*)");
2798 reg::Match match;
2799 const std::string &typ = ttype.str();
2800 if (reg::search(typ,match,re))
2801 {
2802 QCString typeValue = match.str();
2803 ClassDefMutable *cd = getClassMutable(typeValue);
2804 if (cd)
2805 {
2806 // this typedef should hide compound name cd, so we
2807 // change the name that is displayed from cd.
2808 cd->setClassName(name);
2809 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2810 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2811 return nullptr;
2812 }
2813 }
2814 }
2815 }
2816
2817 // see if the function is inside a namespace
2818 NamespaceDefMutable *nd = nullptr;
2819 if (!scope.isEmpty())
2820 {
2821 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2822 nd = getResolvedNamespaceMutable(scope);
2823 }
2824 QCString def;
2825
2826 // determine the definition of the global variable
2827 if (nd && !nd->isAnonymous() &&
2828 !Config_getBool(HIDE_SCOPE_NAMES)
2829 )
2830 // variable is inside a namespace, so put the scope before the name
2831 {
2832 SrcLangExt lang = nd->getLanguage();
2834
2835 if (!type.isEmpty())
2836 {
2837 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2838 {
2839 if (lang==SrcLangExt::Python)
2840 {
2841 def="type "+nd->name()+sep+name+args;
2842 }
2843 else
2844 {
2845 def="using "+nd->name()+sep+name;
2846 }
2847 }
2848 else // normal member
2849 {
2850 def=type+" "+nd->name()+sep+name+args;
2851 }
2852 }
2853 else
2854 {
2855 def=nd->name()+sep+name+args;
2856 }
2857 }
2858 else
2859 {
2860 if (!type.isEmpty() && !root->name.isEmpty())
2861 {
2862 if (name.at(0)=='@') // dummy variable representing anonymous union
2863 {
2864 def=type;
2865 }
2866 else
2867 {
2868 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2869 {
2870 if (root->lang==SrcLangExt::Python)
2871 {
2872 def="type "+root->name+args;
2873 }
2874 else
2875 {
2876 def="using "+root->name;
2877 }
2878 }
2879 else // normal member
2880 {
2881 def=type+" "+name+args;
2882 }
2883 }
2884 }
2885 else
2886 {
2887 def=name+args;
2888 }
2889 }
2890 def.stripPrefix("static ");
2891
2893 if (mn)
2894 {
2895 //QCString nscope=removeAnonymousScopes(scope);
2896 //NamespaceDef *nd=nullptr;
2897 //if (!nscope.isEmpty())
2898 if (!scope.isEmpty())
2899 {
2900 nd = getResolvedNamespaceMutable(scope);
2901 }
2902 for (const auto &imd : *mn)
2903 {
2904 MemberDefMutable *md = toMemberDefMutable(imd.get());
2905 if (md &&
2906 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2907 root->fileName==md->getFileDef()->absFilePath()
2908 ) // both variable names in the same file
2909 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2910 )
2911 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2912 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2913 )
2914 // variable already in the scope
2915 {
2916 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2917 md->argsString()!=args &&
2918 args.find('[')!=-1;
2919 bool staticsInDifferentFiles =
2920 root->isStatic && md->isStatic() &&
2921 root->fileName!=md->getDefFileName();
2922
2923 if (md->getFileDef() &&
2924 !isPHPArray && // not a php array
2925 !staticsInDifferentFiles
2926 )
2927 // not a php array variable
2928 {
2929 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2930 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2931 md->setRefItems(root->sli);
2932 md->setRequirementReferences(root->rqli);
2933 // if md is a variable forward declaration and root is the definition that
2934 // turn md into the definition
2935 if (!root->explicitExternal && md->isExternal())
2936 {
2937 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2939 }
2940 // if md is the definition and root point at a declaration, then add the
2941 // declaration info
2942 else if (root->explicitExternal && !md->isExternal())
2943 {
2944 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2945 }
2946 return md;
2947 }
2948 }
2949 }
2950 }
2951
2952 QCString fileName = root->fileName;
2953 if (fileName.isEmpty() && root->tagInfo())
2954 {
2955 fileName = root->tagInfo()->tagName;
2956 }
2957
2958 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2959 // new global variable, enum value or typedef
2960 auto md = createMemberDef(
2961 fileName,root->startLine,root->startColumn,
2962 type,name,args,QCString(),
2963 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2964 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2965 root->argList, root->metaData);
2966 auto mmd = toMemberDefMutable(md.get());
2967 mmd->setTagInfo(root->tagInfo());
2968 mmd->setMemberSpecifiers(root->spec);
2969 mmd->setVhdlSpecifiers(root->vhdlSpec);
2970 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2971 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2972 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2973 mmd->addSectionsToDefinition(root->anchors);
2974 mmd->setFromAnonymousScope(fromAnnScope);
2975 mmd->setFromAnonymousMember(fromAnnMemb);
2976 if (fromAnnMemb)
2977 {
2978 MemberDefMutable *fromAnnMmd = toMemberDefMutable(fromAnnMemb);
2979 if (fromAnnMmd)
2980 {
2981 fromAnnMmd->setToAnonymousMember(mmd);
2982 }
2983 }
2984 mmd->setInitializer(root->initializer.str());
2985 mmd->setMaxInitLines(root->initLines);
2986 mmd->setMemberGroupId(root->mGrpId);
2987 mmd->setDefinition(def);
2988 mmd->setLanguage(root->lang);
2989 mmd->setId(root->id);
2991 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2992 mmd->addQualifiers(root->qualifiers);
2993 //md->setOuterScope(fd);
2994 if (!root->explicitExternal)
2995 {
2996 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2997 mmd->setBodyDef(fd);
2998 }
2999 addMemberToGroups(root,md.get());
3001
3002 mmd->setRefItems(root->sli);
3003 mmd->setRequirementReferences(root->rqli);
3004 if (nd && !nd->isAnonymous())
3005 {
3006 mmd->setNamespace(nd);
3007 nd->insertMember(md.get());
3008 }
3009
3010 // add member to the file (we do this even if we have already inserted
3011 // it into the namespace.
3012 if (fd)
3013 {
3014 mmd->setFileDef(fd);
3015 fd->insertMember(md.get());
3016 }
3017
3018 root->markAsProcessed();
3019
3020 if (mtype==MemberType::Typedef)
3021 {
3022 resolveTemplateInstanceInType(root,nd,md.get());
3023 }
3024
3025 // add member definition to the list of globals
3026 MemberDef *result = md.get();
3027 mn = Doxygen::functionNameLinkedMap->add(name);
3028 mn->push_back(std::move(md));
3029
3030
3031
3032 return result;
3033}
3034
3035/*! See if the return type string \a type is that of a function pointer
3036 * \returns -1 if this is not a function pointer variable or
3037 * the index at which the closing brace of (...*name) was found.
3038 */
3039static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3040{
3041 AUTO_TRACE("type='{}' lang={}",type,lang);
3042 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3043 {
3044 return -1; // Fortran and VHDL do not have function pointers
3045 }
3046
3047 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3048 reg::Match match;
3049 size_t i=std::string::npos;
3050 size_t l=0;
3051 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3052 {
3053 i = match.position();
3054 l = match.length();
3055 }
3056 if (i!=std::string::npos)
3057 {
3058 size_t di = type.find("decltype(");
3059 if (di!=std::string::npos && di<i)
3060 {
3061 i = std::string::npos;
3062 }
3063 }
3064 size_t bb=type.find('<');
3065 size_t be=type.rfind('>');
3066 bool templFp = false;
3067 if (be!=std::string::npos) {
3068 size_t cc_ast = type.find("::*");
3069 size_t cc_amp = type.find("::&");
3070 templFp = (cc_ast != std::string::npos && cc_ast>be) || (cc_amp != std::string::npos && cc_amp>be); // hack to find, e.g 'B<X>(A<int>::*)'
3071 }
3072
3073 if (!type.empty() && // return type is non-empty
3074 i!=std::string::npos && // contains (...*...)
3075 type.find("operator")==std::string::npos && // not an operator
3076 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3077 // not a function pointer return type
3078 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
3079 )
3080 {
3081 if (pLength) *pLength=static_cast<int>(l);
3082 //printf("findFunctionPtr=%d\n",(int)i);
3083 AUTO_TRACE_EXIT("result={}",i);
3084 return static_cast<int>(i);
3085 }
3086 else
3087 {
3088 //printf("findFunctionPtr=%d\n",-1);
3089 AUTO_TRACE_EXIT("result=-1");
3090 return -1;
3091 }
3092}
3093
3094//--------------------------------------------------------------------------------------
3095
3096/*! Returns TRUE iff \a type is a class within scope \a context.
3097 * Used to detect variable declarations that look like function prototypes.
3098 */
3099static bool isVarWithConstructor(const Entry *root)
3100{
3101 bool result = false;
3102 bool typeIsClass = false;
3103 bool typePtrType = false;
3104 QCString type;
3105 Definition *ctx = nullptr;
3106 FileDef *fd = root->fileDef();
3107 SymbolResolver resolver(fd);
3108
3109 AUTO_TRACE("isVarWithConstructor({})",root->name);
3110 if (root->parent()->section.isCompound())
3111 { // inside a class
3112 result=FALSE;
3113 AUTO_TRACE_EXIT("inside class: result={}",result);
3114 return result;
3115 }
3116 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3117 { // inside a .c file
3118 result=FALSE;
3119 AUTO_TRACE_EXIT("inside C file: result={}",result);
3120 return result;
3121 }
3122 if (root->type.isEmpty())
3123 {
3124 result=FALSE;
3125 AUTO_TRACE_EXIT("no type: result={}",result);
3126 return result;
3127 }
3128 if (!root->parent()->name.isEmpty())
3129 {
3130 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
3131 }
3132 type = root->type;
3133 // remove qualifiers
3134 findAndRemoveWord(type,"const");
3135 findAndRemoveWord(type,"static");
3136 findAndRemoveWord(type,"volatile");
3137 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
3138 if (!typePtrType)
3139 {
3140 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3141 int ti=0;
3142 if (!typeIsClass && (ti=type.find('<'))!=-1)
3143 {
3144 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3145 }
3146 }
3147 if (typeIsClass) // now we still have to check if the arguments are
3148 // types or values. Since we do not have complete type info
3149 // we need to rely on heuristics :-(
3150 {
3151 if (root->argList.empty())
3152 {
3153 result=FALSE; // empty arg list -> function prototype.
3154 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3155 return result;
3156 }
3157 for (const Argument &a : root->argList)
3158 {
3159 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3160 reg::Match match;
3161 if (!a.name.isEmpty() || !a.defval.isEmpty())
3162 {
3163 std::string name = a.name.str();
3164 if (reg::search(name,match,initChars) && match.position()==0)
3165 {
3166 result=TRUE;
3167 }
3168 else
3169 {
3170 result=FALSE; // arg has (type,name) pair -> function prototype
3171 }
3172 AUTO_TRACE_EXIT("function prototype: result={}",result);
3173 return result;
3174 }
3175 if (!a.type.isEmpty() &&
3176 (a.type.at(a.type.length()-1)=='*' ||
3177 a.type.at(a.type.length()-1)=='&'))
3178 // type ends with * or & => pointer or reference
3179 {
3180 result=FALSE;
3181 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3182 return result;
3183 }
3184 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3185 {
3186 result=FALSE; // arg type is a known type
3187 AUTO_TRACE_EXIT("known type: result={}",result);
3188 return result;
3189 }
3190 if (checkIfTypedef(ctx,fd,a.type))
3191 {
3192 result=FALSE; // argument is a typedef
3193 AUTO_TRACE_EXIT("typedef: result={}",result);
3194 return result;
3195 }
3196 std::string atype = a.type.str();
3197 if (reg::search(atype,match,initChars) && match.position()==0)
3198 {
3199 result=TRUE; // argument type starts with typical initializer char
3200 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3201 return result;
3202 }
3203 std::string resType=resolveTypeDef(ctx,a.type).str();
3204 if (resType.empty()) resType=atype;
3205 static const reg::Ex idChars(R"(\a\w*)");
3206 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3207 {
3208 resType=match.str();
3209 if (resType=="int" || resType=="long" ||
3210 resType=="float" || resType=="double" ||
3211 resType=="char" || resType=="void" ||
3212 resType=="signed" || resType=="unsigned" ||
3213 resType=="const" || resType=="volatile" )
3214 {
3215 result=FALSE; // type keyword -> function prototype
3216 AUTO_TRACE_EXIT("type keyword: result={}",result);
3217 return result;
3218 }
3219 }
3220 }
3221 result=TRUE;
3222 }
3223
3224 AUTO_TRACE_EXIT("end: result={}",result);
3225 return result;
3226}
3227
3228//--------------------------------------------------------------------------------------
3229
3230/*! Searches for the end of a template in prototype \a s starting from
3231 * character position \a startPos. If the end was found the position
3232 * of the closing > is returned, otherwise -1 is returned.
3233 *
3234 * Handles exotic cases such as
3235 * \code
3236 * Class<(id<0)>
3237 * Class<bits<<2>
3238 * Class<"<">
3239 * Class<'<'>
3240 * Class<(")<")>
3241 * \endcode
3242 */
3243static int findEndOfTemplate(const QCString &s,size_t startPos)
3244{
3245 // locate end of template
3246 size_t e=startPos;
3247 int brCount=1;
3248 int roundCount=0;
3249 size_t len = s.length();
3250 bool insideString=FALSE;
3251 bool insideChar=FALSE;
3252 char pc = 0;
3253 while (e<len && brCount!=0)
3254 {
3255 char c=s.at(e);
3256 switch(c)
3257 {
3258 case '<':
3259 if (!insideString && !insideChar)
3260 {
3261 if (e<len-1 && s.at(e+1)=='<')
3262 e++;
3263 else if (roundCount==0)
3264 brCount++;
3265 }
3266 break;
3267 case '>':
3268 if (!insideString && !insideChar)
3269 {
3270 if (e<len-1 && s.at(e+1)=='>')
3271 e++;
3272 else if (roundCount==0)
3273 brCount--;
3274 }
3275 break;
3276 case '(':
3277 if (!insideString && !insideChar)
3278 roundCount++;
3279 break;
3280 case ')':
3281 if (!insideString && !insideChar)
3282 roundCount--;
3283 break;
3284 case '"':
3285 if (!insideChar)
3286 {
3287 if (insideString && pc!='\\')
3288 insideString=FALSE;
3289 else
3290 insideString=TRUE;
3291 }
3292 break;
3293 case '\'':
3294 if (!insideString)
3295 {
3296 if (insideChar && pc!='\\')
3297 insideChar=FALSE;
3298 else
3299 insideChar=TRUE;
3300 }
3301 break;
3302 }
3303 pc = c;
3304 e++;
3305 }
3306 return brCount==0 ? static_cast<int>(e) : -1;
3307}
3308
3309//--------------------------------------------------------------------------------------
3310
3311static void addVariable(const Entry *root,int isFuncPtr=-1)
3312{
3313 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3314
3315 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3316 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3317 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3318
3319 QCString type = root->type;
3320 QCString name = root->name;
3321 QCString args = root->args;
3322 if (type.isEmpty() && name.find("operator")==-1 &&
3323 (name.find('*')!=-1 || name.find('&')!=-1))
3324 {
3325 // recover from parse error caused by redundant braces
3326 // like in "int *(var[10]);", which is parsed as
3327 // type="" name="int *" args="(var[10])"
3328
3329 type=name;
3330 std::string sargs = args.str();
3331 static const reg::Ex reName(R"(\a\w*)");
3332 reg::Match match;
3333 if (reg::search(sargs,match,reName))
3334 {
3335 name = match.str(); // e.g. 'var' in '(var[10])'
3336 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3337 size_t j = sargs.find(')');
3338 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3339 }
3340 }
3341 else
3342 {
3343 int i=isFuncPtr;
3344 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3345 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3346 if (i>=0) // function pointer
3347 {
3348 int ai = type.find('[',i);
3349 if (ai>i) // function pointer array
3350 {
3351 args.prepend(type.right(type.length()-ai));
3352 type=type.left(ai);
3353 }
3354 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3355 {
3356 type=type.left(type.length()-1);
3357 args.prepend(") ");
3358 }
3359 }
3360 }
3361 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3362
3363 QCString scope;
3364 name=removeRedundantWhiteSpace(name);
3365
3366 // find the scope of this variable
3367 int index = computeQualifiedIndex(name);
3368 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3369 // grouped members are stored with full scope
3370 {
3371 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3372 scope=name.left(index);
3373 name=name.mid(index+2);
3374 }
3375 else
3376 {
3377 Entry *p = root->parent();
3378 while (p->section.isScope())
3379 {
3380 QCString scopeName = p->name;
3381 if (!scopeName.isEmpty())
3382 {
3383 scope.prepend(scopeName);
3384 break;
3385 }
3386 p=p->parent();
3387 }
3388 }
3389
3390 QCString type_s = type;
3391 type=type.stripWhiteSpace();
3392 ClassDefMutable *cd=nullptr;
3393 bool isRelated=FALSE;
3394 bool isMemberOf=FALSE;
3395
3396 QCString classScope=stripAnonymousNamespaceScope(scope);
3397 if (root->lang==SrcLangExt::CSharp)
3398 {
3399 classScope=mangleCSharpGenericName(classScope);
3400 }
3401 else
3402 {
3403 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3404 }
3405 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3406
3407
3408 // Look for last :: not part of template specifier
3409 int p=-1;
3410 for (size_t i=0;i<name.length()-1;i++)
3411 {
3412 if (name[i]==':' && name[i+1]==':')
3413 {
3414 p=static_cast<int>(i);
3415 }
3416 else if (name[i]=='<') // skip over template parts,
3417 // i.e. A::B<C::D> => p=1 and
3418 // A<B::C>::D => p=8
3419 {
3420 int e = findEndOfTemplate(name,i+1);
3421 if (e!=-1) i=static_cast<int>(e);
3422 }
3423 }
3424
3425 if (p!=-1) // found it
3426 {
3427 if (type=="friend class" || type=="friend struct" ||
3428 type=="friend union")
3429 {
3430 cd=getClassMutable(scope);
3431 if (cd)
3432 {
3433 addVariableToClass(root, // entry
3434 cd, // class to add member to
3435 MemberType::Friend, // type of member
3436 type, // type value as string
3437 name, // name of the member
3438 args, // arguments as string
3439 FALSE, // from Anonymous scope
3440 nullptr, // anonymous member
3441 Protection::Public, // protection
3442 Relationship::Member // related to a class
3443 );
3444 }
3445 }
3446 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3447 {
3448 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3449 }
3450
3451
3452 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3453 return; /* skip this member, because it is a
3454 * static variable definition (always?), which will be
3455 * found in a class scope as well, but then we know the
3456 * correct protection level, so only then it will be
3457 * inserted in the correct list!
3458 */
3459 }
3460
3461 MemberType mtype = MemberType::Variable;
3462 if (type=="@")
3463 mtype=MemberType::EnumValue;
3464 else if (type_s.startsWith("typedef "))
3465 mtype=MemberType::Typedef;
3466 else if (type_s.startsWith("friend "))
3467 mtype=MemberType::Friend;
3468 else if (root->mtype==MethodTypes::Property)
3469 mtype=MemberType::Property;
3470 else if (root->mtype==MethodTypes::Event)
3471 mtype=MemberType::Event;
3472 else if (type.find("sequence<") != -1)
3473 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3474 else if (type.find("dictionary<") != -1)
3475 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3476
3477 if (!root->relates.isEmpty()) // related variable
3478 {
3479 isRelated=TRUE;
3480 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3481 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3482 scope=mergeScopes(scope,root->relates);
3483 else
3484 scope=root->relates;
3485 }
3486
3487 cd=getClassMutable(scope);
3488 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3489 if (cd)
3490 {
3491 MemberDef *md=nullptr;
3492
3493 // if cd is an anonymous (=tag less) scope we insert the member
3494 // into a non-anonymous parent scope as well. This is needed to
3495 // be able to refer to it using \var or \fn
3496
3497
3498 Relationship relationship = isMemberOf ? Relationship::Foreign :
3499 isRelated ? Relationship::Related :
3500 Relationship::Member ;
3501
3502 addVariableToClass(root, // entry
3503 cd, // class to add member to
3504 mtype, // member type
3505 type, // type value as string
3506 name, // name of the member
3507 args, // arguments as string
3508 FALSE, // from anonymous scope
3509 md, // from anonymous member
3510 root->protection,
3511 relationship
3512 );
3513 }
3514 else if (!name.isEmpty()) // global variable
3515 {
3516 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3517 }
3518
3519}
3520
3521//----------------------------------------------------------------------
3522// Searches the Entry tree for typedef documentation sections.
3523// If found they are stored in their class or in the global list.
3524static void buildTypedefList(const Entry *root)
3525{
3526 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3527 if (!root->name.isEmpty() &&
3528 root->section.isVariable() &&
3529 root->type.find("typedef ")!=-1 // its a typedef
3530 )
3531 {
3532 AUTO_TRACE();
3534 QCString scope;
3535 int index = computeQualifiedIndex(rname);
3536 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3537 // grouped members are stored with full scope
3538 {
3539 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3540 scope=rname.left(index);
3541 rname=rname.mid(index+2);
3542 }
3543 else
3544 {
3545 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3546 }
3549 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3550 bool found=false;
3551 if (mn) // symbol with the same name already found
3552 {
3553 for (auto &imd : *mn)
3554 {
3555 if (!imd->isTypedef())
3556 continue;
3557
3558 QCString rtype = root->type;
3559 rtype.stripPrefix("typedef ");
3560
3561 // merge the typedefs only if they're not both grouped, and both are
3562 // either part of the same class, part of the same namespace, or both
3563 // are global (i.e., neither in a class or a namespace)
3564 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3565 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3566 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3567 // qPrint(imd->typeString()),qPrint(root->type));
3568 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3569 {
3570 MemberDefMutable *md = toMemberDefMutable(imd.get());
3571 if (md)
3572 {
3573 md->setDocumentation(root->doc,root->docFile,root->docLine);
3575 md->setDocsForDefinition(!root->proto);
3576 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3578 md->setRefItems(root->sli);
3579 md->setRequirementReferences(root->rqli);
3580 md->addQualifiers(root->qualifiers);
3581
3582 // merge ingroup specifiers
3583 if (md->getGroupDef()==nullptr && !root->groups.empty())
3584 {
3585 addMemberToGroups(root,md);
3586 }
3587 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3588 {
3589 //printf("existing member is grouped, new member not\n");
3590 }
3591 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3592 {
3593 //printf("both members are grouped\n");
3594 }
3595 found=true;
3596 break;
3597 }
3598 }
3599 }
3600 }
3601 if (found)
3602 {
3603 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3604 // mark the entry as processed, as we copied everything from it elsewhere
3605 // also, otherwise, due to containing `typedef` it may later get treated
3606 // as a function typedef in filterMemberDocumentation, which is incorrect
3607 root->markAsProcessed();
3608 }
3609 else
3610 {
3611 AUTO_TRACE_ADD("new typedef '{}'",rname);
3612 addVariable(root);
3613 }
3614
3615 }
3616 for (const auto &e : root->children())
3617 if (!e->section.isEnum())
3618 buildTypedefList(e.get());
3619}
3620
3621//----------------------------------------------------------------------
3622// Searches the Entry tree for sequence documentation sections.
3623// If found they are stored in the global list.
3624static void buildSequenceList(const Entry *root)
3625{
3626 if (!root->name.isEmpty() &&
3627 root->section.isVariable() &&
3628 root->type.find("sequence<")!=-1 // it's a sequence
3629 )
3630 {
3631 AUTO_TRACE();
3632 addVariable(root);
3633 }
3634 for (const auto &e : root->children())
3635 if (!e->section.isEnum())
3636 buildSequenceList(e.get());
3637}
3638
3639//----------------------------------------------------------------------
3640// Searches the Entry tree for dictionary documentation sections.
3641// If found they are stored in the global list.
3642static void buildDictionaryList(const Entry *root)
3643{
3644 if (!root->name.isEmpty() &&
3645 root->section.isVariable() &&
3646 root->type.find("dictionary<")!=-1 // it's a dictionary
3647 )
3648 {
3649 AUTO_TRACE();
3650 addVariable(root);
3651 }
3652 for (const auto &e : root->children())
3653 if (!e->section.isEnum())
3654 buildDictionaryList(e.get());
3655}
3656
3657//----------------------------------------------------------------------
3658// Searches the Entry tree for Variable documentation sections.
3659// If found they are stored in their class or in the global list.
3660
3661static void buildVarList(const Entry *root)
3662{
3663 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3664 int isFuncPtr=-1;
3665 if (!root->name.isEmpty() &&
3666 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3667 (
3668 (root->section.isVariable() && // it's a variable
3669 root->type.find("typedef ")==-1 // and not a typedef
3670 ) ||
3671 (root->section.isFunction() && // or maybe a function pointer variable
3672 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3673 ) ||
3674 (root->section.isFunction() && // class variable initialized by constructor
3676 )
3677 )
3678 ) // documented variable
3679 {
3680 AUTO_TRACE();
3681 addVariable(root,isFuncPtr);
3682 }
3683 for (const auto &e : root->children())
3684 if (!e->section.isEnum())
3685 buildVarList(e.get());
3686}
3687
3688//----------------------------------------------------------------------
3689// Searches the Entry tree for Interface sections (UNO IDL only).
3690// If found they are stored in their service or in the global list.
3691//
3692
3694 const Entry *root,
3695 ClassDefMutable *cd,
3696 QCString const& rname)
3697{
3698 FileDef *fd = root->fileDef();
3699 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3700 QCString fileName = root->fileName;
3701 if (fileName.isEmpty() && root->tagInfo())
3702 {
3703 fileName = root->tagInfo()->tagName;
3704 }
3705 auto md = createMemberDef(
3706 fileName, root->startLine, root->startColumn, root->type, rname,
3707 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3708 type, ArgumentList(), root->argList, root->metaData);
3709 auto mmd = toMemberDefMutable(md.get());
3710 mmd->setTagInfo(root->tagInfo());
3711 mmd->setMemberClass(cd);
3712 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3713 mmd->setDocsForDefinition(false);
3714 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3715 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3716 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3717 mmd->setMemberSpecifiers(root->spec);
3718 mmd->setVhdlSpecifiers(root->vhdlSpec);
3719 mmd->setMemberGroupId(root->mGrpId);
3720 mmd->setTypeConstraints(root->typeConstr);
3721 mmd->setLanguage(root->lang);
3722 mmd->setBodyDef(fd);
3723 mmd->setFileDef(fd);
3724 mmd->addSectionsToDefinition(root->anchors);
3725 QCString const def = root->type + " " + rname;
3726 mmd->setDefinition(def);
3728 mmd->addQualifiers(root->qualifiers);
3729
3730 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3731 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3732
3733 // add member to the class cd
3734 cd->insertMember(md.get());
3735 // also add the member as a "base" (to get nicer diagrams)
3736 // "optional" interface/service get Protected which turns into dashed line
3737 BaseInfo base(rname,
3738 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3739 TemplateNameMap templateNames;
3740 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3741 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3742 // add file to list of used files
3743 cd->insertUsedFile(fd);
3744
3745 addMemberToGroups(root,md.get());
3747 root->markAsProcessed();
3748 mmd->setRefItems(root->sli);
3749 mmd->setRequirementReferences(root->rqli);
3750
3751 // add member to the global list of all members
3752 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3753 mn->push_back(std::move(md));
3754}
3755
3756static void buildInterfaceAndServiceList(const Entry *root)
3757{
3758 if (root->section.isExportedInterface() || root->section.isIncludedService())
3759 {
3760 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3761 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3762 " mGrpId={} spec={} proto={} docFile='{}'",
3763 root->type, root->parent()->name, root->name, root->args,
3764 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3765 root->mGrpId, root->spec, root->proto, root->docFile);
3766
3768
3769 if (!rname.isEmpty())
3770 {
3771 QCString scope = root->parent()->name;
3772 ClassDefMutable *cd = getClassMutable(scope);
3773 assert(cd);
3774 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3775 (ClassDef::Service == cd->compoundType()) ||
3777 {
3779 }
3780 else
3781 {
3782 assert(false); // was checked by scanner.l
3783 }
3784 }
3785 else if (rname.isEmpty())
3786 {
3787 warn(root->fileName,root->startLine,
3788 "Illegal member name found.");
3789 }
3790 }
3791 // can only have these in IDL anyway
3792 switch (root->lang)
3793 {
3794 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3795 case SrcLangExt::IDL:
3796 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3797 break;
3798 default:
3799 return; // nothing to do here
3800 }
3801}
3802
3803
3804//----------------------------------------------------------------------
3805// Searches the Entry tree for Function sections.
3806// If found they are stored in their class or in the global list.
3807
3808static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3809 const QCString &rtype,const QCString &rname,const QCString &rargs,
3810 bool isFriend,
3811 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3812 const QCString &relates
3813 )
3814{
3815 FileDef *fd=root->fileDef();
3816
3817 QCString type = rtype;
3818 QCString args = rargs;
3819
3821 name.stripPrefix("::");
3822
3823 MemberType mtype = MemberType::Function;
3824 if (isFriend) mtype=MemberType::Friend;
3825 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3826 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3827 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3828
3829 // strip redundant template specifier for constructors
3830 int i = -1;
3831 int j = -1;
3832 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3833 !name.startsWith("operator ") && // not operator
3834 (i=name.find('<'))!=-1 && // containing <
3835 (j=name.find('>'))!=-1 && // or >
3836 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3837 )
3838 {
3839 name=name.left(i);
3840 }
3841
3842 QCString fileName = root->fileName;
3843 if (fileName.isEmpty() && root->tagInfo())
3844 {
3845 fileName = root->tagInfo()->tagName;
3846 }
3847
3848 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3849 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3850 // );
3851
3852 // adding class member
3853 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3854 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3855 Relationship::Related ;
3856 auto md = createMemberDef(
3857 fileName,root->startLine,root->startColumn,
3858 type,name,args,root->exception,
3859 protection,virt,
3860 stat && root->relatesType!=RelatesType::MemberOf,
3861 relationship,
3862 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3863 root->argList, root->metaData);
3864 auto mmd = toMemberDefMutable(md.get());
3865 mmd->setTagInfo(root->tagInfo());
3866 mmd->setMemberClass(cd);
3867 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3868 mmd->setDocsForDefinition(!root->proto);
3869 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3870 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3871 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3872 mmd->setMemberSpecifiers(spec);
3873 mmd->setVhdlSpecifiers(root->vhdlSpec);
3874 mmd->setMemberGroupId(root->mGrpId);
3875 mmd->setTypeConstraints(root->typeConstr);
3876 mmd->setLanguage(root->lang);
3877 mmd->setRequiresClause(root->req);
3878 mmd->setId(root->id);
3879 mmd->setBodyDef(fd);
3880 mmd->setFileDef(fd);
3881 mmd->addSectionsToDefinition(root->anchors);
3882 QCString def;
3884 SrcLangExt lang = cd->getLanguage();
3885 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3886 if (scopeSeparator!="::")
3887 {
3888 qualScope = substitute(qualScope,"::",scopeSeparator);
3889 }
3890 if (lang==SrcLangExt::PHP)
3891 {
3892 // for PHP we use Class::method and Namespace\method
3893 scopeSeparator="::";
3894 }
3895 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3896 {
3897 if (!type.isEmpty())
3898 {
3899 def=type+" "+name; //+optArgs;
3900 }
3901 else
3902 {
3903 def=name; //+optArgs;
3904 }
3905 }
3906 else
3907 {
3908 if (!type.isEmpty())
3909 {
3910 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3911 }
3912 else
3913 {
3914 def=qualScope+scopeSeparator+name; //+optArgs;
3915 }
3916 }
3917 def.stripPrefix("friend ");
3918 mmd->setDefinition(def);
3920 mmd->addQualifiers(root->qualifiers);
3921
3922 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3923 type, qualScope, rname, args, root->proto, def);
3924
3925 // add member to the class cd
3926 cd->insertMember(md.get());
3927 // add file to list of used files
3928 cd->insertUsedFile(fd);
3929
3930 addMemberToGroups(root,md.get());
3932 root->markAsProcessed();
3933 mmd->setRefItems(root->sli);
3934 mmd->setRequirementReferences(root->rqli);
3935
3936 // add member to the global list of all members
3937 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3939 mn->push_back(std::move(md));
3940}
3941
3942//------------------------------------------------------------------------------------------
3943
3944static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3945{
3946 QCString scope = sc;
3947
3948 // new global function
3950 auto md = createMemberDef(
3951 root->fileName,root->startLine,root->startColumn,
3952 root->type,name,root->args,root->exception,
3953 root->protection,root->virt,root->isStatic,Relationship::Member,
3954 MemberType::Function,
3955 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3956 root->argList,root->metaData);
3957 auto mmd = toMemberDefMutable(md.get());
3958 mmd->setTagInfo(root->tagInfo());
3959 mmd->setLanguage(root->lang);
3960 mmd->setId(root->id);
3961 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3962 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3963 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3964 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3965 mmd->setDocsForDefinition(!root->proto);
3966 mmd->setTypeConstraints(root->typeConstr);
3967 //md->setBody(root->body);
3968 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3969 FileDef *fd=root->fileDef();
3970 mmd->setBodyDef(fd);
3971 mmd->addSectionsToDefinition(root->anchors);
3972 mmd->setMemberSpecifiers(root->spec);
3973 mmd->setVhdlSpecifiers(root->vhdlSpec);
3974 mmd->setMemberGroupId(root->mGrpId);
3975 mmd->setRequiresClause(root->req);
3976 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3977
3978 NamespaceDefMutable *nd = nullptr;
3979 // see if the function is inside a namespace that was not part of
3980 // the name already (in that case nd should be non-zero already)
3981 if (root->parent()->section.isNamespace())
3982 {
3983 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3984 QCString nscope=root->parent()->name;
3985 if (!nscope.isEmpty())
3986 {
3987 nd = getResolvedNamespaceMutable(nscope);
3988 }
3989 }
3990 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3991 {
3993 }
3994
3995 if (!scope.isEmpty())
3996 {
3998 if (sep!="::")
3999 {
4000 scope = substitute(scope,"::",sep);
4001 }
4002 scope+=sep;
4003 }
4004
4005 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
4006 QCString def;
4007 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
4008 if (!root->type.isEmpty())
4009 {
4010 def=root->type+" "+scope+name; //+optArgs;
4011 }
4012 else
4013 {
4014 def=scope+name; //+optArgs;
4015 }
4016 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4017 root->type,scope,rname,root->args,root->proto,def);
4018 mmd->setDefinition(def);
4020 mmd->addQualifiers(root->qualifiers);
4021
4022 mmd->setRefItems(root->sli);
4023 mmd->setRequirementReferences(root->rqli);
4024 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
4025 {
4026 // add member to namespace
4027 mmd->setNamespace(nd);
4028 nd->insertMember(md.get());
4029 }
4030 if (fd)
4031 {
4032 // add member to the file (we do this even if we have already
4033 // inserted it into the namespace)
4034 mmd->setFileDef(fd);
4035 fd->insertMember(md.get());
4036 }
4037
4038 addMemberToGroups(root,md.get());
4040 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4041 // allow find Member to pick it up
4042 {
4043 root->markAsProcessed(); // Otherwise we have finished with this entry.
4044 }
4045
4046 // add member to the list of file members
4048 mn->push_back(std::move(md));
4049}
4050
4051//------------------------------------------------------------------------------------------
4052
4053static void buildFunctionList(const Entry *root)
4054{
4055 if (root->section.isFunction())
4056 {
4057 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4058 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4059 " spec={} proto={} docFile='{}'",
4060 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4061 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4062 root->spec, root->proto, root->docFile);
4063
4064 bool isFriend=root->type=="friend" || root->type.find("friend ")!=-1;
4066 //printf("rname=%s\n",qPrint(rname));
4067
4068 QCString scope;
4069 int index = computeQualifiedIndex(rname);
4070 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4071 // grouped members are stored with full scope
4072 {
4073 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4074 scope=rname.left(index);
4075 rname=rname.mid(index+2);
4076 }
4077 else
4078 {
4079 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4080 }
4081 if (!rname.isEmpty() && scope.find('@')==-1)
4082 {
4083 // check if this function's parent is a class
4084 if (root->lang==SrcLangExt::CSharp)
4085 {
4086 scope=mangleCSharpGenericName(scope);
4087 }
4088 else
4089 {
4091 }
4092
4093 FileDef *rfd=root->fileDef();
4094
4095 int memIndex=rname.findRev("::");
4096
4098 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4099 {
4100 // strip scope from name
4101 rname=rname.right(rname.length()-root->parent()->name.length()-2);
4102 }
4103
4104 bool isMember=FALSE;
4105 if (memIndex!=-1)
4106 {
4107 int ts=rname.find('<');
4108 int te=rname.find('>');
4109 if (memIndex>0 && (ts==-1 || te==-1))
4110 {
4111 // note: the following code was replaced by inMember=TRUE to deal with a
4112 // function rname='X::foo' of class X inside a namespace also called X...
4113 // bug id 548175
4114 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4115 //isMember = nd==nullptr;
4116 //if (nd)
4117 //{
4118 // // strip namespace scope from name
4119 // scope=rname.left(memIndex);
4120 // rname=rname.right(rname.length()-memIndex-2);
4121 //}
4122 isMember = TRUE;
4123 }
4124 else
4125 {
4126 isMember=memIndex<ts || memIndex>te;
4127 }
4128 }
4129
4130 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
4131 {
4132 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4133 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4134 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4135 }
4136 else if (root->parent()->section.isObjcImpl() && cd)
4137 {
4138 const MemberDef *md = cd->getMemberByName(rname);
4139 if (md)
4140 {
4141 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4142 if (mdm)
4143 {
4144 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4145 mdm->setBodyDef(root->fileDef());
4146 }
4147 }
4148 }
4149 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4150 !isMember &&
4151 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
4152 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4153 )
4154 // no member => unrelated function
4155 {
4156 /* check the uniqueness of the function name in the file.
4157 * A file could contain a function prototype and a function definition
4158 * or even multiple function prototypes.
4159 */
4160 bool found=FALSE;
4161 MemberDef *md_found=nullptr;
4162 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
4163 if (mn)
4164 {
4165 AUTO_TRACE_ADD("function '{}' already found",rname);
4166 for (const auto &imd : *mn)
4167 {
4168 MemberDefMutable *md = toMemberDefMutable(imd.get());
4169 if (md)
4170 {
4171 const NamespaceDef *mnd = md->getNamespaceDef();
4172 NamespaceDef *rnd = nullptr;
4173 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4174 QCString fullScope = scope;
4175 QCString parentScope = root->parent()->name;
4176 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4177 {
4178 if (!scope.isEmpty()) fullScope.prepend("::");
4179 fullScope.prepend(parentScope);
4180 }
4181 //printf("fullScope=%s\n",qPrint(fullScope));
4182 rnd = getResolvedNamespace(fullScope);
4183 const FileDef *mfd = md->getFileDef();
4184 QCString nsName,rnsName;
4185 if (mnd) nsName = mnd->name();
4186 if (rnd) rnsName = rnd->name();
4187 //printf("matching arguments for %s%s %s%s\n",
4188 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4189 const ArgumentList &mdAl = md->argumentList();
4190 const ArgumentList &mdTempl = md->templateArguments();
4191
4192 // in case of template functions, we need to check if the
4193 // functions have the same number of template parameters
4194 bool sameTemplateArgs = TRUE;
4195 bool matchingReturnTypes = TRUE;
4196 bool sameRequiresClause = TRUE;
4197 if (!mdTempl.empty() && !root->tArgLists.empty())
4198 {
4199 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4200 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4201 {
4202 matchingReturnTypes = FALSE;
4203 }
4204 if (md->requiresClause()!=root->req)
4205 {
4206 sameRequiresClause = FALSE;
4207 }
4208 }
4209 else if (!mdTempl.empty() || !root->tArgLists.empty())
4210 { // if one has template parameters and the other doesn't then that also counts as a
4211 // difference
4212 sameTemplateArgs = FALSE;
4213 }
4214
4215 bool staticsInDifferentFiles =
4216 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4217
4218 if (sameTemplateArgs &&
4219 matchingReturnTypes &&
4220 sameRequiresClause &&
4221 !staticsInDifferentFiles &&
4222 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4223 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4224 FALSE,root->lang)
4225 )
4226 {
4227 GroupDef *gd=nullptr;
4228 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4229 {
4230 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4231 }
4232 //printf("match!\n");
4233 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4234 // see if we need to create a new member
4235 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4236 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4237 mfd->absFilePath()==root->fileName // prototype in the same file
4238 )
4239 );
4240 // otherwise, allow a duplicate global member with the same argument list
4241 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4242 {
4243 // member is already in the group, so we don't want to add it again.
4244 found=TRUE;
4245 }
4246
4247 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4248
4249 if (found)
4250 {
4251 // merge argument lists
4252 ArgumentList mergedArgList = root->argList;
4253 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4254 // merge documentation
4255 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4256 {
4257 if (root->proto)
4258 {
4260 }
4261 else
4262 {
4264 }
4265 }
4266
4267 md->setDocumentation(root->doc,root->docFile,root->docLine);
4269 md->setDocsForDefinition(!root->proto);
4270 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4271 {
4272 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4273 md->setBodyDef(rfd);
4274 }
4275
4276 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4277 {
4278 md->setArgsString(root->args);
4279 }
4280 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4281
4283
4285 md->addQualifiers(root->qualifiers);
4286
4287 // merge ingroup specifiers
4288 if (md->getGroupDef()==nullptr && !root->groups.empty())
4289 {
4290 addMemberToGroups(root,md);
4291 }
4292 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4293 {
4294 //printf("existing member is grouped, new member not\n");
4295 }
4296 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4297 {
4298 //printf("both members are grouped\n");
4299 }
4301
4302 // if md is a declaration and root is the corresponding
4303 // definition, then turn md into a definition.
4304 if (md->isPrototype() && !root->proto)
4305 {
4306 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4307 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4308 }
4309 // if md is already the definition, then add the declaration info
4310 else if (!md->isPrototype() && root->proto)
4311 {
4312 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4313 }
4314 }
4315 }
4316 }
4317 if (found)
4318 {
4319 md_found = md;
4320 break;
4321 }
4322 }
4323 }
4324 if (!found) /* global function is unique with respect to the file */
4325 {
4326 addGlobalFunction(root,rname,scope);
4327 }
4328 else
4329 {
4330 FileDef *fd=root->fileDef();
4331 if (fd)
4332 {
4333 // add member to the file (we do this even if we have already
4334 // inserted it into the namespace)
4335 fd->insertMember(md_found);
4336 }
4337 }
4338
4339 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4340 }
4341 else
4342 {
4343 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4344 }
4345 }
4346 else if (rname.isEmpty())
4347 {
4348 warn(root->fileName,root->startLine,
4349 "Illegal member name found."
4350 );
4351 }
4352 }
4353 for (const auto &e : root->children()) buildFunctionList(e.get());
4354}
4355
4356//----------------------------------------------------------------------
4357
4358static void findFriends()
4359{
4360 AUTO_TRACE();
4361 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4362 {
4363 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4364 if (mn)
4365 { // there are members with the same name
4366 // for each function with that name
4367 for (const auto &ifmd : *fn)
4368 {
4369 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4370 // for each member with that name
4371 for (const auto &immd : *mn)
4372 {
4373 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4374 //printf("Checking for matching arguments
4375 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4376 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4377 if (fmd && mmd &&
4378 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4379 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4380 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4381 TRUE,mmd->getLanguage()
4382 )
4383
4384 ) // if the member is related and the arguments match then the
4385 // function is actually a friend.
4386 {
4387 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4388 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4389 const ArgumentList &mmdAl = mmd->argumentList();
4390 const ArgumentList &fmdAl = fmd->argumentList();
4391 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4392
4393 // reset argument lists to add missing default parameters
4394 QCString mmdAlStr = argListToString(mmdAl);
4395 QCString fmdAlStr = argListToString(fmdAl);
4396 mmd->setArgsString(mmdAlStr);
4397 fmd->setArgsString(fmdAlStr);
4398 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4399 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4400 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4401
4402 if (!fmd->documentation().isEmpty())
4403 {
4404 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4405 }
4406 else if (!mmd->documentation().isEmpty())
4407 {
4408 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4409 }
4410 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4411 {
4412 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4413 }
4414 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4415 {
4416 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4417 }
4418 if (!fmd->inbodyDocumentation().isEmpty())
4419 {
4421 }
4422 else if (!mmd->inbodyDocumentation().isEmpty())
4423 {
4425 }
4426 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4427 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4428 {
4429 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4430 mmd->setBodyDef(fmd->getBodyDef());
4431 //mmd->setBodyMember(fmd);
4432 }
4433 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4434 {
4435 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4436 fmd->setBodyDef(mmd->getBodyDef());
4437 //fmd->setBodyMember(mmd);
4438 }
4440
4442
4443 mmd->addQualifiers(fmd->getQualifiers());
4444 fmd->addQualifiers(mmd->getQualifiers());
4445
4446 }
4447 }
4448 }
4449 }
4450 }
4451}
4452
4453//----------------------------------------------------------------------
4454
4456{
4457 AUTO_TRACE();
4458
4459 // find matching function declaration and definitions.
4460 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4461 {
4462 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4463 /* find a matching function declaration and definition for this function */
4464 for (const auto &imdec : *mn)
4465 {
4466 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4467 if (mdec &&
4468 (mdec->isPrototype() ||
4469 (mdec->isVariable() && mdec->isExternal())
4470 ))
4471 {
4472 for (const auto &imdef : *mn)
4473 {
4474 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4475 if (mdef && mdec!=mdef &&
4476 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4477 {
4479 }
4480 }
4481 }
4482 }
4483 }
4484}
4485
4486//----------------------------------------------------------------------
4487
4489{
4490 AUTO_TRACE();
4491 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4492 {
4493 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4494 /* find a matching function declaration and definition for this function */
4495 for (const auto &imd : *mn)
4496 {
4497 MemberDefMutable *md = toMemberDefMutable(imd.get());
4498 if (md)
4499 {
4500 if (md->isPrototype())
4501 mdec=md;
4502 else if (md->isVariable() && md->isExternal())
4503 mdec=md;
4504
4505 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4506 mdef=md;
4507 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4508 mdef=md;
4509 }
4510
4511 if (mdef && mdec) break;
4512 }
4513 if (mdef && mdec)
4514 {
4515 const ArgumentList &mdefAl = mdef->argumentList();
4516 const ArgumentList &mdecAl = mdec->argumentList();
4517 if (
4518 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4519 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4520 TRUE,mdef->getLanguage()
4521 )
4522 ) /* match found */
4523 {
4524 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4525 mdef->mergeReferences(mdec);
4526 mdec->mergeReferences(mdef);
4527 mdef->mergeReferencedBy(mdec);
4528 mdec->mergeReferencedBy(mdef);
4529 }
4530 }
4531 }
4532}
4533
4534//----------------------------------------------------------------------
4535
4537{
4538 AUTO_TRACE();
4539 // find match between function declaration and definition for
4540 // related functions
4541 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4542 {
4543 /* find a matching function declaration and definition for this function */
4544 // for each global function
4545 for (const auto &imd : *mn)
4546 {
4547 MemberDefMutable *md = toMemberDefMutable(imd.get());
4548 if (md)
4549 {
4550 //printf(" Function '%s'\n",qPrint(md->name()));
4551 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4552 if (rmn) // check if there is a member with the same name
4553 {
4554 //printf(" Member name found\n");
4555 // for each member with the same name
4556 for (const auto &irmd : *rmn)
4557 {
4558 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4559 //printf(" Member found: related='%d'\n",rmd->isRelated());
4560 if (rmd &&
4561 (rmd->isRelated() || rmd->isForeign()) && // related function
4562 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4563 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4564 TRUE,md->getLanguage()
4565 )
4566 )
4567 {
4568 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4569 if (rmd->relatedAlso())
4570 md->setRelatedAlso(rmd->relatedAlso());
4571 else if (rmd->isForeign())
4572 md->makeForeign();
4573 else
4574 md->makeRelated();
4575 }
4576 }
4577 }
4578 }
4579 }
4580 }
4581}
4582
4583//----------------------------------------------------------------------
4584
4586{
4587 AUTO_TRACE();
4588 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4589 {
4590 size_t i=qualifiedName.rfind("::");
4591 if (i!=std::string::npos)
4592 {
4593 QCString scope = qualifiedName.substr(0,i);
4594 QCString name = qualifiedName.substr(i+2);
4595 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4596 if (mn)
4597 {
4598 for (const auto &imd : *mn)
4599 {
4600 MemberDefMutable *md = toMemberDefMutable(imd.get());
4601 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4602 {
4603 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4604 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4605 md->setBodySegment(bodyInfo.defLine,
4606 bodyInfo.startLine,
4607 bodyInfo.endLine);
4608 }
4609 }
4610 }
4611 }
4612 }
4613}
4614
4615//----------------------------------------------------------------------
4616
4617/*! make a dictionary of all template arguments of class cd
4618 * that are part of the base class name.
4619 * Example: A template class A with template arguments <R,S,T>
4620 * that inherits from B<T,T,S> will have T and S in the dictionary.
4621 */
4622static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4623{
4624 std::map<std::string,int> templateNames;
4625 int count=0;
4626 for (const Argument &arg : templateArguments)
4627 {
4628 static const reg::Ex re(R"(\a[\w:]*)");
4629 reg::Iterator it(name,re);
4631 for (; it!=end ; ++it)
4632 {
4633 const auto &match = *it;
4634 std::string n = match.str();
4635 if (n==arg.name.str())
4636 {
4637 if (templateNames.find(n)==templateNames.end())
4638 {
4639 templateNames.emplace(n,count);
4640 }
4641 }
4642 }
4643 }
4644 return templateNames;
4645}
4646
4647/*! Searches a class from within \a context and \a cd and returns its
4648 * definition if found (otherwise nullptr is returned).
4649 */
4651{
4652 ClassDef *result=nullptr;
4653 if (cd==nullptr)
4654 {
4655 return result;
4656 }
4657 FileDef *fd=cd->getFileDef();
4658 SymbolResolver resolver(fd);
4659 if (context && cd!=context)
4660 {
4661 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4662 }
4663 //printf("1. result=%p\n",result);
4664 if (result==nullptr)
4665 {
4666 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4667 }
4668 //printf("2. result=%p\n",result);
4669 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4670 {
4671 result = getClass(name);
4672 }
4673 //printf("3. result=%p\n",result);
4674 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4675 // qPrint(name),
4676 // context ? qPrint(context->name()) : "<none>",
4677 // cd ? qPrint(cd->name()) : "<none>",
4678 // result ? qPrint(result->name()) : "<none>",
4679 // Doxygen::classLinkedMap->find(name)
4680 // );
4681 return result;
4682}
4683
4684
4685static void findUsedClassesForClass(const Entry *root,
4686 Definition *context,
4687 ClassDefMutable *masterCd,
4688 ClassDefMutable *instanceCd,
4689 bool isArtificial,
4690 const ArgumentList *actualArgs = nullptr,
4691 const TemplateNameMap &templateNames = TemplateNameMap()
4692 )
4693{
4694 AUTO_TRACE();
4695 const ArgumentList &formalArgs = masterCd->templateArguments();
4696 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4697 {
4698 for (auto &mi : *mni)
4699 {
4700 const MemberDef *md=mi->memberDef();
4701 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4702 {
4703 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4704 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4705 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4706 if (!typedefValue.isEmpty())
4707 {
4708 type = typedefValue;
4709 }
4710 int pos=0;
4711 QCString usedClassName;
4712 QCString templSpec;
4713 bool found=FALSE;
4714 // the type can contain template variables, replace them if present
4715 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4716
4717 //printf(" template substitution gives=%s\n",qPrint(type));
4718 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4719 {
4720 // find the type (if any) that matches usedClassName
4721 SymbolResolver resolver(masterCd->getFileDef());
4722 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4723 //printf("====> usedClassName=%s -> typeCd=%s\n",
4724 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4725 if (typeCd)
4726 {
4727 usedClassName = typeCd->name();
4728 }
4729
4730 // replace any namespace aliases
4731 replaceNamespaceAliases(usedClassName);
4732 // add any template arguments to the class
4733 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4734 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4735
4736 TemplateNameMap formTemplateNames;
4737 if (templateNames.empty())
4738 {
4739 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4740 }
4741 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4742 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4743
4744 for (const Argument &arg : masterCd->templateArguments())
4745 {
4746 if (arg.name==usedName) // type is a template argument
4747 {
4748 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4749 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4750 if (usedCd==nullptr)
4751 {
4752 usedCdm = toClassDefMutable(
4753 Doxygen::hiddenClassLinkedMap->add(usedName,
4755 masterCd->getDefFileName(),masterCd->getDefLine(),
4756 masterCd->getDefColumn(),
4757 usedName,
4758 ClassDef::Class)));
4759 if (usedCdm)
4760 {
4761 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4762 usedCdm->makeTemplateArgument();
4763 usedCdm->setUsedOnly(TRUE);
4764 usedCdm->setLanguage(masterCd->getLanguage());
4765 usedCd = usedCdm;
4766 }
4767 }
4768 if (usedCd)
4769 {
4770 found=TRUE;
4771 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4772 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4773 if (usedCdm)
4774 {
4775 if (isArtificial) usedCdm->setArtificial(TRUE);
4776 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4777 }
4778 }
4779 }
4780 }
4781
4782 if (!found)
4783 {
4784 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4785 //printf("Looking for used class %s: result=%s master=%s\n",
4786 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4787
4788 if (usedCd)
4789 {
4790 found=TRUE;
4791 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4792 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4793 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4794 if (usedCdm)
4795 {
4796 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4797 }
4798 }
4799 }
4800 }
4801 if (!found && !type.isEmpty()) // used class is not documented in any scope
4802 {
4803 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4804 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4805 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4806 {
4807 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4808 {
4809 type+=md->argsString();
4810 }
4811 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4812 usedCdm = toClassDefMutable(
4815 masterCd->getDefFileName(),masterCd->getDefLine(),
4816 masterCd->getDefColumn(),
4817 type,ClassDef::Class)));
4818 if (usedCdm)
4819 {
4820 usedCdm->setUsedOnly(TRUE);
4821 usedCdm->setLanguage(masterCd->getLanguage());
4822 usedCd = usedCdm;
4823 }
4824 }
4825 if (usedCd)
4826 {
4827 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4828 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4829 if (usedCdm)
4830 {
4831 if (isArtificial) usedCdm->setArtificial(TRUE);
4832 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4833 }
4834 }
4835 }
4836 }
4837 }
4838 }
4839}
4840
4842 const Entry *root,
4843 Definition *context,
4844 ClassDefMutable *masterCd,
4845 ClassDefMutable *instanceCd,
4847 bool isArtificial,
4848 const ArgumentList *actualArgs = nullptr,
4849 const TemplateNameMap &templateNames=TemplateNameMap()
4850 )
4851{
4852 AUTO_TRACE("name={}",root->name);
4853 // The base class could ofcouse also be a non-nested class
4854 const ArgumentList &formalArgs = masterCd->templateArguments();
4855 for (const BaseInfo &bi : root->extends)
4856 {
4857 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4858 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4859 TemplateNameMap formTemplateNames;
4860 if (templateNames.empty())
4861 {
4862 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4863 }
4864 BaseInfo tbi = bi;
4865 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4866 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4867
4868 if (mode==DocumentedOnly)
4869 {
4870 // find a documented base class in the correct scope
4871 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4872 {
4873 // 1.8.2: decided to show inheritance relations even if not documented,
4874 // we do make them artificial, so they do not appear in the index
4875 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4876 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4877 //{
4878 // no documented base class -> try to find an undocumented one
4879 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4880 //}
4881 }
4882 }
4883 else if (mode==TemplateInstances)
4884 {
4885 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4886 }
4887 }
4888}
4889
4890//----------------------------------------------------------------------
4891
4892static void findTemplateInstanceRelation(const Entry *root,
4893 Definition *context,
4894 ClassDefMutable *templateClass,const QCString &templSpec,
4895 const TemplateNameMap &templateNames,
4896 bool isArtificial)
4897{
4898 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4899 templateClass->name(),templSpec,isArtificial);
4900
4901 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4902 bool existingClass = templSpec==tempArgsStr;
4903 if (existingClass) return; // avoid recursion
4904
4905 bool freshInstance=FALSE;
4906 ClassDefMutable *instanceClass = toClassDefMutable(
4907 templateClass->insertTemplateInstance(
4908 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4909 if (instanceClass)
4910 {
4911 if (freshInstance)
4912 {
4913 instanceClass->setArtificial(TRUE);
4914 instanceClass->setLanguage(root->lang);
4915
4916 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4917 instanceClass->setTemplateBaseClassNames(templateNames);
4918
4919 // search for new template instances caused by base classes of
4920 // instanceClass
4921 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4922 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4923 {
4924 const Entry *templateRoot = it->second;
4925 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4926 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4927 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4928 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4929
4930 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4931 isArtificial,templArgs.get(),templateNames);
4932 }
4933 }
4934 else
4935 {
4936 AUTO_TRACE_ADD("instance already exists");
4937 }
4938 }
4939}
4940
4941//----------------------------------------------------------------------
4942
4943static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4944{
4945 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4946 // be used to match arguments (see issue #11111)
4947 AUTO_TRACE();
4948 QCString ttype = md->typeString();
4949 ttype.stripPrefix("typedef ");
4950 int ti=ttype.find('<');
4951 if (ti!=-1)
4952 {
4953 QCString templateClassName = ttype.left(ti);
4954 SymbolResolver resolver(root->fileDef());
4955 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4956 templateClassName, true, true);
4957 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4958 if (baseClass)
4959 {
4960 const ArgumentList &tl = baseClass->templateArguments();
4961 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4963 baseClass,
4964 ttype.mid(ti),
4965 templateNames,
4966 baseClass->isArtificial());
4967 }
4968 }
4969}
4970
4971//----------------------------------------------------------------------
4972
4973static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4974{
4975 QCString n=name;
4976 int index=n.find('<');
4977 if (index!=-1)
4978 {
4979 n=n.left(index);
4980 }
4981 bool result = rightScopeMatch(scope,n);
4982 return result;
4983}
4984
4986{
4987 if (name.isEmpty()) return 0;
4988 int l = static_cast<int>(name.length());
4989 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4990 {
4991 int count=1;
4992 int i=l-2;
4993 char insideQuote=0;
4994 while (count>0 && i>=0)
4995 {
4996 char c = name[i--];
4997 switch (c)
4998 {
4999 case '>': if (!insideQuote) count++; break;
5000 case '<': if (!insideQuote) count--; break;
5001 case '\'': if (!insideQuote) insideQuote=c;
5002 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5003 break;
5004 case '"': if (!insideQuote) insideQuote=c;
5005 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5006 break;
5007 default: break;
5008 }
5009 }
5010 if (i>=0) l=i+1;
5011 }
5012 return l;
5013}
5014
5016 const Entry *root,
5017 Definition *context,
5018 ClassDefMutable *cd,
5019 const BaseInfo *bi,
5020 const TemplateNameMap &templateNames,
5022 bool isArtificial
5023 )
5024{
5025 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
5026
5027 QCString biName=bi->name;
5028 bool explicitGlobalScope=FALSE;
5029 if (biName.startsWith("::")) // explicit global scope
5030 {
5031 biName=biName.right(biName.length()-2);
5032 explicitGlobalScope=TRUE;
5033 }
5034
5035 Entry *parentNode=root->parent();
5036 bool lastParent=FALSE;
5037 do // for each parent scope, starting with the largest scope
5038 // (in case of nested classes)
5039 {
5040 QCString scopeName= parentNode ? parentNode->name : QCString();
5041 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5042 do // try all parent scope prefixes, starting with the largest scope
5043 {
5044 //printf("scopePrefix='%s' biName='%s'\n",
5045 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5046
5047 QCString baseClassName=biName;
5048 if (scopeOffset>0)
5049 {
5050 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5051 }
5052 if (root->lang==SrcLangExt::CSharp)
5053 {
5054 baseClassName = mangleCSharpGenericName(baseClassName);
5055 }
5056 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5057 SymbolResolver resolver(cd->getFileDef());
5058 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5059 baseClassName,
5060 mode==Undocumented,
5061 true
5062 );
5063 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5064 QCString templSpec = resolver.getTemplateSpec();
5065 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5066 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5067 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5068 // cd ? qPrint(cd->name()):"<none>",
5069 // qPrint(baseClassName),
5070 // baseClass?qPrint(baseClass->name()):"<none>",
5071 // qPrint(templSpec)
5072 // );
5073 //if (baseClassName.left(root->name.length())!=root->name ||
5074 // baseClassName.at(root->name.length())!='<'
5075 // ) // Check for base class with the same name.
5076 // // If found then look in the outer scope for a match
5077 // // and prevent recursion.
5078 if (!isRecursiveBaseClass(root->name,baseClassName)
5079 || explicitGlobalScope
5080 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5081 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5082 || (root->lang==SrcLangExt::IDL &&
5083 (root->section.isExportedInterface() ||
5084 root->section.isIncludedService()))
5085 )
5086 {
5087 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5088 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5089
5090 int i=findTemplateSpecializationPosition(baseClassName);
5091 int si=baseClassName.findRev("::",i);
5092 if (si==-1) si=0;
5093 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5094 // base class has template specifiers
5095 {
5096 // TODO: here we should try to find the correct template specialization
5097 // but for now, we only look for the unspecialized base class.
5098 int e=findEndOfTemplate(baseClassName,i+1);
5099 //printf("baseClass==0 i=%d e=%d\n",i,e);
5100 if (e!=-1) // end of template was found at e
5101 {
5102 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5103 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
5104 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5105 baseClassName,
5106 mode==Undocumented,
5107 true
5108 );
5109 baseClassTypeDef = resolver.getTypedef();
5110 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5111 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5112 }
5113 }
5114 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
5115 // know it is a template, so see if
5116 // we can also link to the explicit
5117 // instance (for instance if a class
5118 // derived from a template argument)
5119 {
5120 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5121 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5122 if (templClass)
5123 {
5124 // use the template instance instead of the template base.
5125 baseClass = templClass;
5126 templSpec.clear();
5127 }
5128 }
5129
5130 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5131 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5132 AUTO_TRACE_ADD("1. found={}",found);
5133 if (!found && si!=-1)
5134 {
5135 // replace any namespace aliases
5136 replaceNamespaceAliases(baseClassName);
5137 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5138 baseClassName,
5139 mode==Undocumented,
5140 true
5141 );
5142 baseClassTypeDef = resolver.getTypedef();
5143 found=baseClass!=nullptr && baseClass!=cd;
5144 if (found) templSpec = resolver.getTemplateSpec();
5145 }
5146 AUTO_TRACE_ADD("2. found={}",found);
5147
5148 if (!found)
5149 {
5150 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5151 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5152 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5153 found = baseClass!=nullptr && baseClass!=cd;
5154
5155 }
5156 AUTO_TRACE_ADD("3. found={}",found);
5157 if (!found)
5158 {
5159 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5160 // the class name also in the alias mapping.
5161 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5162 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5163 {
5164 baseClass=getClassMutable(it->second.alias);
5165 found = baseClass!=nullptr && baseClass!=cd;
5166 }
5167 }
5168 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5169
5170 AUTO_TRACE_ADD("4. found={}",found);
5171 if (found)
5172 {
5173 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5174 // add base class to this class
5175
5176 // if templSpec is not empty then we should "instantiate"
5177 // the template baseClass. A new ClassDef should be created
5178 // to represent the instance. To be able to add the (instantiated)
5179 // members and documentation of a template class
5180 // (inserted in that template class at a later stage),
5181 // the template should know about its instances.
5182 // the instantiation process, should be done in a recursive way,
5183 // since instantiating a template may introduce new inheritance
5184 // relations.
5185 if (!templSpec.isEmpty() && mode==TemplateInstances)
5186 {
5187 // if baseClass is actually a typedef then we should not
5188 // instantiate it, since typedefs are in a different namespace
5189 // see bug531637 for an example where this would otherwise hang
5190 // Doxygen
5191 if (baseClassTypeDef==nullptr)
5192 {
5193 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5194 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5195 }
5196 }
5197 else if (mode==DocumentedOnly || mode==Undocumented)
5198 {
5199 //printf(" => insert base class\n");
5200 QCString usedName;
5201 if (baseClassTypeDef)
5202 {
5203 usedName=biName;
5204 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5205 }
5206 Protection prot = bi->prot;
5207 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5208 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5209 {
5210 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5211 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5212 // add this class as super class to the base class
5213 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5214 }
5215 else
5216 {
5217 warn(root->fileName,root->startLine,
5218 "Detected potential recursive class relation "
5219 "between class {} and base class {}!",
5220 cd->name(),baseClass->name()
5221 );
5222 }
5223 }
5224 return TRUE;
5225 }
5226 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5227 {
5228 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5229 biName,baseClassName,templSpec,isArtificial);
5230 baseClass=nullptr;
5231 if (isATemplateArgument)
5232 {
5233 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5234 if (baseClass==nullptr) // not found (or alias)
5235 {
5236 baseClass= toClassDefMutable(
5237 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5238 createClassDef(root->fileName,root->startLine,root->startColumn,
5239 baseClassName,
5240 ClassDef::Class)));
5241 if (baseClass) // really added (not alias)
5242 {
5243 if (isArtificial) baseClass->setArtificial(TRUE);
5244 baseClass->setLanguage(root->lang);
5245 }
5246 }
5247 }
5248 else
5249 {
5250 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5251 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5252 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5253 if (baseClass==nullptr) // not found (or alias)
5254 {
5255 baseClass = toClassDefMutable(
5256 Doxygen::classLinkedMap->add(baseClassName,
5257 createClassDef(root->fileName,root->startLine,root->startColumn,
5258 baseClassName,
5259 ClassDef::Class)));
5260 if (baseClass) // really added (not alias)
5261 {
5262 if (isArtificial) baseClass->setArtificial(TRUE);
5263 baseClass->setLanguage(root->lang);
5264 si = baseClassName.findRev("::");
5265 if (si!=-1) // class is nested
5266 {
5267 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5268 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5269 {
5270 baseClass->setArtificial(TRUE); // see bug678139
5271 }
5272 }
5273 }
5274 }
5275 }
5276 if (baseClass)
5277 {
5278 if (biName.endsWith("-p"))
5279 {
5280 biName="<"+biName.left(biName.length()-2)+">";
5281 }
5282 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5283 {
5284 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5285 // add base class to this class
5286 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5287 // add this class as super class to the base class
5288 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5289 }
5290 // the undocumented base was found in this file
5291 baseClass->insertUsedFile(root->fileDef());
5292
5293 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5294 if (scope!=baseClass)
5295 {
5296 baseClass->setOuterScope(scope);
5297 }
5298
5299 if (baseClassName.endsWith("-p"))
5300 {
5302 }
5303 return TRUE;
5304 }
5305 else
5306 {
5307 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5308 }
5309 }
5310 else
5311 {
5312 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5313 }
5314 }
5315 else
5316 {
5317 if (mode!=TemplateInstances)
5318 {
5319 warn(root->fileName,root->startLine,
5320 "Detected potential recursive class relation "
5321 "between class {} and base class {}!",
5322 root->name,baseClassName
5323 );
5324 }
5325 // for mode==TemplateInstance this case is quite common and
5326 // indicates a relation between a template class and a template
5327 // instance with the same name.
5328 }
5329 if (scopeOffset==0)
5330 {
5331 scopeOffset=-1;
5332 }
5333 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5334 {
5335 scopeOffset=0;
5336 }
5337 //printf("new scopeOffset='%d'",scopeOffset);
5338 } while (scopeOffset>=0);
5339
5340 if (parentNode==nullptr)
5341 {
5342 lastParent=TRUE;
5343 }
5344 else
5345 {
5346 parentNode=parentNode->parent();
5347 }
5348 } while (lastParent);
5349
5350 return FALSE;
5351}
5352
5353//----------------------------------------------------------------------
5354// Computes the base and super classes for each class in the tree
5355
5356static bool isClassSection(const Entry *root)
5357{
5358 if ( !root->name.isEmpty() )
5359 {
5360 if (root->section.isCompound())
5361 // is it a compound (class, struct, union, interface ...)
5362 {
5363 return TRUE;
5364 }
5365 else if (root->section.isCompoundDoc())
5366 // is it a documentation block with inheritance info.
5367 {
5368 bool hasExtends = !root->extends.empty();
5369 if (hasExtends) return TRUE;
5370 }
5371 }
5372 return FALSE;
5373}
5374
5375
5376/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5377 */
5378static void findClassEntries(const Entry *root)
5379{
5380 if (isClassSection(root))
5381 {
5382 g_classEntries.emplace(root->name.str(),root);
5383 }
5384 for (const auto &e : root->children()) findClassEntries(e.get());
5385}
5386
5388{
5389 // strip any anonymous scopes first
5392 int i=0;
5393 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5394 (i=bName.find('<'))!=-1)
5395 {
5396 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5397 // template part before looking for matches
5398 if (root->lang==SrcLangExt::CSharp)
5399 {
5400 bName = mangleCSharpGenericName(root->name);
5401 }
5402 else
5403 {
5404 bName = bName.left(i);
5405 }
5406 }
5407 return bName;
5408}
5409
5410/*! Using the dictionary build by findClassEntries(), this
5411 * function will look for additional template specialization that
5412 * exists as inheritance relations only. These instances will be
5413 * added to the template they are derived from.
5414 */
5416{
5417 AUTO_TRACE();
5418 ClassDefSet visitedClasses;
5419 for (const auto &[name,root] : g_classEntries)
5420 {
5421 QCString bName = extractClassName(root);
5422 ClassDefMutable *cdm = getClassMutable(bName);
5423 if (cdm)
5424 {
5426 }
5427 }
5428}
5429
5431{
5432 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5433 int i = root->name.find('<');
5434 int j = root->name.findRev('>');
5435 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5436 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5437 {
5438 ClassDefMutable *master = getClassMutable(root->name.left(i));
5439 if (master && master!=cd && !cd->templateMaster())
5440 {
5441 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5442 cd->setTemplateMaster(master);
5443 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5444 }
5445 }
5446}
5447
5449{
5450 AUTO_TRACE();
5451 for (const auto &[name,root] : g_classEntries)
5452 {
5453 QCString bName = extractClassName(root);
5454 ClassDefMutable *cdm = getClassMutable(bName);
5455 if (cdm)
5456 {
5457 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5459 cdm->addTypeConstraints();
5460 }
5461 }
5462}
5463
5465{
5466 AUTO_TRACE();
5467 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5468 {
5469 if (!nd->hasDocumentation())
5470 {
5471 if ((guessSection(nd->getDefFileName()).isHeader() ||
5472 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5473 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5474 )
5475 {
5476 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5477 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5478 nd->name());
5479 }
5480 }
5481 }
5482}
5483
5485{
5486 AUTO_TRACE();
5487 for (const auto &[name,root] : g_classEntries)
5488 {
5489 QCString bName = extractClassName(root);
5490 ClassDefMutable *cd = getClassMutable(bName);
5491 if (cd)
5492 {
5494 }
5495 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5496 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5497 {
5498 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5499 (guessSection(root->fileName).isHeader() ||
5500 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5501 protectionLevelVisible(root->protection) && // hidden by protection
5502 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5503 )
5504 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5505 }
5506 }
5507}
5508
5510{
5511 AUTO_TRACE();
5512 for (const auto &[name,root] : g_classEntries)
5513 {
5517 // strip any anonymous scopes first
5518 if (cd && !cd->getTemplateInstances().empty())
5519 {
5520 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5521 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5522 {
5523 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5524 if (tcd)
5525 {
5526 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5527 QCString templSpec = ti.templSpec;
5528 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5529 for (const BaseInfo &bi : root->extends)
5530 {
5531 // check if the base class is a template argument
5532 BaseInfo tbi = bi;
5533 const ArgumentList &tl = cd->templateArguments();
5534 if (!tl.empty())
5535 {
5536 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5537 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5538 // for each template name that we inherit from we need to
5539 // substitute the formal with the actual arguments
5540 TemplateNameMap actualTemplateNames;
5541 for (const auto &tn_kv : templateNames)
5542 {
5543 size_t templIndex = tn_kv.second;
5544 Argument actArg;
5545 bool hasActArg=FALSE;
5546 if (templIndex<templArgs->size())
5547 {
5548 actArg=templArgs->at(templIndex);
5549 hasActArg=TRUE;
5550 }
5551 if (hasActArg &&
5552 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5553 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5554 )
5555 {
5556 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5557 }
5558 }
5559
5560 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5561 // find a documented base class in the correct scope
5562 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5563 {
5564 // no documented base class -> try to find an undocumented one
5565 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5566 }
5567 }
5568 }
5569 }
5570 }
5571 }
5572 }
5573}
5574
5575//-----------------------------------------------------------------------
5576// compute the references (anchors in HTML) for each function in the file
5577
5579{
5580 AUTO_TRACE();
5581 for (const auto &cd : *Doxygen::classLinkedMap)
5582 {
5583 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5584 if (cdm)
5585 {
5586 cdm->computeAnchors();
5587 }
5588 }
5589 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5590 {
5591 for (const auto &fd : *fn)
5592 {
5593 fd->computeAnchors();
5594 }
5595 }
5596 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5597 {
5599 if (ndm)
5600 {
5601 ndm->computeAnchors();
5602 }
5603 }
5604 for (const auto &gd : *Doxygen::groupLinkedMap)
5605 {
5606 gd->computeAnchors();
5607 }
5608}
5609
5610//----------------------------------------------------------------------
5611
5612
5613template<typename Func>
5614static void applyToAllDefinitions(Func func)
5615{
5616 for (const auto &cd : *Doxygen::classLinkedMap)
5617 {
5618 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5619 if (cdm)
5620 {
5621 func(cdm);
5622 }
5623 }
5624
5625 for (const auto &cd : *Doxygen::conceptLinkedMap)
5626 {
5627 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5628 if (cdm)
5629 {
5630 func(cdm);
5631 }
5632 }
5633
5634 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5635 {
5636 for (const auto &fd : *fn)
5637 {
5638 func(fd.get());
5639 }
5640 }
5641
5642 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5643 {
5645 if (ndm)
5646 {
5647 func(ndm);
5648 }
5649 }
5650
5651 for (const auto &gd : *Doxygen::groupLinkedMap)
5652 {
5653 func(gd.get());
5654 }
5655
5656 for (const auto &pd : *Doxygen::pageLinkedMap)
5657 {
5658 func(pd.get());
5659 }
5660
5661 for (const auto &dd : *Doxygen::dirLinkedMap)
5662 {
5663 func(dd.get());
5664 }
5665
5666 func(&ModuleManager::instance());
5667}
5668
5669//----------------------------------------------------------------------
5670
5672{
5673 AUTO_TRACE();
5674 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5675}
5676
5677//----------------------------------------------------------------------
5678
5680{
5681 AUTO_TRACE();
5682 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5683}
5684
5685
5686//----------------------------------------------------------------------
5687
5689{
5690 AUTO_TRACE();
5692 {
5693 rl->generatePage();
5694 }
5695}
5696
5697//----------------------------------------------------------------------
5698// Copy the documentation in entry 'root' to member definition 'md' and
5699// set the function declaration of the member to 'funcDecl'. If the boolean
5700// over_load is set the standard overload text is added.
5701
5702static void addMemberDocs(const Entry *root,
5703 MemberDefMutable *md, const QCString &funcDecl,
5704 const ArgumentList *al,
5705 bool over_load,
5706 TypeSpecifier spec
5707 )
5708{
5709 if (md==nullptr) return;
5710 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5711 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5712 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5713 {
5714 QCString fDecl=funcDecl;
5715 // strip extern specifier
5716 fDecl.stripPrefix("extern ");
5717 md->setDefinition(fDecl);
5718 }
5720 md->addQualifiers(root->qualifiers);
5722 const NamespaceDef *nd=md->getNamespaceDef();
5723 QCString fullName;
5724 if (cd)
5725 fullName = cd->name();
5726 else if (nd)
5727 fullName = nd->name();
5728
5729 if (!fullName.isEmpty()) fullName+="::";
5730 fullName+=md->name();
5731 FileDef *rfd=root->fileDef();
5732
5733 // TODO determine scope based on root not md
5734 Definition *rscope = md->getOuterScope();
5735
5736 const ArgumentList &mdAl = md->argumentList();
5737 if (al)
5738 {
5739 ArgumentList mergedAl = *al;
5740 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5741 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5742 }
5743 else
5744 {
5745 if (
5746 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5747 rscope,rfd,root->type,&root->argList,
5748 TRUE, root->lang
5749 )
5750 )
5751 {
5752 //printf("merging arguments (2)\n");
5753 ArgumentList mergedArgList = root->argList;
5754 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5755 }
5756 }
5757 if (over_load) // the \overload keyword was used
5758 {
5760 if (!root->doc.isEmpty())
5761 {
5762 doc+="<p>";
5763 doc+=root->doc;
5764 }
5765 md->setDocumentation(doc,root->docFile,root->docLine);
5767 md->setDocsForDefinition(!root->proto);
5768 }
5769 else
5770 {
5771 //printf("overwrite!\n");
5772 md->setDocumentation(root->doc,root->docFile,root->docLine);
5773 md->setDocsForDefinition(!root->proto);
5774
5775 //printf("overwrite!\n");
5776 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5777
5778 if (
5779 (md->inbodyDocumentation().isEmpty() ||
5780 !root->parent()->name.isEmpty()
5781 ) && !root->inbodyDocs.isEmpty()
5782 )
5783 {
5785 }
5786 }
5787
5788 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5789 // qPrint(md->initializer()),md->initializer().isEmpty(),
5790 // qPrint(root->initializer),root->initializer.isEmpty()
5791 // );
5792 std::string rootInit = root->initializer.str();
5793 if (md->initializer().isEmpty() && !rootInit.empty())
5794 {
5795 //printf("setInitializer\n");
5796 md->setInitializer(rootInit);
5797 }
5798 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5799 {
5800 md->setRequiresClause(root->req);
5801 }
5802
5803 md->setMaxInitLines(root->initLines);
5804
5805 if (rfd)
5806 {
5807 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5808 )
5809 {
5810 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5811 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5812 md->setBodyDef(rfd);
5813 }
5814
5815 md->setRefItems(root->sli);
5816 md->setRequirementReferences(root->rqli);
5817 }
5818
5820 md->addQualifiers(root->qualifiers);
5821
5822 md->mergeMemberSpecifiers(spec);
5824 addMemberToGroups(root,md);
5826 if (cd) cd->insertUsedFile(rfd);
5827 //printf("root->mGrpId=%d\n",root->mGrpId);
5828 if (root->mGrpId!=-1)
5829 {
5830 if (md->getMemberGroupId()!=-1)
5831 {
5832 if (md->getMemberGroupId()!=root->mGrpId)
5833 {
5834 warn(root->fileName,root->startLine,
5835 "member {} belongs to two different groups. The second one found here will be ignored.",
5836 md->name()
5837 );
5838 }
5839 }
5840 else // set group id
5841 {
5842 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5843 md->setMemberGroupId(root->mGrpId);
5844 }
5845 }
5846 md->addQualifiers(root->qualifiers);
5847}
5848
5849//----------------------------------------------------------------------
5850// find a class definition given the scope name and (optionally) a
5851// template list specifier
5852
5854 const QCString &scopeName)
5855{
5856 SymbolResolver resolver(fd);
5857 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5858 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5859 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5860 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5861 return tcd;
5862}
5863
5864//----------------------------------------------------------------------------
5865// Returns TRUE, if the entry belongs to the group of the member definition,
5866// otherwise FALSE.
5867
5868static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5869{
5870 const GroupDef *gd = md->getGroupDef();
5871 if (!gd)
5872 {
5873 return allowNoGroup;
5874 }
5875
5876 for (const auto &g : root->groups)
5877 {
5878 if (g.groupname == gd->name())
5879 {
5880 return true; // matching group
5881 }
5882 }
5883
5884 return false;
5885}
5886
5887//----------------------------------------------------------------------
5888// Adds the documentation contained in 'root' to a global function
5889// with name 'name' and argument list 'args' (for overloading) and
5890// function declaration 'decl' to the corresponding member definition.
5891
5892static bool findGlobalMember(const Entry *root,
5893 const QCString &namespaceName,
5894 const QCString &type,
5895 const QCString &name,
5896 const QCString &tempArg,
5897 const QCString &,
5898 const QCString &decl,
5899 TypeSpecifier /* spec */)
5900{
5901 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5902 QCString n=name;
5903 if (n.isEmpty()) return FALSE;
5904 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5905 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5906 if (mn==nullptr)
5907 {
5908 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5909 }
5910 if (mn) // function name defined
5911 {
5912 AUTO_TRACE_ADD("Found symbol name");
5913 //int count=0;
5914 bool found=FALSE;
5915 for (const auto &md : *mn)
5916 {
5917 // If the entry has groups, then restrict the search to members which are
5918 // in one of the groups of the entry. If md is not associated with a group yet,
5919 // allow this documentation entry to add the group info.
5920 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5921 {
5922 continue;
5923 }
5924
5925 const NamespaceDef *nd=nullptr;
5926 if (md->isAlias() && md->getOuterScope() &&
5927 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5928 {
5929 nd = toNamespaceDef(md->getOuterScope());
5930 }
5931 else
5932 {
5933 nd = md->getNamespaceDef();
5934 }
5935
5936 // special case for strong enums
5937 int enumNamePos=0;
5938 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5939 { // md part of a strong enum in a namespace?
5940 QCString enumName = namespaceName.mid(enumNamePos+2);
5941 if (namespaceName.left(enumNamePos)==nd->name())
5942 {
5943 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5944 if (enumMn)
5945 {
5946 for (const auto &emd : *enumMn)
5947 {
5948 found = emd->isStrong() && md->getEnumScope()==emd.get();
5949 if (found)
5950 {
5951 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5952 break;
5953 }
5954 }
5955 }
5956 }
5957 if (found)
5958 {
5959 break;
5960 }
5961 }
5962 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5963 {
5964 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5965 if (enumMn)
5966 {
5967 for (const auto &emd : *enumMn)
5968 {
5969 found = emd->isStrong() && md->getEnumScope()==emd.get();
5970 if (found)
5971 {
5972 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5973 break;
5974 }
5975 }
5976 }
5977 }
5978
5979 const FileDef *fd=root->fileDef();
5980 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5982 if (fd)
5983 {
5984 nl = fd->getUsedNamespaces();
5985 }
5986 //printf("NamespaceList %p\n",nl);
5987
5988 // search in the list of namespaces that are imported via a
5989 // using declaration
5990 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5991
5992 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5993 (nd && nd->name()==namespaceName) || // or in the same namespace
5994 viaUsingDirective // member in 'using' namespace
5995 )
5996 {
5997 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5998
5999 NamespaceDef *rnd = nullptr;
6000 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
6001
6002 const ArgumentList &mdAl = md.get()->argumentList();
6003 bool matching=
6004 (mdAl.empty() && root->argList.empty()) ||
6005 md->isVariable() || md->isTypedef() || /* in case of function pointers */
6006 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
6007 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
6008 FALSE,root->lang);
6009
6010 // for template members we need to check if the number of
6011 // template arguments is the same, otherwise we are dealing with
6012 // different functions.
6013 if (matching && !root->tArgLists.empty())
6014 {
6015 const ArgumentList &mdTempl = md->templateArguments();
6016 if (root->tArgLists.back().size()!=mdTempl.size())
6017 {
6018 matching=FALSE;
6019 }
6020 }
6021
6022 //printf("%s<->%s\n",
6023 // qPrint(argListToString(md->argumentList())),
6024 // qPrint(argListToString(root->argList)));
6025
6026 // For static members we also check if the comment block was found in
6027 // the same file. This is needed because static members with the same
6028 // name can be in different files. Thus it would be wrong to just
6029 // put the comment block at the first syntactically matching member. If
6030 // the comment block belongs to a group of the static member, then add
6031 // the documentation even if it is in a different file.
6032 if (matching && md->isStatic() &&
6033 md->getDefFileName()!=root->fileName &&
6034 mn->size()>1 &&
6035 !isEntryInGroupOfMember(root,md.get()))
6036 {
6037 matching = FALSE;
6038 }
6039
6040 // for template member we also need to check the return type and requires
6041 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6042 {
6043 //printf("Comparing return types '%s'<->'%s'\n",
6044 // md->typeString(),type);
6045 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6046 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6047 md->typeString()!=type ||
6048 md->requiresClause()!=root->req)
6049 {
6050 //printf(" ---> no matching\n");
6051 matching = FALSE;
6052 }
6053 }
6054
6055 if (matching) // add docs to the member
6056 {
6057 AUTO_TRACE_ADD("Match found");
6058 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
6059 found=TRUE;
6060 break;
6061 }
6062 }
6063 }
6064 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6065 {
6066 QCString fullFuncDecl=decl;
6067 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
6068 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6069 if (mn->size()>0)
6070 {
6071 warnMsg+="\nPossible candidates:";
6072 for (const auto &md : *mn)
6073 {
6074 warnMsg+="\n '";
6075 warnMsg+=replaceAnonymousScopes(md->declaration());
6076 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6077 }
6078 }
6079 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6080 }
6081 }
6082 else // got docs for an undefined member!
6083 {
6084 if (root->type!="friend class" &&
6085 root->type!="friend struct" &&
6086 root->type!="friend union" &&
6087 root->type!="friend" &&
6088 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6089 root->type.find("typedef ")==-1)
6090 )
6091 {
6092 warn(root->fileName,root->startLine,
6093 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6094 );
6095 }
6096 }
6097 return TRUE;
6098}
6099
6101 const ArgumentLists &srcTempArgLists,
6102 const ArgumentLists &dstTempArgLists
6103 )
6104{
6105 auto srcIt = srcTempArgLists.begin();
6106 auto dstIt = dstTempArgLists.begin();
6107 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6108 {
6109 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
6110 ++srcIt;
6111 ++dstIt;
6112 }
6113 return FALSE;
6114}
6115
6116static bool scopeIsTemplate(const Definition *d)
6117{
6118 bool result=FALSE;
6119 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6121 {
6122 auto cd = toClassDef(d);
6123 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6125 }
6126 //printf("< scopeIsTemplate=%d\n",result);
6127 return result;
6128}
6129
6131 const ArgumentLists &srcTempArgLists,
6132 const ArgumentLists &dstTempArgLists,
6133 const std::string &src
6134 )
6135{
6136 std::string dst;
6137 static const reg::Ex re(R"(\a\w*)");
6138 reg::Iterator it(src,re);
6140 //printf("type=%s\n",qPrint(sa->type));
6141 size_t p=0;
6142 for (; it!=end ; ++it) // for each word in srcType
6143 {
6144 const auto &match = *it;
6145 size_t i = match.position();
6146 size_t l = match.length();
6147 bool found=FALSE;
6148 dst+=src.substr(p,i-p);
6149 std::string name=match.str();
6150
6151 auto srcIt = srcTempArgLists.begin();
6152 auto dstIt = dstTempArgLists.begin();
6153 while (srcIt!=srcTempArgLists.end() && !found)
6154 {
6155 const ArgumentList *tdAli = nullptr;
6156 std::vector<Argument>::const_iterator tdaIt;
6157 if (dstIt!=dstTempArgLists.end())
6158 {
6159 tdAli = &(*dstIt);
6160 tdaIt = tdAli->begin();
6161 ++dstIt;
6162 }
6163
6164 const ArgumentList &tsaLi = *srcIt;
6165 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6166 {
6167 Argument tsa = *tsaIt;
6168 const Argument *tda = nullptr;
6169 if (tdAli && tdaIt!=tdAli->end())
6170 {
6171 tda = &(*tdaIt);
6172 ++tdaIt;
6173 }
6174 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6175 // qPrint(tsa.type),qPrint(tsa.name),
6176 // qPrint(tda->type),qPrint(tda->name));
6177 if (name==tsa.name.str())
6178 {
6179 if (tda && tda->name.isEmpty())
6180 {
6181 QCString tdaName = tda->name;
6182 QCString tdaType = tda->type;
6183 int vc=0;
6184 if (tdaType.startsWith("class ")) vc=6;
6185 else if (tdaType.startsWith("typename ")) vc=9;
6186 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6187 {
6188 tdaName = tdaType.mid(vc);
6189 }
6190 if (!tdaName.isEmpty())
6191 {
6192 name=tdaName.str(); // substitute
6193 found=TRUE;
6194 }
6195 }
6196 }
6197 }
6198
6199 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6200 // qPrint(argListToString(srclali.current())),
6201 // qPrint(argListToString(dstlali.current())),
6202 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6203 ++srcIt;
6204 }
6205 dst+=name;
6206 p=i+l;
6207 }
6208 dst+=src.substr(p);
6209 //printf(" substituteTemplatesInString(%s)=%s\n",
6210 // qPrint(src),qPrint(dst));
6211 return dst;
6212}
6213
6215 const ArgumentLists &srcTempArgLists,
6216 const ArgumentLists &dstTempArgLists,
6217 const ArgumentList &src,
6218 ArgumentList &dst
6219 )
6220{
6221 auto dstIt = dst.begin();
6222 for (const Argument &sa : src)
6223 {
6224 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6225 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6226 if (dstIt == dst.end())
6227 {
6228 Argument da = sa;
6229 da.type = dstType;
6230 da.array = dstArray;
6231 dst.push_back(da);
6232 dstIt = dst.end();
6233 }
6234 else
6235 {
6236 Argument da = *dstIt;
6237 da.type = dstType;
6238 da.array = dstArray;
6239 ++dstIt;
6240 }
6241 }
6246 srcTempArgLists,dstTempArgLists,
6247 src.trailingReturnType().str()));
6248 dst.setIsDeleted(src.isDeleted());
6249 dst.setRefQualifier(src.refQualifier());
6250 dst.setNoParameters(src.noParameters());
6251 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6252 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6253 // );
6254}
6255
6256//-------------------------------------------------------------------------------------------
6257
6258static void addLocalObjCMethod(const Entry *root,
6259 const QCString &scopeName,
6260 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6261 const QCString &exceptions,const QCString &funcDecl,
6262 TypeSpecifier spec)
6263{
6264 AUTO_TRACE();
6265 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6266 ClassDefMutable *cd=nullptr;
6267 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6268 {
6269 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6270 auto md = createMemberDef(
6271 root->fileName,root->startLine,root->startColumn,
6272 funcType,funcName,funcArgs,exceptions,
6273 root->protection,root->virt,root->isStatic,Relationship::Member,
6274 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6275 auto mmd = toMemberDefMutable(md.get());
6276 mmd->setTagInfo(root->tagInfo());
6277 mmd->setLanguage(root->lang);
6278 mmd->setId(root->id);
6279 mmd->makeImplementationDetail();
6280 mmd->setMemberClass(cd);
6281 mmd->setDefinition(funcDecl);
6283 mmd->addQualifiers(root->qualifiers);
6284 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6285 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6286 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6287 mmd->setDocsForDefinition(!root->proto);
6288 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6289 mmd->addSectionsToDefinition(root->anchors);
6290 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6291 FileDef *fd=root->fileDef();
6292 mmd->setBodyDef(fd);
6293 mmd->setMemberSpecifiers(spec);
6294 mmd->setVhdlSpecifiers(root->vhdlSpec);
6295 mmd->setMemberGroupId(root->mGrpId);
6296 cd->insertMember(md.get());
6297 cd->insertUsedFile(fd);
6298 mmd->setRefItems(root->sli);
6299 mmd->setRequirementReferences(root->rqli);
6300
6302 mn->push_back(std::move(md));
6303 }
6304 else
6305 {
6306 // local objective C method found for class without interface
6307 }
6308}
6309
6310//-------------------------------------------------------------------------------------------
6311
6312static void addMemberFunction(const Entry *root,
6313 MemberName *mn,
6314 const QCString &scopeName,
6315 const QCString &namespaceName,
6316 const QCString &className,
6317 const QCString &funcTyp,
6318 const QCString &funcName,
6319 const QCString &funcArgs,
6320 const QCString &funcTempList,
6321 const QCString &exceptions,
6322 const QCString &type,
6323 const QCString &args,
6324 bool isFriend,
6325 TypeSpecifier spec,
6326 const QCString &relates,
6327 const QCString &funcDecl,
6328 bool overloaded,
6329 bool isFunc)
6330{
6331 AUTO_TRACE();
6332 QCString funcType = funcTyp;
6333 int count=0;
6334 int noMatchCount=0;
6335 bool memFound=FALSE;
6336 for (const auto &imd : *mn)
6337 {
6338 MemberDefMutable *md = toMemberDefMutable(imd.get());
6339 if (md==nullptr) continue;
6341 if (cd==nullptr) continue;
6342 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6343 // scopeName, cd->name(), md->argsString(), root->fileName);
6344 FileDef *fd=root->fileDef();
6345 NamespaceDef *nd=nullptr;
6346 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6347
6348 //printf("scopeName %s->%s\n",qPrint(scopeName),
6349 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6350
6351 // if the member we are searching for is an enum value that is part of
6352 // a "strong" enum, we need to look into the fields of the enum for a match
6353 int enumNamePos=0;
6354 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6355 {
6356 QCString enumName = className.mid(enumNamePos+2);
6357 QCString fullScope = className.left(enumNamePos);
6358 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6359 if (fullScope==cd->name())
6360 {
6361 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6362 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6363 if (enumMn)
6364 {
6365 for (const auto &emd : *enumMn)
6366 {
6367 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6368 if (memFound)
6369 {
6370 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6371 count++;
6372 }
6373 if (memFound) break;
6374 }
6375 }
6376 }
6377 }
6378 if (memFound) break;
6379
6380 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6381 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6382 {
6383 // don't be fooled by anonymous scopes
6384 tcd=cd;
6385 }
6386 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6387 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6388
6389 if (cd && tcd==cd) // member's classes match
6390 {
6391 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6392
6393 // get the template parameter lists found at the member declaration
6394 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6395 const ArgumentList &templAl = md->templateArguments();
6396 if (!templAl.empty())
6397 {
6398 declTemplArgs.push_back(templAl);
6399 }
6400
6401 // get the template parameter lists found at the member definition
6402 const ArgumentLists &defTemplArgs = root->tArgLists;
6403 //printf("defTemplArgs=%p\n",defTemplArgs);
6404
6405 // do we replace the decl argument lists with the def argument lists?
6406 bool substDone=false;
6407 ArgumentList argList;
6408
6409 /* substitute the occurrences of class template names in the
6410 * argument list before matching
6411 */
6412 const ArgumentList &mdAl = md->argumentList();
6413 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6414 {
6415 /* the function definition has template arguments
6416 * and the class definition also has template arguments, so
6417 * we must substitute the template names of the class by that
6418 * of the function definition before matching.
6419 */
6420 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6421
6422 substDone=TRUE;
6423 }
6424 else /* no template arguments, compare argument lists directly */
6425 {
6426 argList = mdAl;
6427 }
6428
6429 bool matching=
6430 md->isVariable() || md->isTypedef() || // needed for function pointers
6432 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6433 cd,fd,root->type,&root->argList,
6434 TRUE,root->lang);
6435
6436 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6437 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName,matching);
6438
6439 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6440 {
6441 matching = FALSE; // don't match methods and attributes with the same name
6442 }
6443
6444 // for template member we also need to check the return type
6445 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6446 {
6447 QCString memType = md->typeString();
6448 memType.stripPrefix("static "); // see bug700696
6450 className+"::",""); // see bug700693 & bug732594
6452 className+"::",""); // see bug758900
6453 if (memType=="auto" && !argList.trailingReturnType().isEmpty())
6454 {
6455 memType = argList.trailingReturnType();
6456 memType.stripPrefix(" -> ");
6457 }
6458 if (funcType=="auto" && !root->argList.trailingReturnType().isEmpty())
6459 {
6460 funcType = root->argList.trailingReturnType();
6461 funcType.stripPrefix(" -> ");
6463 substDone=true;
6464 }
6465 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6466 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6467 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6468 {
6469 //printf(" ---> no matching\n");
6470 matching = FALSE;
6471 }
6472 }
6473 else if (defTemplArgs.size()>declTemplArgs.size())
6474 {
6475 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6476 // avoid matching a non-template function in a template class against a
6477 // template function with the same name and parameters, see issue #10184
6478 substDone = false;
6479 matching = false;
6480 }
6481 bool rootIsUserDoc = root->section.isMemberDoc();
6482 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6483 bool mdIsTemplate = md->templateArguments().hasParameters();
6484 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6485 bool rootIsTemplate = !root->tArgLists.empty();
6486 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6487 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6488 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6489 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6490 )
6491 {
6492 // Method with template return type does not match method without return type
6493 // even if the parameters are the same. See also bug709052
6494 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6495 matching = FALSE;
6496 }
6497
6498 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6499
6500 if (substDone) // found a new argument list
6501 {
6502 if (matching) // replace member's argument list
6503 {
6505 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6506 }
6507 else // no match
6508 {
6509 if (!funcTempList.isEmpty() &&
6510 isSpecialization(declTemplArgs,defTemplArgs))
6511 {
6512 // check if we are dealing with a partial template
6513 // specialization. In this case we add it to the class
6514 // even though the member arguments do not match.
6515
6516 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6517 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6518 return;
6519 }
6520 }
6521 }
6522 if (matching)
6523 {
6524 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6525 count++;
6526 memFound=TRUE;
6527 }
6528 }
6529 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6530 // but in a different namespace
6531 {
6532 noMatchCount++;
6533 }
6534
6535 if (memFound) break;
6536 }
6537 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6538 {
6539 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6540 return;
6541 }
6542 if (count==0 && !(isFriend && funcType=="class"))
6543 {
6544 int candidates=0;
6545 const ClassDef *ecd = nullptr, *ucd = nullptr;
6546 MemberDef *emd = nullptr, *umd = nullptr;
6547 //printf("Assume template class\n");
6548 for (const auto &md : *mn)
6549 {
6550 MemberDef *cmd=md.get();
6552 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6553 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6554 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6555 {
6556 const ArgumentList &templAl = md->templateArguments();
6557 if (!root->tArgLists.empty() && !templAl.empty() &&
6558 root->tArgLists.back().size()<=templAl.size())
6559 {
6560 AUTO_TRACE_ADD("add template specialization");
6561 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6562 root->protection,root->isStatic,root->virt,spec,relates);
6563 return;
6564 }
6565 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6567 { // exact argument list match -> remember
6568 ucd = ecd = ccd;
6569 umd = emd = cmd;
6570 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6571 className,ccd->name(),md->argsString());
6572 }
6573 else // arguments do not match, but member name and scope do -> remember
6574 {
6575 ucd = ccd;
6576 umd = cmd;
6577 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6578 className,ccd->name(),md->argsString());
6579 }
6580 candidates++;
6581 }
6582 }
6583 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6584 if (!strictProtoMatching)
6585 {
6586 if (candidates==1 && ucd && umd)
6587 {
6588 // we didn't find an actual match on argument lists, but there is only 1 member with this
6589 // name in the same scope, so that has to be the one.
6590 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6591 return;
6592 }
6593 else if (candidates>1 && ecd && emd)
6594 {
6595 // we didn't find a unique match using type resolution,
6596 // but one of the matches has the exact same signature so
6597 // we take that one.
6598 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6599 return;
6600 }
6601 }
6602
6603 QCString warnMsg = "no ";
6604 if (noMatchCount>1) warnMsg+="uniquely ";
6605 warnMsg+="matching class member found for \n";
6606
6607 for (const ArgumentList &al : root->tArgLists)
6608 {
6609 warnMsg+=" template ";
6610 warnMsg+=tempArgListToString(al,root->lang);
6611 warnMsg+='\n';
6612 }
6613
6614 QCString fullFuncDecl=funcDecl;
6615 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6616
6617 warnMsg+=" ";
6618 warnMsg+=fullFuncDecl;
6619
6620 if (candidates>0 || noMatchCount>=1)
6621 {
6622 warnMsg+="\nPossible candidates:";
6623
6624 NamespaceDef *nd=nullptr;
6625 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6626 FileDef *fd=root->fileDef();
6627
6628 for (const auto &md : *mn)
6629 {
6630 const ClassDef *cd=md->getClassDef();
6631 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6632 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6633 {
6634 // don't be fooled by anonymous scopes
6635 tcd=cd;
6636 }
6637 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6638 {
6639 warnMsg+='\n';
6640 const ArgumentList &templAl = md->templateArguments();
6641 warnMsg+=" '";
6642 if (templAl.hasParameters())
6643 {
6644 warnMsg+="template ";
6645 warnMsg+=tempArgListToString(templAl,root->lang);
6646 warnMsg+='\n';
6647 warnMsg+=" ";
6648 }
6649 if (!md->typeString().isEmpty())
6650 {
6651 warnMsg+=md->typeString();
6652 warnMsg+=' ';
6653 }
6655 if (!qScope.isEmpty())
6656 warnMsg+=qScope+"::"+md->name();
6657 warnMsg+=md->argsString();
6658 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6659 }
6660 }
6661 }
6662 warn(root->fileName,root->startLine,"{}",warnMsg);
6663 }
6664}
6665
6666//-------------------------------------------------------------------------------------------
6667
6668static void addMemberSpecialization(const Entry *root,
6669 MemberName *mn,
6670 ClassDefMutable *cd,
6671 const QCString &funcType,
6672 const QCString &funcName,
6673 const QCString &funcArgs,
6674 const QCString &funcDecl,
6675 const QCString &exceptions,
6676 TypeSpecifier spec
6677 )
6678{
6679 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6680 MemberDef *declMd=nullptr;
6681 for (const auto &md : *mn)
6682 {
6683 if (md->getClassDef()==cd)
6684 {
6685 // TODO: we should probably also check for matching arguments
6686 declMd = md.get();
6687 break;
6688 }
6689 }
6690 MemberType mtype=MemberType::Function;
6691 ArgumentList tArgList;
6692 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6693 auto md = createMemberDef(
6694 root->fileName,root->startLine,root->startColumn,
6695 funcType,funcName,funcArgs,exceptions,
6696 declMd ? declMd->protection() : root->protection,
6697 root->virt,root->isStatic,Relationship::Member,
6698 mtype,tArgList,root->argList,root->metaData);
6699 auto mmd = toMemberDefMutable(md.get());
6700 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6701 mmd->setTagInfo(root->tagInfo());
6702 mmd->setLanguage(root->lang);
6703 mmd->setId(root->id);
6704 mmd->setMemberClass(cd);
6705 mmd->setTemplateSpecialization(TRUE);
6706 mmd->setTypeConstraints(root->typeConstr);
6707 mmd->setDefinition(funcDecl);
6709 mmd->addQualifiers(root->qualifiers);
6710 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6711 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6712 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6713 mmd->setDocsForDefinition(!root->proto);
6714 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6715 mmd->addSectionsToDefinition(root->anchors);
6716 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6717 FileDef *fd=root->fileDef();
6718 mmd->setBodyDef(fd);
6719 mmd->setMemberSpecifiers(spec);
6720 mmd->setVhdlSpecifiers(root->vhdlSpec);
6721 mmd->setMemberGroupId(root->mGrpId);
6722 cd->insertMember(md.get());
6723 mmd->setRefItems(root->sli);
6724 mmd->setRequirementReferences(root->rqli);
6725
6726 mn->push_back(std::move(md));
6727}
6728
6729//-------------------------------------------------------------------------------------------
6730
6731static void addOverloaded(const Entry *root,MemberName *mn,
6732 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6733 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6734{
6735 // for unique overloaded member we allow the class to be
6736 // omitted, this is to be Qt compatible. Using this should
6737 // however be avoided, because it is error prone
6738 bool sameClass=false;
6739 if (mn->size()>0)
6740 {
6741 // check if all members with the same name are also in the same class
6742 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6743 [](const auto &md1,const auto &md2)
6744 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6745 }
6746 if (sameClass)
6747 {
6748 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6749 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6750 if (cd==nullptr) return;
6751
6752 MemberType mtype = MemberType::Function;
6753 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6754 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6755 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6756
6757 // new overloaded member function
6758 std::unique_ptr<ArgumentList> tArgList =
6759 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6760 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6761 auto md = createMemberDef(
6762 root->fileName,root->startLine,root->startColumn,
6763 funcType,funcName,funcArgs,exceptions,
6764 root->protection,root->virt,root->isStatic,Relationship::Related,
6765 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6766 auto mmd = toMemberDefMutable(md.get());
6767 mmd->setTagInfo(root->tagInfo());
6768 mmd->setLanguage(root->lang);
6769 mmd->setId(root->id);
6770 mmd->setTypeConstraints(root->typeConstr);
6771 mmd->setMemberClass(cd);
6772 mmd->setDefinition(funcDecl);
6774 mmd->addQualifiers(root->qualifiers);
6776 doc+="<p>";
6777 doc+=root->doc;
6778 mmd->setDocumentation(doc,root->docFile,root->docLine);
6779 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6780 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6781 mmd->setDocsForDefinition(!root->proto);
6782 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6783 mmd->addSectionsToDefinition(root->anchors);
6784 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6785 FileDef *fd=root->fileDef();
6786 mmd->setBodyDef(fd);
6787 mmd->setMemberSpecifiers(spec);
6788 mmd->setVhdlSpecifiers(root->vhdlSpec);
6789 mmd->setMemberGroupId(root->mGrpId);
6790 cd->insertMember(md.get());
6791 cd->insertUsedFile(fd);
6792 mmd->setRefItems(root->sli);
6793 mmd->setRequirementReferences(root->rqli);
6794
6795 mn->push_back(std::move(md));
6796 }
6797}
6798
6799static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6800{
6801 if (outerScope && outerScope!=Doxygen::globalScope)
6802 {
6803 auto aliasMd = createMemberDefAlias(outerScope,md);
6804 if (outerScope->definitionType()==Definition::TypeClass)
6805 {
6806 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6807 if (cdm)
6808 {
6809 cdm->insertMember(aliasMd.get());
6810 }
6811 }
6812 else if (outerScope->definitionType()==Definition::TypeNamespace)
6813 {
6814 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6815 if (ndm)
6816 {
6817 ndm->insertMember(aliasMd.get());
6818 }
6819 }
6820 else if (outerScope->definitionType()==Definition::TypeFile)
6821 {
6822 toFileDef(outerScope)->insertMember(aliasMd.get());
6823 }
6824 if (aliasMd)
6825 {
6826 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6827 }
6828 }
6829}
6830
6831//-------------------------------------------------------------------------------------------
6832
6833/*! This function tries to find a member (in a documented class/file/namespace)
6834 * that corresponds to the function/variable declaration given in \a funcDecl.
6835 *
6836 * The boolean \a overloaded is used to specify whether or not a standard
6837 * overload documentation line should be generated.
6838 *
6839 * The boolean \a isFunc is a hint that indicates that this is a function
6840 * instead of a variable or typedef.
6841 */
6842static void findMember(const Entry *root,
6843 const QCString &relates,
6844 const QCString &type,
6845 const QCString &args,
6846 QCString funcDecl,
6847 bool overloaded,
6848 bool isFunc
6849 )
6850{
6851 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6852 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6853 root->spec, root->lang);
6854
6855 QCString scopeName;
6856 QCString className;
6857 QCString namespaceName;
6858 QCString funcType;
6859 QCString funcName;
6860 QCString funcArgs;
6861 QCString funcTempList;
6862 QCString exceptions;
6863 QCString funcSpec;
6864 bool isRelated=false;
6865 bool isMemberOf=false;
6866 bool isFriend=false;
6867 bool done=false;
6868 TypeSpecifier spec = root->spec;
6869 while (!done)
6870 {
6871 done=true;
6872 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6873 {
6874 isFriend=true;
6875 done=false;
6876 }
6877 if (funcDecl.stripPrefix("inline "))
6878 {
6879 spec.setInline(true);
6880 done=false;
6881 }
6882 if (funcDecl.stripPrefix("explicit "))
6883 {
6884 spec.setExplicit(true);
6885 done=false;
6886 }
6887 if (funcDecl.stripPrefix("mutable "))
6888 {
6889 spec.setMutable(true);
6890 done=false;
6891 }
6892 if (funcDecl.stripPrefix("thread_local "))
6893 {
6894 spec.setThreadLocal(true);
6895 done=false;
6896 }
6897 if (funcDecl.stripPrefix("virtual "))
6898 {
6899 done=false;
6900 }
6901 }
6902
6903 // delete any ; from the function declaration
6904 int sep=0;
6905 while ((sep=funcDecl.find(';'))!=-1)
6906 {
6907 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6908 }
6909
6910 // make sure the first character is a space to simplify searching.
6911 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6912
6913 // remove some superfluous spaces
6914 funcDecl= substitute(
6915 substitute(
6916 substitute(funcDecl,"~ ","~"),
6917 ":: ","::"
6918 ),
6919 " ::","::"
6920 ).stripWhiteSpace();
6921
6922 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6923 if (isFriend && funcDecl.startsWith("class "))
6924 {
6925 //printf("friend class\n");
6926 funcDecl=funcDecl.right(funcDecl.length()-6);
6927 funcName = funcDecl;
6928 }
6929 else if (isFriend && funcDecl.startsWith("struct "))
6930 {
6931 funcDecl=funcDecl.right(funcDecl.length()-7);
6932 funcName = funcDecl;
6933 }
6934 else
6935 {
6936 // extract information from the declarations
6937 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6938 funcArgs,funcTempList,exceptions
6939 );
6940 }
6941
6942 // the class name can also be a namespace name, we decide this later.
6943 // if a related class name is specified and the class name could
6944 // not be derived from the function declaration, then use the
6945 // related field.
6946 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6947 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6948 if (!relates.isEmpty())
6949 { // related member, prefix user specified scope
6950 isRelated=TRUE;
6951 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6952 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6953 {
6954 scopeName= mergeScopes(scopeName,relates);
6955 }
6956 else
6957 {
6958 scopeName = relates;
6959 }
6960 }
6961
6962 if (relates.isEmpty() && root->parent() &&
6963 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6964 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6965 // with the scope in which it was found
6966 {
6967 QCString joinedName = root->parent()->name+"::"+scopeName;
6968 if (!scopeName.isEmpty() &&
6969 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6970 {
6971 scopeName = joinedName;
6972 }
6973 else
6974 {
6975 scopeName = mergeScopes(root->parent()->name,scopeName);
6976 }
6977 }
6978 else // see if we can prefix a namespace or class that is used from the file
6979 {
6980 FileDef *fd=root->fileDef();
6981 if (fd)
6982 {
6983 for (const auto &fnd : fd->getUsedNamespaces())
6984 {
6985 QCString joinedName = fnd->name()+"::"+scopeName;
6986 if (Doxygen::namespaceLinkedMap->find(joinedName))
6987 {
6988 scopeName=joinedName;
6989 break;
6990 }
6991 }
6992 }
6993 }
6995 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6996
6997 // funcSpec contains the last template specifiers of the given scope.
6998 // If this method does not have any template arguments or they are
6999 // empty while funcSpec is not empty we assume this is a
7000 // specialization of a method. If not, we clear the funcSpec and treat
7001 // this as a normal method of a template class.
7002 if (!(root->tArgLists.size()>0 &&
7003 root->tArgLists.front().size()==0
7004 )
7005 )
7006 {
7007 funcSpec.clear();
7008 }
7009
7010 //namespaceName=removeAnonymousScopes(namespaceName);
7011 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
7012
7013 // split scope into a namespace and a class part
7014 extractNamespaceName(scopeName,className,namespaceName,TRUE);
7015 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
7016
7017 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
7018 // merge class and namespace scopes again
7019 scopeName.clear();
7020 if (!namespaceName.isEmpty())
7021 {
7022 if (className.isEmpty())
7023 {
7024 scopeName=namespaceName;
7025 }
7026 else if (!relates.isEmpty() || // relates command with explicit scope
7027 !getClass(className)) // class name only exists in a namespace
7028 {
7029 scopeName=namespaceName+"::"+className;
7030 }
7031 else
7032 {
7033 scopeName=className;
7034 }
7035 }
7036 else if (!className.isEmpty())
7037 {
7038 scopeName=className;
7039 }
7040 //printf("new scope='%s'\n",qPrint(scopeName));
7041
7042 QCString tempScopeName=scopeName;
7043 ClassDefMutable *cd=getClassMutable(scopeName);
7044 if (cd)
7045 {
7046 if (funcSpec.isEmpty())
7047 {
7048 uint32_t argListIndex=0;
7049 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7050 }
7051 else
7052 {
7053 tempScopeName=scopeName+funcSpec;
7054 }
7055 }
7056 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7057 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7058
7059 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7060 // rebuild the function declaration (needed to get the scope right).
7061 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7062 {
7063 if (!funcType.isEmpty())
7064 {
7065 if (isFunc) // a function -> we use argList for the arguments
7066 {
7067 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7068 }
7069 else
7070 {
7071 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7072 }
7073 }
7074 else
7075 {
7076 if (isFunc) // a function => we use argList for the arguments
7077 {
7078 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7079 }
7080 else // variable => add 'argument' list
7081 {
7082 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7083 }
7084 }
7085 }
7086 else // build declaration without scope
7087 {
7088 if (!funcType.isEmpty()) // but with a type
7089 {
7090 if (isFunc) // function => omit argument list
7091 {
7092 funcDecl=funcType+" "+funcName+funcTempList;
7093 }
7094 else // variable => add 'argument' list
7095 {
7096 funcDecl=funcType+" "+funcName+funcArgs;
7097 }
7098 }
7099 else // no type
7100 {
7101 if (isFunc)
7102 {
7103 funcDecl=funcName+funcTempList;
7104 }
7105 else
7106 {
7107 funcDecl=funcName+funcArgs;
7108 }
7109 }
7110 }
7111
7112 if (funcType=="template class" && !funcTempList.isEmpty())
7113 return; // ignore explicit template instantiations
7114
7115 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7116 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7117 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7118 namespaceName, className, funcType, funcSpec,
7119 funcName, funcArgs, funcTempList, funcDecl, relates,
7120 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7121
7122 if (!funcName.isEmpty()) // function name is valid
7123 {
7124 // check if 'className' is actually a scoped enum, in which case we need to
7125 // process it as a global, see issue #6471
7126 bool strongEnum = false;
7127 MemberName *mn=nullptr;
7128 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7129 {
7130 for (const auto &imd : *mn)
7131 {
7132 MemberDefMutable *md = toMemberDefMutable(imd.get());
7133 Definition *mdScope = nullptr;
7134 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7135 // need filter for the correct scope, see issue #9668
7136 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7137 {
7138 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7139 strongEnum = true;
7140 // pass the scope name name as a 'namespace' to the findGlobalMember function
7141 if (!namespaceName.isEmpty())
7142 {
7143 namespaceName+="::"+className;
7144 }
7145 else
7146 {
7147 namespaceName=className;
7148 }
7149 }
7150 }
7151 }
7152
7153 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7154 {
7155 funcName = substitute(funcName,className+"::","");
7156 }
7157 mn = nullptr;
7158 if (!funcTempList.isEmpty()) // try with member specialization
7159 {
7160 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7161 }
7162 if (mn==nullptr) // try without specialization
7163 {
7164 mn=Doxygen::memberNameLinkedMap->find(funcName);
7165 }
7166 if (!isRelated && !strongEnum && mn) // function name already found
7167 {
7168 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7169 if (!className.isEmpty()) // class name is valid
7170 {
7171 if (funcSpec.isEmpty()) // not a member specialization
7172 {
7173 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7174 funcArgs,funcTempList,exceptions,
7175 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7176 }
7177 else if (cd) // member specialization
7178 {
7179 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7180 }
7181 else
7182 {
7183 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7184 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7185 }
7186 }
7187 else if (overloaded) // check if the function belongs to only one class
7188 {
7189 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7190 }
7191 else // unrelated function with the same name as a member
7192 {
7193 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7194 {
7195 QCString fullFuncDecl=funcDecl;
7196 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7197 warn(root->fileName,root->startLine,
7198 "Cannot determine class for function\n{}",
7199 fullFuncDecl
7200 );
7201 }
7202 }
7203 }
7204 else if (isRelated && !relates.isEmpty())
7205 {
7206 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7207 if (className.isEmpty()) className=relates;
7208 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7209 if ((cd=getClassMutable(scopeName)))
7210 {
7211 bool newMember=TRUE; // assume we have a new member
7212 MemberDefMutable *mdDefine=nullptr;
7213 {
7214 mn = Doxygen::functionNameLinkedMap->find(funcName);
7215 if (mn)
7216 {
7217 for (const auto &imd : *mn)
7218 {
7219 MemberDefMutable *md = toMemberDefMutable(imd.get());
7220 if (md && md->isDefine())
7221 {
7222 mdDefine = md;
7223 break;
7224 }
7225 }
7226 }
7227 }
7228
7229 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7230 {
7231 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7232
7233 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7234 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7235 // insert it as a class member
7236 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7237 {
7238 mn=Doxygen::memberNameLinkedMap->add(funcName);
7239 }
7240
7241 if (mdDefine->getFileDef())
7242 {
7243 mdDefine->getFileDef()->removeMember(mdDefine);
7244 }
7245 mdDefine->makeRelated();
7246 mdDefine->setMemberClass(cd);
7247 mdDefine->moveTo(cd);
7248 cd->insertMember(mdDefine);
7249 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7250 insertMemberAlias(cd->getOuterScope(),mdDefine);
7251 mn->push_back(std::move(mdDefineTaken));
7252 }
7253 else // normal member, needs to be created and added to the class
7254 {
7255 FileDef *fd=root->fileDef();
7256
7257 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7258 {
7259 mn=Doxygen::memberNameLinkedMap->add(funcName);
7260 }
7261 else
7262 {
7263 // see if we got another member with matching arguments
7264 MemberDefMutable *rmd_found = nullptr;
7265 for (const auto &irmd : *mn)
7266 {
7267 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7268 if (rmd)
7269 {
7270 const ArgumentList &rmdAl = rmd->argumentList();
7271
7272 newMember=
7273 className!=rmd->getOuterScope()->name() ||
7274 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7275 cd,fd,root->type,&root->argList,
7276 TRUE,root->lang);
7277 if (!newMember)
7278 {
7279 rmd_found = rmd;
7280 }
7281 }
7282 }
7283 if (rmd_found) // member already exists as rmd -> add docs
7284 {
7285 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7286 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7287 newMember=false;
7288 }
7289 }
7290
7291 if (newMember) // need to create a new member
7292 {
7293 MemberType mtype = MemberType::Function;
7294 switch (root->mtype)
7295 {
7296 case MethodTypes::Method: mtype = MemberType::Function; break;
7297 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7298 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7299 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7300 case MethodTypes::Property: mtype = MemberType::Property; break;
7301 case MethodTypes::Event: mtype = MemberType::Event; break;
7302 }
7303
7304 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7305 // root->argList ? (int)root->argList->count() : -1);
7306
7307 // first note that we pass:
7308 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7309 // for the template arguments for the new "member."
7310 // this accurately reflects the template arguments of
7311 // the related function, which don't have to do with
7312 // those of the related class.
7313 auto md = createMemberDef(
7314 root->fileName,root->startLine,root->startColumn,
7315 funcType,funcName,funcArgs,exceptions,
7316 root->protection,root->virt,
7317 root->isStatic,
7318 isMemberOf ? Relationship::Foreign : Relationship::Related,
7319 mtype,
7320 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7321 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7322 root->metaData);
7323 auto mmd = toMemberDefMutable(md.get());
7324
7325 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7326 insertMemberAlias(cd->getOuterScope(),md.get());
7327
7328 // we still have the problem that
7329 // MemberDef::writeDocumentation() in memberdef.cpp
7330 // writes the template argument list for the class,
7331 // as if this member is a member of the class.
7332 // fortunately, MemberDef::writeDocumentation() has
7333 // a special mechanism that allows us to totally
7334 // override the set of template argument lists that
7335 // are printed. We use that and set it to the
7336 // template argument lists of the related function.
7337 //
7338 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7339
7340 mmd->setTagInfo(root->tagInfo());
7341
7342 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7343 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7344
7345 // try to find the matching line number of the body from the
7346 // global function list
7347 bool found=FALSE;
7348 if (root->bodyLine==-1)
7349 {
7350 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7351 if (rmn)
7352 {
7353 const MemberDefMutable *rmd_found=nullptr;
7354 for (const auto &irmd : *rmn)
7355 {
7356 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7357 if (rmd)
7358 {
7359 const ArgumentList &rmdAl = rmd->argumentList();
7360 // check for matching argument lists
7361 if (
7362 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7363 cd,fd,root->type,&root->argList,
7364 TRUE,root->lang)
7365 )
7366 {
7367 found=TRUE;
7368 rmd_found = rmd;
7369 break;
7370 }
7371 }
7372 }
7373 if (rmd_found) // member found -> copy line number info
7374 {
7375 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7376 mmd->setBodyDef(rmd_found->getBodyDef());
7377 //md->setBodyMember(rmd);
7378 }
7379 }
7380 }
7381 if (!found) // line number could not be found or is available in this
7382 // entry
7383 {
7384 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7385 mmd->setBodyDef(fd);
7386 }
7387
7388 //if (root->mGrpId!=-1)
7389 //{
7390 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7391 //}
7392 mmd->setMemberClass(cd);
7393 mmd->setMemberSpecifiers(spec);
7394 mmd->setVhdlSpecifiers(root->vhdlSpec);
7395 mmd->setDefinition(funcDecl);
7397 mmd->addQualifiers(root->qualifiers);
7398 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7399 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7400 mmd->setDocsForDefinition(!root->proto);
7401 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7402 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7403 mmd->addSectionsToDefinition(root->anchors);
7404 mmd->setMemberGroupId(root->mGrpId);
7405 mmd->setLanguage(root->lang);
7406 mmd->setId(root->id);
7407 //md->setMemberDefTemplateArguments(root->mtArgList);
7408 cd->insertMember(md.get());
7409 cd->insertUsedFile(fd);
7410 mmd->setRefItems(root->sli);
7411 mmd->setRequirementReferences(root->rqli);
7412 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7413 addMemberToGroups(root,md.get());
7415 //printf("Adding member=%s\n",qPrint(md->name()));
7416 mn->push_back(std::move(md));
7417 }
7418 if (root->relatesType==RelatesType::Duplicate)
7419 {
7420 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7421 {
7422 QCString fullFuncDecl=funcDecl;
7423 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7424 warn(root->fileName,root->startLine,
7425 "Cannot determine file/namespace for relatedalso function\n{}",
7426 fullFuncDecl
7427 );
7428 }
7429 }
7430 }
7431 }
7432 else
7433 {
7434 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7435 }
7436 }
7437 else if (root->parent() && root->parent()->section.isObjcImpl())
7438 {
7439 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7440 }
7441 else // unrelated not overloaded member found
7442 {
7443 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7444 if (className.isEmpty() && !globMem)
7445 {
7446 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7447 }
7448 else if (!className.isEmpty() && !globMem)
7449 {
7450 warn(root->fileName,root->startLine,
7451 "member '{}' of class '{}' cannot be found",
7452 funcName,className);
7453 }
7454 }
7455 }
7456 else
7457 {
7458 // this should not be called
7459 warn(root->fileName,root->startLine,"member with no name found.");
7460 }
7461 return;
7462}
7463
7464//----------------------------------------------------------------------
7465// find the members corresponding to the different documentation blocks
7466// that are extracted from the sources.
7467
7468static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7469{
7470 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7471 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7472 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7473 bool isFunc=TRUE;
7474
7475 QCString type = root->type;
7476 QCString args = root->args;
7477 int i=-1, l=0;
7478 if ( // detect func variable/typedef to func ptr
7479 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7480 )
7481 {
7482 //printf("Fixing function pointer!\n");
7483 // fix type and argument
7484 args.prepend(type.right(type.length()-i-l));
7485 type=type.left(i+l);
7486 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7487 isFunc=FALSE;
7488 }
7489 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7490 // detect function types marked as functions
7491 {
7492 isFunc=FALSE;
7493 }
7494
7495 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7496 if (root->section.isMemberDoc())
7497 {
7498 //printf("Documentation for inline member '%s' found args='%s'\n",
7499 // qPrint(root->name),qPrint(args));
7500 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7501 if (type.isEmpty())
7502 {
7503 findMember(root,
7504 relates,
7505 type,
7506 args,
7507 root->name + args + root->exception,
7508 FALSE,
7509 isFunc);
7510 }
7511 else
7512 {
7513 findMember(root,
7514 relates,
7515 type,
7516 args,
7517 type + " " + root->name + args + root->exception,
7518 FALSE,
7519 isFunc);
7520 }
7521 }
7522 else if (root->section.isOverloadDoc())
7523 {
7524 //printf("Overloaded member %s found\n",qPrint(root->name));
7525 findMember(root,
7526 relates,
7527 type,
7528 args,
7529 root->name,
7530 TRUE,
7531 isFunc);
7532 }
7533 else if
7534 ((root->section.isFunction() // function
7535 ||
7536 (root->section.isVariable() && // variable
7537 !type.isEmpty() && // with a type
7538 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7539 // (to skip forward declaration of class etc.)
7540 )
7541 )
7542 )
7543 {
7544 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7545 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7546 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7547 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7548 if (type=="friend class" || type=="friend struct" ||
7549 type=="friend union")
7550 {
7551 findMember(root,
7552 relates,
7553 type,
7554 args,
7555 type+" "+root->name,
7556 FALSE,FALSE);
7557
7558 }
7559 else if (!type.isEmpty())
7560 {
7561 findMember(root,
7562 relates,
7563 type,
7564 args,
7565 type+" "+ root->inside + root->name + args + root->exception,
7566 FALSE,isFunc);
7567 }
7568 else
7569 {
7570 findMember(root,
7571 relates,
7572 type,
7573 args,
7574 root->inside + root->name + args + root->exception,
7575 FALSE,isFunc);
7576 }
7577 }
7578 else if (root->section.isDefine() && !relates.isEmpty())
7579 {
7580 findMember(root,
7581 relates,
7582 type,
7583 args,
7584 root->name + args,
7585 FALSE,
7586 !args.isEmpty());
7587 }
7588 else if (root->section.isVariableDoc())
7589 {
7590 //printf("Documentation for variable %s found\n",qPrint(root->name));
7591 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7592 findMember(root,
7593 relates,
7594 type,
7595 args,
7596 root->name,
7597 FALSE,
7598 FALSE);
7599 }
7600 else if (root->section.isExportedInterface() ||
7601 root->section.isIncludedService())
7602 {
7603 findMember(root,
7604 relates,
7605 type,
7606 args,
7607 type + " " + root->name,
7608 FALSE,
7609 FALSE);
7610 }
7611 else
7612 {
7613 // skip section
7614 //printf("skip section\n");
7615 }
7616}
7617
7618static void findMemberDocumentation(const Entry *root)
7619{
7620 if (root->section.isMemberDoc() ||
7621 root->section.isOverloadDoc() ||
7622 root->section.isFunction() ||
7623 root->section.isVariable() ||
7624 root->section.isVariableDoc() ||
7625 root->section.isDefine() ||
7626 root->section.isIncludedService() ||
7627 root->section.isExportedInterface()
7628 )
7629 {
7630 AUTO_TRACE();
7631 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7632 {
7634 }
7636 }
7637 for (const auto &e : root->children())
7638 {
7639 if (!e->section.isEnum())
7640 {
7641 findMemberDocumentation(e.get());
7642 }
7643 }
7644}
7645
7646//----------------------------------------------------------------------
7647
7648static void findObjCMethodDefinitions(const Entry *root)
7649{
7650 AUTO_TRACE();
7651 for (const auto &objCImpl : root->children())
7652 {
7653 if (objCImpl->section.isObjcImpl())
7654 {
7655 for (const auto &objCMethod : objCImpl->children())
7656 {
7657 if (objCMethod->section.isFunction())
7658 {
7659 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7660 findMember(objCMethod.get(),
7661 objCMethod->relates,
7662 objCMethod->type,
7663 objCMethod->args,
7664 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7665 FALSE,TRUE);
7666 objCMethod->section=EntryType::makeEmpty();
7667 }
7668 }
7669 }
7670 }
7671}
7672
7673//----------------------------------------------------------------------
7674// find and add the enumeration to their classes, namespaces or files
7675
7676static void findEnums(const Entry *root)
7677{
7678 if (root->section.isEnum())
7679 {
7680 AUTO_TRACE("name={}",root->name);
7681 ClassDefMutable *cd = nullptr;
7682 FileDef *fd = nullptr;
7683 NamespaceDefMutable *nd = nullptr;
7684 MemberNameLinkedMap *mnsd = nullptr;
7685 bool isGlobal = false;
7686 bool isRelated = false;
7687 bool isMemberOf = false;
7688 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7689
7690 QCString name;
7691 QCString scope;
7692
7693 int i = root->name.findRev("::");
7694 if (i!=-1) // scope is specified
7695 {
7696 scope=root->name.left(i); // extract scope
7697 if (root->lang==SrcLangExt::CSharp)
7698 {
7699 scope = mangleCSharpGenericName(scope);
7700 }
7701 name=root->name.right(root->name.length()-i-2); // extract name
7702 if ((cd=getClassMutable(scope))==nullptr)
7703 {
7705 }
7706 }
7707 else // no scope, check the scope in which the docs where found
7708 {
7709 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7710 {
7711 scope=root->parent()->name;
7712 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7713 }
7714 name=root->name;
7715 }
7716
7717 if (!root->relates.isEmpty())
7718 { // related member, prefix user specified scope
7719 isRelated=TRUE;
7720 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7721 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7722 scope=mergeScopes(scope,root->relates);
7723 else
7724 scope=root->relates;
7725 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7726 }
7727
7728 if (cd && !name.isEmpty()) // found a enum inside a compound
7729 {
7730 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7731 fd=nullptr;
7733 isGlobal=false;
7734 }
7735 else if (nd) // found enum inside namespace
7736 {
7738 isGlobal=true;
7739 }
7740 else // found a global enum
7741 {
7742 fd=root->fileDef();
7744 isGlobal=true;
7745 }
7746
7747 if (!name.isEmpty())
7748 {
7749 // new enum type
7750 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7751 auto md = createMemberDef(
7752 root->fileName,root->startLine,root->startColumn,
7753 QCString(),name,QCString(),QCString(),
7754 root->protection,Specifier::Normal,FALSE,
7755 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7756 MemberType::Enumeration,
7758 auto mmd = toMemberDefMutable(md.get());
7759 mmd->setTagInfo(root->tagInfo());
7760 mmd->setLanguage(root->lang);
7761 mmd->setId(root->id);
7762 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7763 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7764 mmd->setBodyDef(root->fileDef());
7765 mmd->setMemberSpecifiers(root->spec);
7766 mmd->setVhdlSpecifiers(root->vhdlSpec);
7767 mmd->setEnumBaseType(root->args);
7768 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7769 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7770 mmd->addSectionsToDefinition(root->anchors);
7771 mmd->setMemberGroupId(root->mGrpId);
7773 mmd->addQualifiers(root->qualifiers);
7774 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7775 mmd->setRefItems(root->sli);
7776 mmd->setRequirementReferences(root->rqli);
7777 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7778 bool defSet=FALSE;
7779
7780 QCString baseType = root->args;
7781 if (!baseType.isEmpty())
7782 {
7783 baseType.prepend(" : ");
7784 }
7785
7786 if (nd)
7787 {
7788 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7789 {
7790 mmd->setDefinition(name+baseType);
7791 }
7792 else
7793 {
7794 mmd->setDefinition(nd->name()+"::"+name+baseType);
7795 }
7796 //printf("definition=%s\n",md->definition());
7797 defSet=TRUE;
7798 mmd->setNamespace(nd);
7799 nd->insertMember(md.get());
7800 }
7801
7802 // even if we have already added the enum to a namespace, we still
7803 // also want to add it to other appropriate places such as file
7804 // or class.
7805 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7806 {
7807 if (!defSet) mmd->setDefinition(name+baseType);
7808 if (fd==nullptr && root->parent())
7809 {
7810 fd=root->parent()->fileDef();
7811 }
7812 if (fd)
7813 {
7814 mmd->setFileDef(fd);
7815 fd->insertMember(md.get());
7816 }
7817 }
7818 else if (cd)
7819 {
7820 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7821 {
7822 mmd->setDefinition(name+baseType);
7823 }
7824 else
7825 {
7826 mmd->setDefinition(cd->name()+"::"+name+baseType);
7827 }
7828 cd->insertMember(md.get());
7829 cd->insertUsedFile(fd);
7830 }
7831 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7832 mmd->setDocsForDefinition(!root->proto);
7833 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7834 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7835
7836 //printf("Adding member=%s\n",qPrint(md->name()));
7837 addMemberToGroups(root,md.get());
7839
7840 MemberName *mn = mnsd->add(name);
7841 mn->push_back(std::move(md));
7842 }
7843 }
7844 else
7845 {
7846 for (const auto &e : root->children()) findEnums(e.get());
7847 }
7848}
7849
7850//----------------------------------------------------------------------
7851
7852static void addEnumValuesToEnums(const Entry *root)
7853{
7854 if (root->section.isEnum())
7855 // non anonymous enumeration
7856 {
7857 AUTO_TRACE("name={}",root->name);
7858 ClassDefMutable *cd = nullptr;
7859 FileDef *fd = nullptr;
7860 NamespaceDefMutable *nd = nullptr;
7861 MemberNameLinkedMap *mnsd = nullptr;
7862 bool isGlobal = false;
7863 bool isRelated = false;
7864 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7865
7866 QCString name;
7867 QCString scope;
7868
7869 int i = root->name.findRev("::");
7870 if (i!=-1) // scope is specified
7871 {
7872 scope=root->name.left(i); // extract scope
7873 if (root->lang==SrcLangExt::CSharp)
7874 {
7875 scope = mangleCSharpGenericName(scope);
7876 }
7877 name=root->name.right(root->name.length()-i-2); // extract name
7878 if ((cd=getClassMutable(scope))==nullptr)
7879 {
7881 }
7882 }
7883 else // no scope, check the scope in which the docs where found
7884 {
7885 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7886 {
7887 scope=root->parent()->name;
7888 if (root->lang==SrcLangExt::CSharp)
7889 {
7890 scope = mangleCSharpGenericName(scope);
7891 }
7892 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7893 }
7894 name=root->name;
7895 }
7896
7897 if (!root->relates.isEmpty())
7898 { // related member, prefix user specified scope
7899 isRelated=TRUE;
7900 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7901 scope=mergeScopes(scope,root->relates);
7902 else
7903 scope=root->relates;
7904 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7905 }
7906
7907 if (cd && !name.isEmpty()) // found a enum inside a compound
7908 {
7909 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7910 fd=nullptr;
7912 isGlobal=false;
7913 }
7914 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7915 {
7916 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7918 isGlobal=true;
7919 }
7920 else // found a global enum
7921 {
7922 fd=root->fileDef();
7923 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7925 isGlobal=true;
7926 }
7927
7928 if (!name.isEmpty())
7929 {
7930 //printf("** name=%s\n",qPrint(name));
7931 MemberName *mn = mnsd->find(name); // for all members with this name
7932 if (mn)
7933 {
7934 struct EnumValueInfo
7935 {
7936 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7937 name(n), member(std::move(md)) {}
7938 QCString name;
7939 std::unique_ptr<MemberDef> member;
7940 };
7941 std::vector< EnumValueInfo > extraMembers;
7942 // for each enum in this list
7943 for (const auto &imd : *mn)
7944 {
7945 MemberDefMutable *md = toMemberDefMutable(imd.get());
7946 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7947 if (md && md->isEnumerate() && !root->children().empty())
7948 {
7949 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7950 for (const auto &e : root->children())
7951 {
7952 SrcLangExt sle = root->lang;
7953 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7954 if ( isJavaLike || root->spec.isStrong())
7955 {
7956 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7957 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7958 // values are only visible inside the enum scope, so we must create
7959 // them here and only add them to the enum
7960 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7961 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7962 QCString qualifiedName = root->name;
7963 i = qualifiedName.findRev("::");
7964 if (i!=-1 && sle==SrcLangExt::CSharp)
7965 {
7966 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7967 }
7968 if (isJavaLike)
7969 {
7970 qualifiedName=substitute(qualifiedName,"::",".");
7971 }
7972 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7973 {
7974 QCString fileName = e->fileName;
7975 if (fileName.isEmpty() && e->tagInfo())
7976 {
7977 fileName = e->tagInfo()->tagName;
7978 }
7979 AUTO_TRACE_ADD("strong enum value {}",e->name);
7980 auto fmd = createMemberDef(
7981 fileName,e->startLine,e->startColumn,
7982 e->type,e->name,e->args,QCString(),
7983 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7984 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7985 auto fmmd = toMemberDefMutable(fmd.get());
7986 NamespaceDef *mnd = md->getNamespaceDef();
7987 if (md->getClassDef())
7988 fmmd->setMemberClass(md->getClassDef());
7989 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7990 fmmd->setNamespace(mnd);
7991 else if (md->getFileDef())
7992 fmmd->setFileDef(md->getFileDef());
7993 fmmd->setOuterScope(md->getOuterScope());
7994 fmmd->setTagInfo(e->tagInfo());
7995 fmmd->setLanguage(e->lang);
7996 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7997 fmmd->setBodyDef(e->fileDef());
7998 fmmd->setId(e->id);
7999 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
8000 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
8001 fmmd->addSectionsToDefinition(e->anchors);
8002 fmmd->setInitializer(e->initializer.str());
8003 fmmd->setMaxInitLines(e->initLines);
8004 fmmd->setMemberGroupId(e->mGrpId);
8005 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
8006 fmmd->setRefItems(e->sli);
8007 fmmd->setRequirementReferences(e->rqli);
8008 fmmd->setAnchor();
8009 md->insertEnumField(fmd.get());
8010 fmmd->setEnumScope(md,TRUE);
8011 extraMembers.emplace_back(e->name,std::move(fmd));
8012 }
8013 }
8014 else
8015 {
8016 AUTO_TRACE_ADD("enum value {}",e->name);
8017 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
8018 MemberName *fmn=nullptr;
8019 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
8020 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
8021 // get list of members with the same name as the field
8022 {
8023 for (const auto &ifmd : *fmn)
8024 {
8025 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8026 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
8027 {
8028 //printf("found enum value with same name %s in scope %s\n",
8029 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
8030 if (nd && !nd->isAnonymous())
8031 {
8032 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8033 {
8034 const NamespaceDef *fnd=fmd->getNamespaceDef();
8035 if (fnd==nd) // enum value is inside a namespace
8036 {
8037 md->insertEnumField(fmd);
8038 fmd->setEnumScope(md);
8039 }
8040 }
8041 }
8042 else if (isGlobal)
8043 {
8044 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8045 {
8046 const FileDef *ffd=fmd->getFileDef();
8047 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8048 {
8049 md->insertEnumField(fmd);
8050 fmd->setEnumScope(md);
8051 }
8052 }
8053 }
8054 else if (isRelated && cd) // reparent enum value to
8055 // match the enum's scope
8056 {
8057 md->insertEnumField(fmd); // add field def to list
8058 fmd->setEnumScope(md); // cross ref with enum name
8059 fmd->setEnumClassScope(cd); // cross ref with enum name
8060 fmd->setOuterScope(cd);
8061 fmd->makeRelated();
8062 cd->insertMember(fmd);
8063 }
8064 else
8065 {
8066 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8067 {
8068 const ClassDef *fcd=fmd->getClassDef();
8069 if (fcd==cd) // enum value is inside a class
8070 {
8071 //printf("Inserting enum field %s in enum scope %s\n",
8072 // qPrint(fmd->name()),qPrint(md->name()));
8073 md->insertEnumField(fmd); // add field def to list
8074 fmd->setEnumScope(md); // cross ref with enum name
8075 }
8076 }
8077 }
8078 }
8079 }
8080 }
8081 }
8082 }
8083 }
8084 }
8085 // move the newly added members into mn
8086 for (auto &e : extraMembers)
8087 {
8088 MemberName *emn=mnsd->add(e.name);
8089 emn->push_back(std::move(e.member));
8090 }
8091 }
8092 }
8093 }
8094 else
8095 {
8096 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8097 }
8098}
8099
8100//----------------------------------------------------------------------
8101
8102static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8103{
8104 AUTO_TRACE();
8105 // documentation outside a compound overrides the documentation inside it
8106 {
8107 md->setDocumentation(root->doc,root->docFile,root->docLine);
8108 md->setDocsForDefinition(!root->proto);
8109 }
8110
8111 // brief descriptions inside a compound override the documentation
8112 // outside it
8113 {
8114 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8115 }
8116
8117 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
8118 {
8120 }
8121
8122 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8123 {
8124 md->setMemberGroupId(root->mGrpId);
8125 }
8126
8128 md->setRefItems(root->sli);
8129 md->setRequirementReferences(root->rqli);
8130
8131 const GroupDef *gd=md->getGroupDef();
8132 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8133 {
8134 addMemberToGroups(root,md);
8135 }
8137}
8138
8139//----------------------------------------------------------------------
8140// Search for the name in the associated groups. If a matching member
8141// definition exists, then add the documentation to it and return TRUE,
8142// otherwise FALSE.
8143
8144static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
8145{
8146 for (const auto &g : root->groups)
8147 {
8148 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8149 if (gd)
8150 {
8151 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8152 if (ml)
8153 {
8154 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8155 if (md)
8156 {
8157 addEnumDocs(root,md);
8158 return TRUE;
8159 }
8160 }
8161 }
8162 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8163 {
8164 warn(root->fileName, root->startLine,
8165 "Found non-existing group '{}' for the command '{}', ignoring command",
8166 g.groupname, Grouping::getGroupPriName( g.pri )
8167 );
8168 }
8169 }
8170
8171 return FALSE;
8172}
8173
8174//----------------------------------------------------------------------
8175// find the documentation blocks for the enumerations
8176
8177static void findEnumDocumentation(const Entry *root)
8178{
8179 if (root->section.isEnumDoc() &&
8180 !root->name.isEmpty() &&
8181 root->name.at(0)!='@' // skip anonymous enums
8182 )
8183 {
8184 QCString name;
8185 QCString scope;
8186 int i = root->name.findRev("::");
8187 if (i!=-1) // scope is specified as part of the name
8188 {
8189 name=root->name.right(root->name.length()-i-2); // extract name
8190 scope=root->name.left(i); // extract scope
8191 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8192 }
8193 else // just the name
8194 {
8195 name=root->name;
8196 }
8197 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
8198 {
8199 if (!scope.isEmpty()) scope.prepend("::");
8200 scope.prepend(root->parent()->name);
8201 }
8202 const ClassDef *cd = getClass(scope);
8203 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8204 const FileDef *fd = root->fileDef();
8205 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8206 name,scope,root->parent()->name,
8207 cd ? cd->name() : QCString("<none>"),
8208 nd ? nd->name() : QCString("<none>"),
8209 fd ? fd->name() : QCString("<none>"));
8210
8211 if (!name.isEmpty())
8212 {
8213 bool found = tryAddEnumDocsToGroupMember(root, name);
8214 if (!found)
8215 {
8216 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8217 if (mn)
8218 {
8219 for (const auto &imd : *mn)
8220 {
8221 MemberDefMutable *md = toMemberDefMutable(imd.get());
8222 if (md && md->isEnumerate())
8223 {
8224 const ClassDef *mcd = md->getClassDef();
8225 const NamespaceDef *mnd = md->getNamespaceDef();
8226 const FileDef *mfd = md->getFileDef();
8227 if (cd && mcd==cd)
8228 {
8229 AUTO_TRACE_ADD("Match found for class scope");
8230 addEnumDocs(root,md);
8231 found = TRUE;
8232 break;
8233 }
8234 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8235 {
8236 AUTO_TRACE_ADD("Match found for namespace scope");
8237 addEnumDocs(root,md);
8238 found = TRUE;
8239 break;
8240 }
8241 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8242 {
8243 AUTO_TRACE_ADD("Match found for global scope");
8244 addEnumDocs(root,md);
8245 found = TRUE;
8246 break;
8247 }
8248 }
8249 }
8250 }
8251 }
8252 if (!found)
8253 {
8254 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8255 }
8256 }
8257 }
8258 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8259}
8260
8261// search for each enum (member or function) in mnl if it has documented
8262// enum values.
8263static void findDEV(const MemberNameLinkedMap &mnsd)
8264{
8265 // for each member name
8266 for (const auto &mn : mnsd)
8267 {
8268 // for each member definition
8269 for (const auto &imd : *mn)
8270 {
8271 MemberDefMutable *md = toMemberDefMutable(imd.get());
8272 if (md && md->isEnumerate()) // member is an enum
8273 {
8274 int documentedEnumValues=0;
8275 // for each enum value
8276 for (const auto &fmd : md->enumFieldList())
8277 {
8278 if (fmd->isLinkableInProject()) documentedEnumValues++;
8279 }
8280 // at least one enum value is documented
8281 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8282 }
8283 }
8284 }
8285}
8286
8287// search for each enum (member or function) if it has documented enum
8288// values.
8294
8295//----------------------------------------------------------------------
8296
8298{
8299 auto &index = Index::instance();
8300 // for each class member name
8301 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8302 {
8303 // for each member definition
8304 for (const auto &md : *mn)
8305 {
8306 index.addClassMemberNameToIndex(md.get());
8307 if (md->getModuleDef())
8308 {
8309 index.addModuleMemberNameToIndex(md.get());
8310 }
8311 }
8312 }
8313 // for each file/namespace function name
8314 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8315 {
8316 // for each member definition
8317 for (const auto &md : *mn)
8318 {
8319 if (md->getNamespaceDef())
8320 {
8321 index.addNamespaceMemberNameToIndex(md.get());
8322 }
8323 else
8324 {
8325 index.addFileMemberNameToIndex(md.get());
8326 }
8327 if (md->getModuleDef())
8328 {
8329 index.addModuleMemberNameToIndex(md.get());
8330 }
8331 }
8332 }
8333
8334 index.sortMemberIndexLists();
8335}
8336
8337//----------------------------------------------------------------------
8338
8339static void addToIndices()
8340{
8341 for (const auto &cd : *Doxygen::classLinkedMap)
8342 {
8343 if (cd->isLinkableInProject())
8344 {
8345 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8346 if (Doxygen::searchIndex.enabled())
8347 {
8348 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8349 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8350 }
8351 }
8352 }
8353
8354 for (const auto &cd : *Doxygen::conceptLinkedMap)
8355 {
8356 if (cd->isLinkableInProject())
8357 {
8358 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8359 if (Doxygen::searchIndex.enabled())
8360 {
8361 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8362 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8363 }
8364 }
8365 }
8366
8367 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8368 {
8369 if (nd->isLinkableInProject())
8370 {
8371 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8372 if (Doxygen::searchIndex.enabled())
8373 {
8374 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8375 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8376 }
8377 }
8378 }
8379
8380 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8381 {
8382 for (const auto &fd : *fn)
8383 {
8384 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8385 {
8386 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8387 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8388 }
8389 }
8390 }
8391
8392 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8393 {
8394 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8395 if (Doxygen::searchIndex.enabled())
8396 {
8397 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8398 std::string s = title.str();
8399 static const reg::Ex re(R"(\a[\w-]*)");
8400 reg::Iterator it(s,re);
8402 for (; it!=end ; ++it)
8403 {
8404 const auto &match = *it;
8405 std::string matchStr = match.str();
8406 Doxygen::searchIndex.addWord(matchStr,true);
8407 }
8408 }
8409 };
8410
8411 for (const auto &gd : *Doxygen::groupLinkedMap)
8412 {
8413 if (gd->isLinkableInProject())
8414 {
8415 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8416 }
8417 }
8418
8419 for (const auto &pd : *Doxygen::pageLinkedMap)
8420 {
8421 if (pd->isLinkableInProject())
8422 {
8423 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8424 }
8425 }
8426
8428 {
8429 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8430 }
8431
8432 auto addMemberToSearchIndex = [](const MemberDef *md)
8433 {
8434 if (Doxygen::searchIndex.enabled())
8435 {
8436 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8437 QCString ln=md->localName();
8438 QCString qn=md->qualifiedName();
8439 Doxygen::searchIndex.addWord(ln,TRUE);
8440 if (ln!=qn)
8441 {
8442 Doxygen::searchIndex.addWord(qn,TRUE);
8443 if (md->getClassDef())
8444 {
8445 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8446 }
8447 if (md->getNamespaceDef())
8448 {
8449 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8450 }
8451 }
8452 }
8453 };
8454
8455 auto getScope = [](const MemberDef *md)
8456 {
8457 const Definition *scope = nullptr;
8458 if (md->getGroupDef()) scope = md->getGroupDef();
8459 else if (md->getClassDef()) scope = md->getClassDef();
8460 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8461 else if (md->getFileDef()) scope = md->getFileDef();
8462 return scope;
8463 };
8464
8465 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8466 {
8467 if (md->isLinkableInProject())
8468 {
8469 if (!(md->isEnumerate() && md->isAnonymous()))
8470 {
8471 Doxygen::indexList->addIndexItem(getScope(md),md);
8473 }
8474 if (md->isEnumerate())
8475 {
8476 for (const auto &fmd : md->enumFieldList())
8477 {
8478 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8480 }
8481 }
8482 }
8483 };
8484
8485 // for each class member name
8486 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8487 {
8488 // for each member definition
8489 for (const auto &md : *mn)
8490 {
8491 addMemberToIndices(md.get());
8492 }
8493 }
8494 // for each file/namespace function name
8495 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8496 {
8497 // for each member definition
8498 for (const auto &md : *mn)
8499 {
8500 addMemberToIndices(md.get());
8501 }
8502 }
8503}
8504
8505//----------------------------------------------------------------------
8506
8508{
8509 // for each member name
8510 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8511 {
8512 // for each member definition
8513 for (const auto &imd : *mn)
8514 {
8515 MemberDefMutable *md = toMemberDefMutable(imd.get());
8516 if (md)
8517 {
8519 }
8520 }
8521 }
8522 // for each member name
8523 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8524 {
8525 // for each member definition
8526 for (const auto &imd : *mn)
8527 {
8528 MemberDefMutable *md = toMemberDefMutable(imd.get());
8529 if (md)
8530 {
8532 }
8533 }
8534 }
8535}
8536
8537// recursive helper function looking for reimplements/implemented
8538// by relations between class cd and direct or indirect base class bcd
8540{
8541 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8542 {
8543 for (const auto &imd : *mn) // for each member with a given name
8544 {
8545 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8546 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8547 {
8548 ClassDef *mbcd = bcd->classDef;
8549 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8550 {
8551 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8552 const auto &bmni = bmn.find(mn->memberName());
8553 if (bmni) // there are base class members with the same name
8554 {
8555 for (const auto &ibmd : *bmni) // for base class member with that name
8556 {
8557 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8558 if (bmd) // not part of an inline namespace
8559 {
8560 auto lang = bmd->getLanguage();
8561 auto compType = mbcd->compoundType();
8562 if (bmd->virtualness()!=Specifier::Normal ||
8563 lang==SrcLangExt::Python ||
8564 lang==SrcLangExt::Java ||
8565 lang==SrcLangExt::PHP ||
8566 compType==ClassDef::Interface ||
8567 compType==ClassDef::Protocol)
8568 {
8569 const ArgumentList &bmdAl = bmd->argumentList();
8570 const ArgumentList &mdAl = md->argumentList();
8571 //printf(" Base argList='%s'\n Super argList='%s'\n",
8572 // qPrint(argListToString(bmdAl)),
8573 // qPrint(argListToString(mdAl))
8574 // );
8575 if (
8576 lang==SrcLangExt::Python ||
8577 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8578 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8579 TRUE,lang
8580 )
8581 )
8582 {
8583 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8584 //printf("match!\n");
8585 const MemberDef *rmd = md->reimplements();
8586 if (rmd==nullptr) // not already assigned
8587 {
8588 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8589 md->setReimplements(bmd);
8590 }
8591 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8592 bmd->insertReimplementedBy(md);
8593 }
8594 else
8595 {
8596 //printf("no match!\n");
8597 }
8598 }
8599 }
8600 }
8601 }
8602 }
8603 }
8604 }
8605 }
8606
8607 // do also for indirect base classes
8608 for (const auto &bbcd : bcd->classDef->baseClasses())
8609 {
8611 }
8612}
8613
8614//----------------------------------------------------------------------
8615// computes the relation between all members. For each member 'm'
8616// the members that override the implementation of 'm' are searched and
8617// the member that 'm' overrides is searched.
8618
8620{
8621 for (const auto &cd : *Doxygen::classLinkedMap)
8622 {
8623 if (cd->isLinkable())
8624 {
8625 for (const auto &bcd : cd->baseClasses())
8626 {
8628 }
8629 }
8630 }
8631}
8632
8633//----------------------------------------------------------------------------
8634
8636{
8637 // for each class
8638 for (const auto &cd : *Doxygen::classLinkedMap)
8639 {
8640 // that is a template
8641 for (const auto &ti : cd->getTemplateInstances())
8642 {
8643 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8644 if (tcdm)
8645 {
8646 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8647 }
8648 }
8649 }
8650}
8651
8652//----------------------------------------------------------------------------
8653
8654static void mergeCategories()
8655{
8656 AUTO_TRACE();
8657 // merge members of categories into the class they extend
8658 for (const auto &cd : *Doxygen::classLinkedMap)
8659 {
8660 int i=cd->name().find('(');
8661 if (i!=-1) // it is an Objective-C category
8662 {
8663 QCString baseName=cd->name().left(i);
8664 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8665 if (baseClass)
8666 {
8667 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8668 baseClass->mergeCategory(cd.get());
8669 }
8670 }
8671 }
8672}
8673
8674// builds the list of all members for each class
8675
8677{
8678 // merge the member list of base classes into the inherited classes.
8679 for (const auto &cd : *Doxygen::classLinkedMap)
8680 {
8681 if (// !cd->isReference() && // not an external class
8682 cd->subClasses().empty() && // is a root of the hierarchy
8683 !cd->baseClasses().empty()) // and has at least one base class
8684 {
8685 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8686 if (cdm)
8687 {
8688 //printf("*** merging members for %s\n",qPrint(cd->name()));
8689 cdm->mergeMembers();
8690 }
8691 }
8692 }
8693 // now sort the member list of all members for all classes.
8694 for (const auto &cd : *Doxygen::classLinkedMap)
8695 {
8696 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8697 if (cdm)
8698 {
8699 cdm->sortAllMembersList();
8700 }
8701 }
8702}
8703
8704//----------------------------------------------------------------------------
8705
8707{
8708 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8709 {
8710 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8711 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8712 if (showSources)
8713 {
8714 msg("Generating code for file {}...\n",fd->docName());
8715 fd->writeSourceHeader(ol);
8716 fd->writeSourceBody(ol,parser);
8717 fd->writeSourceFooter(ol);
8718 }
8719 else if (parseSources)
8720 {
8721 msg("Parsing code for file {}...\n",fd->docName());
8722 fd->parseSource(parser);
8723 }
8724 };
8725 if (!Doxygen::inputNameLinkedMap->empty())
8726 {
8727#if USE_LIBCLANG
8729 {
8730 StringUnorderedSet processedFiles;
8731
8732 // create a dictionary with files to process
8733 StringUnorderedSet filesToProcess;
8734
8735 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8736 {
8737 for (const auto &fd : *fn)
8738 {
8739 filesToProcess.insert(fd->absFilePath().str());
8740 }
8741 }
8742 // process source files (and their include dependencies)
8743 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8744 {
8745 for (const auto &fd : *fn)
8746 {
8747 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8748 (fd->generateSourceFile() ||
8750 )
8751 )
8752 {
8753 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8754 clangParser->parse();
8755 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8756
8757 for (auto incFile : clangParser->filesInSameTU())
8758 {
8759 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8760 fd->absFilePath()!=incFile && // not same file
8761 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8762 {
8763 StringVector moreFiles;
8764 bool ambig = false;
8766 if (ifd && !ifd->isReference())
8767 {
8768 processSourceFile(ifd,*g_outputList,clangParser.get());
8769 processedFiles.insert(incFile);
8770 }
8771 }
8772 }
8773 processedFiles.insert(fd->absFilePath().str());
8774 }
8775 }
8776 }
8777 // process remaining files
8778 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8779 {
8780 for (const auto &fd : *fn)
8781 {
8782 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8783 {
8784 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8785 {
8786 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8787 clangParser->parse();
8788 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8789 }
8790 else // non C/C++ file, use built-in parser
8791 {
8792 processSourceFile(fd.get(),*g_outputList,nullptr);
8793 }
8794 }
8795 }
8796 }
8797 }
8798 else
8799#endif
8800 {
8801 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8802 if (numThreads>1)
8803 {
8804 msg("Generating code files using {} threads.\n",numThreads);
8805 struct SourceContext
8806 {
8807 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8808 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8809 FileDef *fd;
8810 bool generateSourceFile;
8811 OutputList ol;
8812 };
8813 ThreadPool threadPool(numThreads);
8814 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8815 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8816 {
8817 for (const auto &fd : *fn)
8818 {
8819 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8820 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8821 auto processFile = [ctx]()
8822 {
8823 if (ctx->generateSourceFile)
8824 {
8825 msg("Generating code for file {}...\n",ctx->fd->docName());
8826 }
8827 else
8828 {
8829 msg("Parsing code for file {}...\n",ctx->fd->docName());
8830 }
8831 StringVector filesInSameTu;
8832 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8833 if (ctx->generateSourceFile) // sources need to be shown in the output
8834 {
8835 ctx->fd->writeSourceHeader(ctx->ol);
8836 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8837 ctx->fd->writeSourceFooter(ctx->ol);
8838 }
8839 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8840 // we needed to parse the sources even if we do not show them
8841 {
8842 ctx->fd->parseSource(nullptr);
8843 }
8844 return ctx;
8845 };
8846 results.emplace_back(threadPool.queue(processFile));
8847 }
8848 }
8849 for (auto &f : results)
8850 {
8851 auto ctx = f.get();
8852 }
8853 }
8854 else // single threaded version
8855 {
8856 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8857 {
8858 for (const auto &fd : *fn)
8859 {
8860 StringVector filesInSameTu;
8861 fd->getAllIncludeFilesRecursively(filesInSameTu);
8862 processSourceFile(fd.get(),*g_outputList,nullptr);
8863 }
8864 }
8865 }
8866 }
8867 }
8868}
8869
8870//----------------------------------------------------------------------------
8871
8872static void generateFileDocs()
8873{
8874 if (Index::instance().numDocumentedFiles()==0) return;
8875
8876 if (!Doxygen::inputNameLinkedMap->empty())
8877 {
8878 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8879 if (numThreads>1) // multi threaded processing
8880 {
8881 struct DocContext
8882 {
8883 DocContext(FileDef *fd_,const OutputList &ol_)
8884 : fd(fd_), ol(ol_) {}
8885 FileDef *fd;
8886 OutputList ol;
8887 };
8888 ThreadPool threadPool(numThreads);
8889 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8890 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8891 {
8892 for (const auto &fd : *fn)
8893 {
8894 bool doc = fd->isLinkableInProject();
8895 if (doc)
8896 {
8897 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8898 auto processFile = [ctx]() {
8899 msg("Generating docs for file {}...\n",ctx->fd->docName());
8900 ctx->fd->writeDocumentation(ctx->ol);
8901 return ctx;
8902 };
8903 results.emplace_back(threadPool.queue(processFile));
8904 }
8905 }
8906 }
8907 for (auto &f : results)
8908 {
8909 auto ctx = f.get();
8910 }
8911 }
8912 else // single threaded processing
8913 {
8914 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8915 {
8916 for (const auto &fd : *fn)
8917 {
8918 bool doc = fd->isLinkableInProject();
8919 if (doc)
8920 {
8921 msg("Generating docs for file {}...\n",fd->docName());
8922 fd->writeDocumentation(*g_outputList);
8923 }
8924 }
8925 }
8926 }
8927 }
8928}
8929
8930//----------------------------------------------------------------------------
8931
8933{
8934 // add source references for class definitions
8935 for (const auto &cd : *Doxygen::classLinkedMap)
8936 {
8937 const FileDef *fd=cd->getBodyDef();
8938 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8939 {
8940 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8941 }
8942 }
8943 // add source references for concept definitions
8944 for (const auto &cd : *Doxygen::conceptLinkedMap)
8945 {
8946 const FileDef *fd=cd->getBodyDef();
8947 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8948 {
8949 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8950 }
8951 }
8952 // add source references for namespace definitions
8953 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8954 {
8955 const FileDef *fd=nd->getBodyDef();
8956 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8957 {
8958 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8959 }
8960 }
8961
8962 // add source references for member names
8963 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8964 {
8965 for (const auto &md : *mn)
8966 {
8967 //printf("class member %s: def=%s body=%d link?=%d\n",
8968 // qPrint(md->name()),
8969 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8970 // md->getStartBodyLine(),md->isLinkableInProject());
8971 const FileDef *fd=md->getBodyDef();
8972 if (fd &&
8973 md->getStartDefLine()!=-1 &&
8974 md->isLinkableInProject() &&
8976 )
8977 {
8978 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8979 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8980 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8981 }
8982 }
8983 }
8984 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8985 {
8986 for (const auto &md : *mn)
8987 {
8988 const FileDef *fd=md->getBodyDef();
8989 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8990 // qPrint(md->name()),
8991 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8992 // md->isLinkableInProject(),
8993 // Doxygen::parseSourcesNeeded);
8994 if (fd &&
8995 md->getStartDefLine()!=-1 &&
8996 md->isLinkableInProject() &&
8998 )
8999 {
9000 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
9001 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
9002 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
9003 }
9004 }
9005 }
9006}
9007
9008//----------------------------------------------------------------------------
9009
9010// add the macro definitions found during preprocessing as file members
9011static void buildDefineList()
9012{
9013 AUTO_TRACE();
9014 for (const auto &s : g_inputFiles)
9015 {
9016 auto it = Doxygen::macroDefinitions.find(s);
9018 {
9019 for (const auto &def : it->second)
9020 {
9021 auto md = createMemberDef(
9022 def.fileName,def.lineNr,def.columnNr,
9023 "#define",def.name,def.args,QCString(),
9024 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9025 ArgumentList(),ArgumentList(),"");
9026 auto mmd = toMemberDefMutable(md.get());
9027
9028 if (!def.args.isEmpty())
9029 {
9030 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
9031 }
9032 mmd->setInitializer(def.definition);
9033 mmd->setFileDef(def.fileDef);
9034 mmd->setDefinition("#define "+def.name);
9035
9036 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
9037 if (def.fileDef)
9038 {
9039 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9040 if (defMl)
9041 {
9042 const MemberDef *defMd = defMl->findRev(def.name);
9043 if (defMd) // definition already stored
9044 {
9045 mmd->setRedefineCount(defMd->redefineCount()+1);
9046 }
9047 }
9048 def.fileDef->insertMember(md.get());
9049 }
9050 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9051 mn->push_back(std::move(md));
9052 }
9053 }
9054 }
9055}
9056
9057//----------------------------------------------------------------------------
9058
9059static void sortMemberLists()
9060{
9061 // sort class member lists
9062 for (const auto &cd : *Doxygen::classLinkedMap)
9063 {
9064 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9065 if (cdm)
9066 {
9067 cdm->sortMemberLists();
9068 }
9069 }
9070
9071 // sort namespace member lists
9072 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9073 {
9075 if (ndm)
9076 {
9077 ndm->sortMemberLists();
9078 }
9079 }
9080
9081 // sort file member lists
9082 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9083 {
9084 for (const auto &fd : *fn)
9085 {
9086 fd->sortMemberLists();
9087 }
9088 }
9089
9090 // sort group member lists
9091 for (const auto &gd : *Doxygen::groupLinkedMap)
9092 {
9093 gd->sortMemberLists();
9094 }
9095
9097}
9098
9099//----------------------------------------------------------------------------
9100
9101static bool isSymbolHidden(const Definition *d)
9102{
9103 bool hidden = d->isHidden();
9104 const Definition *parent = d->getOuterScope();
9105 return parent ? hidden || isSymbolHidden(parent) : hidden;
9106}
9107
9109{
9110 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9111 if (numThreads>1)
9112 {
9113 ThreadPool threadPool(numThreads);
9114 std::vector < std::future< void > > results;
9115 // queue the work
9116 for (const auto &[name,symList] : *Doxygen::symbolMap)
9117 {
9118 for (const auto &def : symList)
9119 {
9121 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9122 {
9123 auto processTooltip = [dm]() {
9124 dm->computeTooltip();
9125 };
9126 results.emplace_back(threadPool.queue(processTooltip));
9127 }
9128 }
9129 }
9130 // wait for the results
9131 for (auto &f : results)
9132 {
9133 f.get();
9134 }
9135 }
9136 else
9137 {
9138 for (const auto &[name,symList] : *Doxygen::symbolMap)
9139 {
9140 for (const auto &def : symList)
9141 {
9143 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9144 {
9145 dm->computeTooltip();
9146 }
9147 }
9148 }
9149 }
9150}
9151
9152//----------------------------------------------------------------------------
9153
9155{
9156 for (const auto &cd : *Doxygen::classLinkedMap)
9157 {
9158 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9159 if (cdm)
9160 {
9161 cdm->setAnonymousEnumType();
9162 }
9163 }
9164}
9165
9166//----------------------------------------------------------------------------
9167
9168static void countMembers()
9169{
9170 for (const auto &cd : *Doxygen::classLinkedMap)
9171 {
9172 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9173 if (cdm)
9174 {
9175 cdm->countMembers();
9176 }
9177 }
9178
9179 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9180 {
9182 if (ndm)
9183 {
9184 ndm->countMembers();
9185 }
9186 }
9187
9188 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9189 {
9190 for (const auto &fd : *fn)
9191 {
9192 fd->countMembers();
9193 }
9194 }
9195
9196 for (const auto &gd : *Doxygen::groupLinkedMap)
9197 {
9198 gd->countMembers();
9199 }
9200
9201 auto &mm = ModuleManager::instance();
9202 mm.countMembers();
9203}
9204
9205
9206//----------------------------------------------------------------------------
9207// generate the documentation for all classes
9208
9209static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9210{
9211 AUTO_TRACE();
9212 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9213 if (numThreads>1) // multi threaded processing
9214 {
9215 struct DocContext
9216 {
9217 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9218 : cd(cd_), ol(ol_) {}
9219 ClassDefMutable *cd;
9220 OutputList ol;
9221 };
9222 ThreadPool threadPool(numThreads);
9223 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9224 for (const auto &cd : classList)
9225 {
9226 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9227 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9228 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9229 )
9230 {
9231 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9232 auto processFile = [ctx]()
9233 {
9234 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9235
9236 // skip external references, anonymous compounds and
9237 // template instances
9238 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9239 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9240 {
9241 ctx->cd->writeDocumentation(ctx->ol);
9242 ctx->cd->writeMemberList(ctx->ol);
9243 }
9244
9245 // even for undocumented classes, the inner classes can be documented.
9246 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9247 return ctx;
9248 };
9249 results.emplace_back(threadPool.queue(processFile));
9250 }
9251 }
9252 for (auto &f : results)
9253 {
9254 auto ctx = f.get();
9255 }
9256 }
9257 else // single threaded processing
9258 {
9259 for (const auto &cd : classList)
9260 {
9261 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9262 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9263 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9264 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9265 )
9266 {
9267 // skip external references, anonymous compounds and
9268 // template instances
9269 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9270 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9271 {
9272 msg("Generating docs for compound {}...\n",cd->displayName());
9273
9274 cd->writeDocumentation(*g_outputList);
9275 cd->writeMemberList(*g_outputList);
9276 }
9277 // even for undocumented classes, the inner classes can be documented.
9278 cd->writeDocumentationForInnerClasses(*g_outputList);
9279 }
9280 }
9281 }
9282}
9283
9284static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9285{
9286 list.push_back(cd);
9287 for (const auto &innerCdi : cd->getClasses())
9288 {
9289 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9290 if (innerCd)
9291 {
9292 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9293 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9294 innerCd->isEmbeddedInOuterScope());
9295 }
9296 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9297 protectionLevelVisible(innerCd->protection()) &&
9298 !innerCd->isEmbeddedInOuterScope()
9299 )
9300 {
9301 list.push_back(innerCd);
9302 addClassAndNestedClasses(list,innerCd);
9303 }
9304 }
9305}
9306
9308{
9309 std::vector<ClassDefMutable*> classList;
9310 for (const auto &cdi : *Doxygen::classLinkedMap)
9311 {
9312 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9313 if (cd && (cd->getOuterScope()==nullptr ||
9315 {
9316 addClassAndNestedClasses(classList,cd);
9317 }
9318 }
9319 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9320 {
9321 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9322 if (cd && (cd->getOuterScope()==nullptr ||
9324 {
9325 addClassAndNestedClasses(classList,cd);
9326 }
9327 }
9328 generateDocsForClassList(classList);
9329}
9330
9331//----------------------------------------------------------------------------
9332
9334{
9335 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9336 {
9338
9339 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9340 if (cd &&
9341 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9342 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9343 ) && !cd->isHidden() && cd->isLinkableInProject()
9344 )
9345 {
9346 msg("Generating docs for concept {}...\n",cd->displayName());
9348 }
9349 }
9350}
9351
9352//----------------------------------------------------------------------------
9353
9355{
9356 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9357 {
9358 for (const auto &imd : *mn)
9359 {
9360 MemberDefMutable *md = toMemberDefMutable(imd.get());
9361 //static int count=0;
9362 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9363 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9364 { // no documentation yet
9365 const MemberDef *bmd = md->reimplements();
9366 while (bmd && bmd->documentation().isEmpty() &&
9367 bmd->briefDescription().isEmpty()
9368 )
9369 { // search up the inheritance tree for a documentation member
9370 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9371 bmd = bmd->reimplements();
9372 }
9373 if (bmd) // copy the documentation from the reimplemented member
9374 {
9375 md->setInheritsDocsFrom(bmd);
9376 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9378 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9379 md->copyArgumentNames(bmd);
9381 }
9382 }
9383 }
9384 }
9385}
9386
9387//----------------------------------------------------------------------------
9388
9390{
9391 // for each file
9392 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9393 {
9394 for (const auto &fd : *fn)
9395 {
9396 fd->combineUsingRelations();
9397 }
9398 }
9399
9400 // for each namespace
9401 NamespaceDefSet visitedNamespaces;
9402 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9403 {
9405 if (ndm)
9406 {
9407 ndm->combineUsingRelations(visitedNamespaces);
9408 }
9409 }
9410}
9411
9412//----------------------------------------------------------------------------
9413
9415{
9416 // for each class
9417 for (const auto &cd : *Doxygen::classLinkedMap)
9418 {
9419 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9420 if (cdm)
9421 {
9423 }
9424 }
9425 // for each file
9426 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9427 {
9428 for (const auto &fd : *fn)
9429 {
9430 fd->addMembersToMemberGroup();
9431 }
9432 }
9433 // for each namespace
9434 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9435 {
9437 if (ndm)
9438 {
9440 }
9441 }
9442 // for each group
9443 for (const auto &gd : *Doxygen::groupLinkedMap)
9444 {
9445 gd->addMembersToMemberGroup();
9446 }
9448}
9449
9450//----------------------------------------------------------------------------
9451
9453{
9454 // for each class
9455 for (const auto &cd : *Doxygen::classLinkedMap)
9456 {
9457 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9458 if (cdm)
9459 {
9461 }
9462 }
9463 // for each file
9464 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9465 {
9466 for (const auto &fd : *fn)
9467 {
9468 fd->distributeMemberGroupDocumentation();
9469 }
9470 }
9471 // for each namespace
9472 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9473 {
9475 if (ndm)
9476 {
9478 }
9479 }
9480 // for each group
9481 for (const auto &gd : *Doxygen::groupLinkedMap)
9482 {
9483 gd->distributeMemberGroupDocumentation();
9484 }
9486}
9487
9488//----------------------------------------------------------------------------
9489
9491{
9492 // for each class
9493 for (const auto &cd : *Doxygen::classLinkedMap)
9494 {
9495 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9496 if (cdm)
9497 {
9499 }
9500 }
9501 // for each concept
9502 for (const auto &cd : *Doxygen::conceptLinkedMap)
9503 {
9504 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9505 if (cdm)
9506 {
9508 }
9509 }
9510 // for each file
9511 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9512 {
9513 for (const auto &fd : *fn)
9514 {
9515 fd->findSectionsInDocumentation();
9516 }
9517 }
9518 // for each namespace
9519 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9520 {
9522 if (ndm)
9523 {
9525 }
9526 }
9527 // for each group
9528 for (const auto &gd : *Doxygen::groupLinkedMap)
9529 {
9530 gd->findSectionsInDocumentation();
9531 }
9532 // for each page
9533 for (const auto &pd : *Doxygen::pageLinkedMap)
9534 {
9535 pd->findSectionsInDocumentation();
9536 }
9537 // for each directory
9538 for (const auto &dd : *Doxygen::dirLinkedMap)
9539 {
9540 dd->findSectionsInDocumentation();
9541 }
9543 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9544}
9545
9546//----------------------------------------------------------------------
9547
9548
9550{
9551 // remove all references to classes from the cache
9552 // as there can be new template instances in the inheritance path
9553 // to this class. Optimization: only remove those classes that
9554 // have inheritance instances as direct or indirect sub classes.
9556
9557 // remove all cached typedef resolutions whose target is a
9558 // template class as this may now be a template instance
9559 // for each global function name
9560 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9561 {
9562 // for each function with that name
9563 for (const auto &ifmd : *fn)
9564 {
9565 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9566 if (fmd && fmd->isTypedefValCached())
9567 {
9568 const ClassDef *cd = fmd->getCachedTypedefVal();
9569 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9570 }
9571 }
9572 }
9573 // for each class method name
9574 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9575 {
9576 // for each function with that name
9577 for (const auto &imd : *nm)
9578 {
9579 MemberDefMutable *md = toMemberDefMutable(imd.get());
9580 if (md && md->isTypedefValCached())
9581 {
9582 const ClassDef *cd = md->getCachedTypedefVal();
9583 if (cd->isTemplate()) md->invalidateTypedefValCache();
9584 }
9585 }
9586 }
9587}
9588
9589//----------------------------------------------------------------------------
9590
9592{
9593 // Remove all unresolved references to classes from the cache.
9594 // This is needed before resolving the inheritance relations, since
9595 // it would otherwise not find the inheritance relation
9596 // for C in the example below, as B::I was already found to be unresolvable
9597 // (which is correct if you ignore the inheritance relation between A and B).
9598 //
9599 // class A { class I {} };
9600 // class B : public A {};
9601 // class C : public B::I {};
9603
9604 // for each class method name
9605 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9606 {
9607 // for each function with that name
9608 for (const auto &imd : *nm)
9609 {
9610 MemberDefMutable *md = toMemberDefMutable(imd.get());
9611 if (md)
9612 {
9614 }
9615 }
9616 }
9617
9618}
9619
9620//----------------------------------------------------------------------------
9621// Returns TRUE if the entry and member definition have equal file names,
9622// otherwise FALSE.
9623
9624static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9625{
9626 if (const FileDef *fd = md->getFileDef())
9627 {
9628 return fd->absFilePath() == root->fileName;
9629 }
9630 return false;
9631}
9632
9633//----------------------------------------------------------------------------
9634
9635static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9636{
9637 md->setDocumentation(root->doc,root->docFile,root->docLine);
9638 md->setDocsForDefinition(!root->proto);
9639 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9640 if (md->inbodyDocumentation().isEmpty())
9641 {
9643 }
9644 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9645 {
9646 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9647 md->setBodyDef(root->fileDef());
9648 }
9650 md->setMaxInitLines(root->initLines);
9652 md->setRefItems(root->sli);
9653 md->setRequirementReferences(root->rqli);
9654 md->addQualifiers(root->qualifiers);
9655 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9656 addMemberToGroups(root,md);
9658}
9659
9660//----------------------------------------------------------------------------
9661
9663{
9664 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9665 {
9666 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9667 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9668
9669 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9670 {
9671 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9672 "#define",root->name,root->args,QCString(),
9673 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9674 ArgumentList(),ArgumentList(),"");
9675 auto mmd = toMemberDefMutable(md.get());
9676 mmd->setTagInfo(root->tagInfo());
9677 mmd->setLanguage(root->lang);
9678 mmd->addQualifiers(root->qualifiers);
9679 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9680 mmd->setFileDef(root->parent()->fileDef());
9681 //printf("Adding member=%s\n",qPrint(md->name()));
9683 mn->push_back(std::move(md));
9684 }
9686 if (mn)
9687 {
9688 int count=0;
9689 for (const auto &md : *mn)
9690 {
9691 if (md->memberType()==MemberType::Define) count++;
9692 }
9693 if (count==1)
9694 {
9695 for (const auto &imd : *mn)
9696 {
9697 MemberDefMutable *md = toMemberDefMutable(imd.get());
9698 if (md && md->memberType()==MemberType::Define)
9699 {
9700 addDefineDoc(root,md);
9701 }
9702 }
9703 }
9704 else if (count>1 &&
9705 (!root->doc.isEmpty() ||
9706 !root->brief.isEmpty() ||
9707 root->bodyLine!=-1
9708 )
9709 )
9710 // multiple defines don't know where to add docs
9711 // but maybe they are in different files together with their documentation
9712 {
9713 for (const auto &imd : *mn)
9714 {
9715 MemberDefMutable *md = toMemberDefMutable(imd.get());
9716 if (md && md->memberType()==MemberType::Define)
9717 {
9718 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9719 // doc and define in the same file or group assume they belong together.
9720 {
9721 addDefineDoc(root,md);
9722 }
9723 }
9724 }
9725 //warn("define {} found in the following files:\n",root->name);
9726 //warn("Cannot determine where to add the documentation found "
9727 // "at line {} of file {}. \n",
9728 // root->startLine,root->fileName);
9729 }
9730 }
9731 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9732 {
9733 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9734 if (preEnabled)
9735 {
9736 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9737 }
9738 else
9739 {
9740 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9741 }
9742 }
9743 }
9744 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9745}
9746
9747//----------------------------------------------------------------------------
9748
9749static void findDirDocumentation(const Entry *root)
9750{
9751 if (root->section.isDirDoc())
9752 {
9753 QCString normalizedName = root->name;
9754 normalizedName = substitute(normalizedName,"\\","/");
9755 //printf("root->docFile=%s normalizedName=%s\n",
9756 // qPrint(root->docFile),qPrint(normalizedName));
9757 if (root->docFile==normalizedName) // current dir?
9758 {
9759 int lastSlashPos=normalizedName.findRev('/');
9760 if (lastSlashPos!=-1) // strip file name
9761 {
9762 normalizedName=normalizedName.left(lastSlashPos);
9763 }
9764 }
9765 if (normalizedName.at(normalizedName.length()-1)!='/')
9766 {
9767 normalizedName+='/';
9768 }
9769 DirDef *matchingDir=nullptr;
9770 for (const auto &dir : *Doxygen::dirLinkedMap)
9771 {
9772 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9773 if (dir->name().right(normalizedName.length())==normalizedName)
9774 {
9775 if (matchingDir)
9776 {
9777 warn(root->fileName,root->startLine,
9778 "\\dir command matches multiple directories.\n"
9779 " Applying the command for directory {}\n"
9780 " Ignoring the command for directory {}",
9781 matchingDir->name(),dir->name()
9782 );
9783 }
9784 else
9785 {
9786 matchingDir=dir.get();
9787 }
9788 }
9789 }
9790 if (matchingDir)
9791 {
9792 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9793 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9794 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9795 matchingDir->setRefItems(root->sli);
9796 matchingDir->setRequirementReferences(root->rqli);
9797 matchingDir->addSectionsToDefinition(root->anchors);
9798 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9799 addDirToGroups(root,matchingDir);
9800 }
9801 else
9802 {
9803 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9804 }
9805 }
9806 for (const auto &e : root->children()) findDirDocumentation(e.get());
9807}
9808
9809//----------------------------------------------------------------------------
9811{
9812 if (root->section.isRequirementDoc())
9813 {
9815 }
9816 for (const auto &e : root->children()) buildRequirementsList(e.get());
9817}
9818
9819//----------------------------------------------------------------------------
9820// create a (sorted) list of separate documentation pages
9821
9822static void buildPageList(Entry *root)
9823{
9824 if (root->section.isPageDoc())
9825 {
9826 if (!root->name.isEmpty())
9827 {
9828 addRelatedPage(root);
9829 }
9830 }
9831 else if (root->section.isMainpageDoc())
9832 {
9833 QCString title=root->args.stripWhiteSpace();
9834 if (title.isEmpty()) title=theTranslator->trMainPage();
9835 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9836 QCString name = "index";
9837 addRefItem(root->sli,
9838 name,
9839 theTranslator->trPage(TRUE,TRUE),
9840 name,
9841 title,
9842 QCString(),nullptr
9843 );
9844 }
9845 for (const auto &e : root->children()) buildPageList(e.get());
9846}
9847
9848// search for the main page defined in this project
9849static void findMainPage(Entry *root)
9850{
9851 if (root->section.isMainpageDoc())
9852 {
9853 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9854 {
9855 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9856 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9857 QCString title=root->args.stripWhiteSpace();
9858 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9859 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9860 QCString indexName="index";
9862 indexName, root->brief+root->doc+root->inbodyDocs,title);
9863 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9864 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9865 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9866 Doxygen::mainPage->setFileName(indexName);
9867 Doxygen::mainPage->setLocalToc(root->localToc);
9869
9871 if (si)
9872 {
9873 if (!si->ref().isEmpty()) // we are from a tag file
9874 {
9875 // a page name is a label as well! but should no be double either
9877 Doxygen::mainPage->name(),
9878 indexName,
9879 root->startLine,
9880 Doxygen::mainPage->title(),
9882 0); // level 0
9883 }
9884 else if (si->lineNr() != -1)
9885 {
9886 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9887 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9888 }
9889 else
9890 {
9891 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9892 Doxygen::mainPage->name(),si->fileName());
9893 }
9894 }
9895 else
9896 {
9897 // a page name is a label as well! but should no be double either
9899 Doxygen::mainPage->name(),
9900 indexName,
9901 root->startLine,
9902 Doxygen::mainPage->title(),
9904 0); // level 0
9905 }
9906 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9907 }
9908 else if (root->tagInfo()==nullptr)
9909 {
9910 warn(root->fileName,root->startLine,
9911 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9912 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9913 }
9914 }
9915 for (const auto &e : root->children()) findMainPage(e.get());
9916}
9917
9918// search for the main page imported via tag files and add only the section labels
9919static void findMainPageTagFiles(Entry *root)
9920{
9921 if (root->section.isMainpageDoc())
9922 {
9923 if (Doxygen::mainPage && root->tagInfo())
9924 {
9925 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9926 }
9927 }
9928 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9929}
9930
9931static void computePageRelations(Entry *root)
9932{
9933 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9934 {
9935 PageDef *pd = root->section.isPageDoc() ?
9936 Doxygen::pageLinkedMap->find(root->name) :
9937 Doxygen::mainPage.get();
9938 if (pd)
9939 {
9940 for (const BaseInfo &bi : root->extends)
9941 {
9942 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9943 if (pd==subPd)
9944 {
9945 term("page defined {} with label {} is a direct "
9946 "subpage of itself! Please remove this cyclic dependency.\n",
9947 warn_line(pd->docFile(),pd->docLine()),pd->name());
9948 }
9949 else if (subPd)
9950 {
9951 pd->addInnerCompound(subPd);
9952 //printf("*** Added subpage relation: %s->%s\n",
9953 // qPrint(pd->name()),qPrint(subPd->name()));
9954 }
9955 }
9956 }
9957 }
9958 for (const auto &e : root->children()) computePageRelations(e.get());
9959}
9960
9962{
9963 for (const auto &pd : *Doxygen::pageLinkedMap)
9964 {
9965 Definition *ppd = pd->getOuterScope();
9966 while (ppd)
9967 {
9968 if (ppd==pd.get())
9969 {
9970 term("page defined {} with label {} is a subpage "
9971 "of itself! Please remove this cyclic dependency.\n",
9972 warn_line(pd->docFile(),pd->docLine()),pd->name());
9973 }
9974 ppd=ppd->getOuterScope();
9975 }
9976 }
9977}
9978
9979//----------------------------------------------------------------------------
9980
9982{
9983 for (const auto &si : SectionManager::instance())
9984 {
9985 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9986 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9987 // qPrint(si->fileName));
9988 PageDef *pd=nullptr;
9989
9990 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9991 // different files, so the resulting section's all have the wrong file
9992 // name (not from the todo/test/bug/deprecated list, but from the file in
9993 // which they are defined). We correct this here by looking at the
9994 // generated section labels!
9996 {
9997 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9998 if (si->label().left(label.length())==label)
9999 {
10000 si->setFileName(rl->listName());
10001 si->setGenerated(TRUE);
10002 break;
10003 }
10004 }
10005
10006 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10007 if (!si->generated())
10008 {
10009 // if this section is in a page and the page is in a group, then we
10010 // have to adjust the link file name to point to the group.
10011 if (!si->fileName().isEmpty() &&
10012 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
10013 pd->getGroupDef())
10014 {
10015 si->setFileName(pd->getGroupDef()->getOutputFileBase());
10016 }
10017
10018 if (si->definition())
10019 {
10020 // TODO: there should be one function in Definition that returns
10021 // the file to link to, so we can avoid the following tests.
10022 const GroupDef *gd=nullptr;
10023 if (si->definition()->definitionType()==Definition::TypeMember)
10024 {
10025 gd = (toMemberDef(si->definition()))->getGroupDef();
10026 }
10027
10028 if (gd)
10029 {
10030 si->setFileName(gd->getOutputFileBase());
10031 }
10032 else
10033 {
10034 //si->fileName=si->definition->getOutputFileBase();
10035 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
10036 }
10037 }
10038 }
10039 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10040 }
10041}
10042
10043
10044
10045//----------------------------------------------------------------------------
10046// generate all separate documentation pages
10047
10048
10049static void generatePageDocs()
10050{
10051 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10052 if (Index::instance().numDocumentedPages()==0) return;
10053 for (const auto &pd : *Doxygen::pageLinkedMap)
10054 {
10055 if (!pd->getGroupDef() && !pd->isReference())
10056 {
10057 msg("Generating docs for page {}...\n",pd->name());
10058 pd->writeDocumentation(*g_outputList);
10059 }
10060 }
10061}
10062
10063//----------------------------------------------------------------------------
10064// create a (sorted) list & dictionary of example pages
10065
10066static void buildExampleList(Entry *root)
10067{
10068 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
10069 {
10070 if (Doxygen::exampleLinkedMap->find(root->name))
10071 {
10072 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10073 }
10074 else
10075 {
10076 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
10077 createPageDef(root->fileName,root->startLine,
10078 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10079 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10080 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
10082 pd->setLanguage(root->lang);
10083 pd->setShowLineNo(root->section.isExampleLineno());
10084
10085 //we don't add example to groups
10086 //addExampleToGroups(root,pd);
10087 }
10088 }
10089 for (const auto &e : root->children()) buildExampleList(e.get());
10090}
10091
10092//----------------------------------------------------------------------------
10093// prints the Entry tree (for debugging)
10094
10095void printNavTree(Entry *root,int indent)
10096{
10098 {
10099 QCString indentStr;
10100 indentStr.fill(' ',indent);
10101 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10102 indentStr.isEmpty()?"":indentStr,
10103 root->name.isEmpty()?"<empty>":root->name,
10104 root->fileName,root->startLine,
10105 root->section.to_string(),
10106 root->spec.to_string());
10107 for (const auto &e : root->children())
10108 {
10109 printNavTree(e.get(),indent+2);
10110 }
10111 }
10112}
10113
10114
10115//----------------------------------------------------------------------------
10116// prints the Sections tree (for debugging)
10117
10119{
10121 {
10122 for (const auto &si : SectionManager::instance())
10123 {
10124 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10125 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10126 }
10127 }
10128}
10129
10130
10131//----------------------------------------------------------------------------
10132// generate the example documentation
10133
10135{
10136 g_outputList->disable(OutputType::Man);
10137 for (const auto &pd : *Doxygen::exampleLinkedMap)
10138 {
10139 msg("Generating docs for example {}...\n",pd->name());
10140 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10141 if (lang != SrcLangExt::Unknown)
10142 {
10143 QCString ext = getFileNameExtension(pd->name());
10144 auto intf = Doxygen::parserManager->getCodeParser(ext);
10145 intf->resetCodeParserState();
10146 }
10147 QCString n=pd->getOutputFileBase();
10148 startFile(*g_outputList,n,false,n,pd->name());
10150 g_outputList->docify(pd->name());
10152 g_outputList->startContents();
10153 QCString lineNoOptStr;
10154 if (pd->showLineNo())
10155 {
10156 lineNoOptStr="{lineno}";
10157 }
10158 g_outputList->generateDoc(pd->docFile(), // file
10159 pd->docLine(), // startLine
10160 pd.get(), // context
10161 nullptr, // memberDef
10162 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
10163 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10164 DocOptions()
10165 .setIndexWords(true)
10166 .setExample(pd->name()));
10167 endFile(*g_outputList); // contains g_outputList->endContents()
10168 }
10170}
10171
10172//----------------------------------------------------------------------------
10173// generate module pages
10174
10176{
10177 for (const auto &gd : *Doxygen::groupLinkedMap)
10178 {
10179 if (!gd->isReference())
10180 {
10181 gd->writeDocumentation(*g_outputList);
10182 }
10183 }
10184}
10185
10186//----------------------------------------------------------------------------
10187// generate module pages
10188
10190{
10191 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10192 if (numThreads>1) // multi threaded processing
10193 {
10194 struct DocContext
10195 {
10196 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10197 : cdm(cdm_), ol(ol_) {}
10198 ClassDefMutable *cdm;
10199 OutputList ol;
10200 };
10201 ThreadPool threadPool(numThreads);
10202 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10203 // for each class in the namespace...
10204 for (const auto &cd : classList)
10205 {
10207 if (cdm)
10208 {
10209 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10210 auto processFile = [ctx]()
10211 {
10212 if ( ( ctx->cdm->isLinkableInProject() &&
10213 !ctx->cdm->isImplicitTemplateInstance()
10214 ) // skip external references, anonymous compounds and
10215 // template instances and nested classes
10216 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10217 )
10218 {
10219 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10220 ctx->cdm->writeDocumentation(ctx->ol);
10221 ctx->cdm->writeMemberList(ctx->ol);
10222 }
10223 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10224 return ctx;
10225 };
10226 results.emplace_back(threadPool.queue(processFile));
10227 }
10228 }
10229 // wait for the results
10230 for (auto &f : results)
10231 {
10232 auto ctx = f.get();
10233 }
10234 }
10235 else // single threaded processing
10236 {
10237 // for each class in the namespace...
10238 for (const auto &cd : classList)
10239 {
10241 if (cdm)
10242 {
10243 if ( ( cd->isLinkableInProject() &&
10244 !cd->isImplicitTemplateInstance()
10245 ) // skip external references, anonymous compounds and
10246 // template instances and nested classes
10247 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10248 )
10249 {
10250 msg("Generating docs for compound {}...\n",cd->displayName());
10251
10254 }
10256 }
10257 }
10258 }
10259}
10260
10262{
10263 // for each concept in the namespace...
10264 for (const auto &cd : conceptList)
10265 {
10267 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10268 {
10269 msg("Generating docs for concept {}...\n",cd->name());
10271 }
10272 }
10273}
10274
10276{
10277 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10278
10279 //writeNamespaceIndex(*g_outputList);
10280
10281 // for each namespace...
10282 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10283 {
10284 if (nd->isLinkableInProject())
10285 {
10287 if (ndm)
10288 {
10289 msg("Generating docs for namespace {}\n",nd->displayName());
10291 }
10292 }
10293
10294 generateNamespaceClassDocs(nd->getClasses());
10295 if (sliceOpt)
10296 {
10297 generateNamespaceClassDocs(nd->getInterfaces());
10298 generateNamespaceClassDocs(nd->getStructs());
10299 generateNamespaceClassDocs(nd->getExceptions());
10300 }
10301 generateNamespaceConceptDocs(nd->getConcepts());
10302 }
10303}
10304
10306{
10307 std::string oldDir = Dir::currentDirPath();
10308 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10311 {
10312 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10313 }
10314 Dir::setCurrent(oldDir);
10315}
10316
10318{
10319 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10320 std::string oldDir = Dir::currentDirPath();
10321 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10322
10323 QCString qhgLocation=Config_getString(QHG_LOCATION);
10324 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10325 {
10326 // run qhelpgenerator -v and extract the Qt version used
10327 QCString cmd=qhgLocation+ " -v 2>&1";
10328 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10329 FILE *f=Portable::popen(cmd,"r");
10330 if (!f)
10331 {
10332 err("could not execute {}\n",qhgLocation);
10333 }
10334 else
10335 {
10336 const size_t bufSize = 1024;
10337 char inBuf[bufSize+1];
10338 size_t numRead=fread(inBuf,1,bufSize,f);
10339 inBuf[numRead] = '\0';
10340 Debug::print(Debug::Qhp,0,"{}",inBuf);
10342
10343 int qtVersion=0;
10344 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10345 reg::Match match;
10346 std::string s = inBuf;
10347 if (reg::search(s,match,versionReg))
10348 {
10349 qtVersion = 10000*QCString(match[1].str()).toInt() +
10350 100*QCString(match[2].str()).toInt() +
10351 QCString(match[3].str()).toInt();
10352 }
10353 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10354 {
10355 // dump the output of qhelpgenerator -c file.qhp
10356 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10357 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10358 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10359 f=Portable::popen(cmd,"r");
10360 if (!f)
10361 {
10362 err("could not execute {}\n",qhgLocation);
10363 }
10364 else
10365 {
10366 std::string output;
10367 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10368 {
10369 inBuf[numRead] = '\0';
10370 output += inBuf;
10371 }
10373 Debug::print(Debug::Qhp,0,"{}",output);
10374 }
10375 }
10376 }
10377 }
10378
10379 if (Portable::system(qhgLocation, args, FALSE))
10380 {
10381 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10382 }
10383 Dir::setCurrent(oldDir);
10384}
10385
10386//----------------------------------------------------------------------------
10387
10389{
10390 // check dot path
10391 QCString dotPath = Config_getString(DOT_PATH);
10392 if (!dotPath.isEmpty())
10393 {
10394 FileInfo fi(dotPath.str());
10395 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10396 {
10397 dotPath = dotPath+"/dot"+Portable::commandExtension();
10398 FileInfo dp(dotPath.str());
10399 if (!dp.exists() || !dp.isFile())
10400 {
10401 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10402 dotPath = "dot";
10403 dotPath += Portable::commandExtension();
10404 }
10405 }
10406#if defined(_WIN32) // convert slashes
10407 size_t l=dotPath.length();
10408 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10409#endif
10410 }
10411 else
10412 {
10413 dotPath = "dot";
10414 dotPath += Portable::commandExtension();
10415 }
10416 Doxygen::verifiedDotPath = dotPath;
10418}
10419
10420//----------------------------------------------------------------------------
10421
10422/*! Generate a template version of the configuration file.
10423 * If the \a shortList parameter is TRUE a configuration file without
10424 * comments will be generated.
10425 */
10426static void generateConfigFile(const QCString &configFile,bool shortList,
10427 bool updateOnly=FALSE)
10428{
10429 std::ofstream f;
10430 bool fileOpened=openOutputFile(configFile,f);
10431 bool writeToStdout=configFile=="-";
10432 if (fileOpened)
10433 {
10434 TextStream t(&f);
10435 Config::writeTemplate(t,shortList,updateOnly);
10436 if (!writeToStdout)
10437 {
10438 if (!updateOnly)
10439 {
10440 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10441 msg("Now edit the configuration file and enter\n\n");
10442 if (configFile!="Doxyfile" && configFile!="doxyfile")
10443 msg(" doxygen {}\n\n",configFile);
10444 else
10445 msg(" doxygen\n\n");
10446 msg("to generate the documentation for your project\n\n");
10447 }
10448 else
10449 {
10450 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10451 }
10452 }
10453 }
10454 else
10455 {
10456 term("Cannot open file {} for writing\n",configFile);
10457 }
10458}
10459
10461{
10462 std::ofstream f;
10463 bool fileOpened=openOutputFile("-",f);
10464 if (fileOpened)
10465 {
10466 TextStream t(&f);
10467 Config::compareDoxyfile(t,diffList);
10468 }
10469 else
10470 {
10471 term("Cannot open stdout for writing\n");
10472 }
10473}
10474
10475//----------------------------------------------------------------------------
10476// read and parse a tag file
10477
10478static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10479{
10480 QCString fileName;
10481 QCString destName;
10482 int eqPos = tagLine.find('=');
10483 if (eqPos!=-1) // tag command contains a destination
10484 {
10485 fileName = tagLine.left(eqPos).stripWhiteSpace();
10486 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10487 if (fileName.isEmpty() || destName.isEmpty()) return;
10488 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10489 }
10490 else
10491 {
10492 fileName = tagLine;
10493 }
10494
10495 FileInfo fi(fileName.str());
10496 if (!fi.exists() || !fi.isFile())
10497 {
10498 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10499 return;
10500 }
10501
10502 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10503
10504 Doxygen::tagFileSet.emplace(fi.absFilePath());
10505
10506 if (!destName.isEmpty())
10507 {
10508 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10509 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10510 }
10511 else
10512 {
10513 msg("Reading tag file '{}'...\n",fileName);
10514 }
10515
10516 parseTagFile(root,fi.absFilePath().c_str());
10517}
10518
10519//----------------------------------------------------------------------------
10521{
10522 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10523 for (const auto &sheet : latexExtraStyleSheet)
10524 {
10525 std::string fileName = sheet;
10526 if (!fileName.empty())
10527 {
10528 FileInfo fi(fileName);
10529 if (!fi.exists())
10530 {
10531 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10532 }
10533 else if (fi.isDir())
10534 {
10535 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10536 }
10537 else
10538 {
10539 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10541 {
10542 destFileName += LATEX_STYLE_EXTENSION;
10543 }
10544 copyFile(fileName, destFileName);
10545 }
10546 }
10547 }
10548}
10549
10550//----------------------------------------------------------------------------
10551static void copyStyleSheet()
10552{
10553 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10554 if (!htmlStyleSheet.isEmpty())
10555 {
10556 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10557 {
10558 FileInfo fi(htmlStyleSheet.str());
10559 if (!fi.exists())
10560 {
10561 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10562 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10563 }
10564 else if (fi.isDir())
10565 {
10566 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10567 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10568 }
10569 else
10570 {
10571 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10572 copyFile(htmlStyleSheet,destFileName);
10573 }
10574 }
10575 }
10576 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10577 for (const auto &sheet : htmlExtraStyleSheet)
10578 {
10579 QCString fileName(sheet);
10580 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10581 {
10582 FileInfo fi(fileName.str());
10583 if (!fi.exists())
10584 {
10585 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10586 }
10587 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10588 {
10589 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10590 }
10591 else if (fi.isDir())
10592 {
10593 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10594 }
10595 else
10596 {
10597 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10598 copyFile(fileName, destFileName);
10599 }
10600 }
10601 }
10602}
10603
10604static void copyLogo(const QCString &outputOption, bool toIndex)
10605{
10606 QCString projectLogo = projectLogoFile();
10607 if (!projectLogo.isEmpty())
10608 {
10609 FileInfo fi(projectLogo.str());
10610 if (!fi.exists())
10611 {
10612 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10613 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10614 }
10615 else if (fi.isDir())
10616 {
10617 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10618 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10619 }
10620 else
10621 {
10622 QCString destFileName = outputOption+"/"+fi.fileName();
10623 copyFile(projectLogo,destFileName);
10624 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10625 }
10626 }
10627}
10628
10629static void copyIcon(const QCString &outputOption, bool toIndex)
10630{
10631 QCString projectIcon = Config_getString(PROJECT_ICON);
10632 if (!projectIcon.isEmpty())
10633 {
10634 FileInfo fi(projectIcon.str());
10635 if (!fi.exists())
10636 {
10637 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10638 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10639 }
10640 else if (fi.isDir())
10641 {
10642 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10643 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10644 }
10645 else
10646 {
10647 QCString destFileName = outputOption+"/"+fi.fileName();
10648 copyFile(projectIcon,destFileName);
10649 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10650 }
10651 }
10652}
10653
10654static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption, bool toIndex)
10655{
10656 for (const auto &fileName : files)
10657 {
10658 if (!fileName.empty())
10659 {
10660 FileInfo fi(fileName);
10661 if (!fi.exists())
10662 {
10663 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10664 }
10665 else if (fi.isDir())
10666 {
10667 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10668 }
10669 else
10670 {
10671 QCString destFileName = outputOption+"/"+fi.fileName();
10672 copyFile(fileName, destFileName);
10673 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10674 }
10675 }
10676 }
10677}
10678
10679//----------------------------------------------------------------------------
10680
10682{
10683 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10684 {
10685 struct FileEntry
10686 {
10687 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10688 QCString path;
10689 FileDef *fileDef;
10690 };
10691
10692 // collect the entry for which to compute the longest common prefix (LCP) of the path
10693 std::vector<FileEntry> fileEntries;
10694 for (const auto &fd : *fn)
10695 {
10696 if (!fd->isReference()) // skip external references
10697 {
10698 fileEntries.emplace_back(fd->getPath(),fd.get());
10699 }
10700 }
10701
10702 size_t size = fileEntries.size();
10703
10704 if (size==1) // name if unique, so diskname is simply the name
10705 {
10706 FileDef *fd = fileEntries[0].fileDef;
10707 fd->setDiskName(fn->fileName());
10708 }
10709 else if (size>1) // multiple occurrences of the same file name
10710 {
10711 // sort the array
10712 std::stable_sort(fileEntries.begin(),
10713 fileEntries.end(),
10714 [](const FileEntry &fe1,const FileEntry &fe2)
10715 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10716 );
10717
10718 // since the entries are sorted, the common prefix of the whole array is same
10719 // as the common prefix between the first and last entry
10720 const FileEntry &first = fileEntries[0];
10721 const FileEntry &last = fileEntries[size-1];
10722 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10723 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10724 int j=0;
10725 int i=0;
10726 for (i=0;i<first_path_size && i<last_path_size;i++)
10727 {
10728 if (first.path[i]=='/') j=i;
10729 if (first.path[i]!=last.path[i]) break;
10730 }
10731 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10732 {
10733 // case first='some/path' and last='some/path/more' => match is 'some/path'
10734 j=first_path_size;
10735 }
10736 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10737 {
10738 // case first='some/path/more' and last='some/path' => match is 'some/path'
10739 j=last_path_size;
10740 }
10741
10742 // add non-common part of the path to the name
10743 for (auto &fileEntry : fileEntries)
10744 {
10745 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10746 fileEntry.fileDef->setName(prefix+fn->fileName());
10747 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10748 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10749 }
10750 }
10751 }
10752}
10753
10754
10755
10756//----------------------------------------------------------------------------
10757
10758static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10759{
10760 QCString fileName=fn;
10761 QCString extension;
10762 int sep = fileName.findRev('/');
10763 int ei = fileName.findRev('.');
10764 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10765 {
10766 extension=fileName.right(fileName.length()-ei);
10767 }
10768 else
10769 {
10770 extension = ".no_extension";
10771 }
10772
10773 return Doxygen::parserManager->getOutlineParser(extension);
10774}
10775
10776static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10777 FileDef *fd,const QCString &fn,
10778 ClangTUParser *clangParser,bool newTU)
10779{
10780 QCString fileName=fn;
10781 AUTO_TRACE("fileName={}",fileName);
10782 QCString extension;
10783 int ei = fileName.findRev('.');
10784 if (ei!=-1)
10785 {
10786 extension=fileName.right(fileName.length()-ei);
10787 }
10788 else
10789 {
10790 extension = ".no_extension";
10791 }
10792
10793 FileInfo fi(fileName.str());
10794 std::string preBuf;
10795
10796 if (Config_getBool(ENABLE_PREPROCESSING) &&
10797 parser.needsPreprocessing(extension))
10798 {
10799 Preprocessor preprocessor;
10800 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10801 for (const auto &s : includePath)
10802 {
10803 std::string absPath = FileInfo(s).absFilePath();
10804 preprocessor.addSearchDir(absPath);
10805 }
10806 std::string inBuf;
10807 msg("Preprocessing {}...\n",fn);
10808 readInputFile(fileName,inBuf);
10809 addTerminalCharIfMissing(inBuf,'\n');
10810 preprocessor.processFile(fileName,inBuf,preBuf);
10811 }
10812 else // no preprocessing
10813 {
10814 msg("Reading {}...\n",fn);
10815 readInputFile(fileName,preBuf);
10816 addTerminalCharIfMissing(preBuf,'\n');
10817 }
10818
10819 std::string convBuf;
10820 convBuf.reserve(preBuf.size()+1024);
10821
10822 // convert multi-line C++ comments to C style comments
10823 convertCppComments(preBuf,convBuf,fileName.str());
10824
10825 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10826 // use language parse to parse the file
10827 if (clangParser)
10828 {
10829 if (newTU) clangParser->parse();
10830 clangParser->switchToFile(fd);
10831 }
10832 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10833 fileRoot->setFileDef(fd);
10834 return fileRoot;
10835}
10836
10837//! parse the list of input files
10838static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10839{
10840 AUTO_TRACE();
10841#if USE_LIBCLANG
10843 {
10844 StringUnorderedSet processedFiles;
10845
10846 // create a dictionary with files to process
10847 StringUnorderedSet filesToProcess;
10848 for (const auto &s : g_inputFiles)
10849 {
10850 filesToProcess.insert(s);
10851 }
10852
10853 std::mutex processedFilesLock;
10854 // process source files (and their include dependencies)
10855 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10856 msg("Processing input using {} threads.\n",numThreads);
10857 ThreadPool threadPool(numThreads);
10858 using FutureType = std::vector< std::shared_ptr<Entry> >;
10859 std::vector< std::future< FutureType > > results;
10860 for (const auto &s : g_inputFiles)
10861 {
10862 bool ambig = false;
10863 QCString qs = s;
10865 ASSERT(fd!=nullptr);
10866 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10867 {
10868 // lambda representing the work to executed by a thread
10869 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10870 bool ambig_l = false;
10871 std::vector< std::shared_ptr<Entry> > roots;
10873 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10874 auto parser = getParserForFile(qs);
10875 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10876 roots.push_back(fileRoot);
10877
10878 // Now process any include files in the same translation unit
10879 // first. When libclang is used this is much more efficient.
10880 for (auto incFile : clangParser->filesInSameTU())
10881 {
10882 QCString qincFile = incFile;
10883 if (filesToProcess.find(incFile)!=filesToProcess.end())
10884 {
10885 bool needsToBeProcessed = false;
10886 {
10887 std::lock_guard<std::mutex> lock(processedFilesLock);
10888 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10889 if (needsToBeProcessed) processedFiles.insert(incFile);
10890 }
10891 if (qincFile!=qs && needsToBeProcessed)
10892 {
10893 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10894 if (ifd && !ifd->isReference())
10895 {
10896 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10897 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10898 roots.push_back(fileRoot);
10899 }
10900 }
10901 }
10902 }
10903 return roots;
10904 };
10905 // dispatch the work and collect the future results
10906 results.emplace_back(threadPool.queue(processFile));
10907 }
10908 }
10909 // synchronize with the Entry result lists produced and add them to the root
10910 for (auto &f : results)
10911 {
10912 auto l = f.get();
10913 for (auto &e : l)
10914 {
10915 root->moveToSubEntryAndKeep(e);
10916 }
10917 }
10918 // process remaining files
10919 results.clear();
10920 for (const auto &s : g_inputFiles)
10921 {
10922 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10923 {
10924 // lambda representing the work to executed by a thread
10925 auto processFile = [s]() {
10926 bool ambig = false;
10927 QCString qs = s;
10928 std::vector< std::shared_ptr<Entry> > roots;
10930 auto parser { getParserForFile(qs) };
10931 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10932 if (useClang)
10933 {
10934 auto clangParser = ClangParser::instance()->createTUParser(fd);
10935 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10936 roots.push_back(fileRoot);
10937 }
10938 else
10939 {
10940 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10941 roots.push_back(fileRoot);
10942 }
10943 return roots;
10944 };
10945 results.emplace_back(threadPool.queue(processFile));
10946 }
10947 }
10948 // synchronize with the Entry result lists produced and add them to the root
10949 for (auto &f : results)
10950 {
10951 auto l = f.get();
10952 for (auto &e : l)
10953 {
10954 root->moveToSubEntryAndKeep(e);
10955 }
10956 }
10957 }
10958 else // normal processing
10959#endif
10960 {
10961 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10962 msg("Processing input using {} threads.\n",numThreads);
10963 ThreadPool threadPool(numThreads);
10964 using FutureType = std::shared_ptr<Entry>;
10965 std::vector< std::future< FutureType > > results;
10966 for (const auto &s : g_inputFiles)
10967 {
10968 // lambda representing the work to executed by a thread
10969 auto processFile = [s]() {
10970 bool ambig = false;
10971 QCString qs = s;
10973 auto parser = getParserForFile(qs);
10974 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10975 return fileRoot;
10976 };
10977 // dispatch the work and collect the future results
10978 results.emplace_back(threadPool.queue(processFile));
10979 }
10980 // synchronize with the Entry results produced and add them to the root
10981 for (auto &f : results)
10982 {
10983 root->moveToSubEntryAndKeep(f.get());
10984 }
10985 }
10986}
10987
10988//! parse the list of input files
10989static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10990{
10991 AUTO_TRACE();
10992#if USE_LIBCLANG
10994 {
10995 StringUnorderedSet processedFiles;
10996
10997 // create a dictionary with files to process
10998 StringUnorderedSet filesToProcess;
10999 for (const auto &s : g_inputFiles)
11000 {
11001 filesToProcess.insert(s);
11002 }
11003
11004 // process source files (and their include dependencies)
11005 for (const auto &s : g_inputFiles)
11006 {
11007 bool ambig = false;
11008 QCString qs =s;
11010 ASSERT(fd!=nullptr);
11011 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
11012 {
11013 auto clangParser = ClangParser::instance()->createTUParser(fd);
11014 auto parser { getParserForFile(qs) };
11015 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11016 root->moveToSubEntryAndKeep(fileRoot);
11017 processedFiles.insert(s);
11018
11019 // Now process any include files in the same translation unit
11020 // first. When libclang is used this is much more efficient.
11021 for (auto incFile : clangParser->filesInSameTU())
11022 {
11023 //printf(" file %s\n",qPrint(incFile));
11024 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
11025 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
11026 {
11028 if (ifd && !ifd->isReference())
11029 {
11030 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
11031 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
11032 root->moveToSubEntryAndKeep(fileRoot);
11033 processedFiles.insert(incFile);
11034 }
11035 }
11036 }
11037 }
11038 }
11039 // process remaining files
11040 for (const auto &s : g_inputFiles)
11041 {
11042 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11043 {
11044 bool ambig = false;
11045 QCString qs = s;
11047 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11048 {
11049 auto clangParser = ClangParser::instance()->createTUParser(fd);
11050 auto parser { getParserForFile(qs) };
11051 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11052 root->moveToSubEntryAndKeep(fileRoot);
11053 }
11054 else
11055 {
11056 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11057 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11058 root->moveToSubEntryAndKeep(fileRoot);
11059 }
11060 processedFiles.insert(s);
11061 }
11062 }
11063 }
11064 else // normal processing
11065#endif
11066 {
11067 for (const auto &s : g_inputFiles)
11068 {
11069 bool ambig = false;
11070 QCString qs = s;
11072 ASSERT(fd!=nullptr);
11073 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11074 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11075 root->moveToSubEntryAndKeep(std::move(fileRoot));
11076 }
11077 }
11078}
11079
11080// resolves a path that may include symlinks, if a recursive symlink is
11081// found an empty string is returned.
11082static std::string resolveSymlink(const std::string &path)
11083{
11084 int sepPos=0;
11085 int oldPos=0;
11086 StringUnorderedSet nonSymlinks;
11087 StringUnorderedSet known;
11088 QCString result(path);
11089 QCString oldPrefix = "/";
11090 do
11091 {
11092#if defined(_WIN32)
11093 // UNC path, skip server and share name
11094 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11095 sepPos = result.find('/',2);
11096 if (sepPos!=-1)
11097 sepPos = result.find('/',sepPos+1);
11098#else
11099 sepPos = result.find('/',sepPos+1);
11100#endif
11101 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
11102 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11103 {
11104 FileInfo fi(prefix.str());
11105 if (fi.isSymLink())
11106 {
11107 QCString target = fi.readLink();
11108 bool isRelative = FileInfo(target.str()).isRelative();
11109 if (isRelative)
11110 {
11111 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11112 }
11113 if (sepPos!=-1)
11114 {
11115 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
11116 {
11117 target+='/';
11118 }
11119 target+=result.mid(sepPos);
11120 }
11121 result = Dir::cleanDirPath(target.str());
11122 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11123 known.insert(result.str());
11124 if (isRelative)
11125 {
11126 sepPos = oldPos;
11127 }
11128 else // link to absolute path
11129 {
11130 sepPos = 0;
11131 oldPrefix = "/";
11132 }
11133 }
11134 else
11135 {
11136 nonSymlinks.insert(prefix.str());
11137 oldPrefix = prefix;
11138 }
11139 oldPos = sepPos;
11140 }
11141 }
11142 while (sepPos!=-1);
11143 return Dir::cleanDirPath(result.str());
11144}
11145
11147
11148//----------------------------------------------------------------------------
11149// Read all files matching at least one pattern in 'patList' in the
11150// directory represented by 'fi'.
11151// The directory is read iff the recursiveFlag is set.
11152// The contents of all files is append to the input string
11153
11154static void readDir(FileInfo *fi,
11155 FileNameLinkedMap *fnMap,
11156 StringUnorderedSet *exclSet,
11157 const StringVector *patList,
11158 const StringVector *exclPatList,
11159 StringVector *resultList,
11160 StringUnorderedSet *resultSet,
11161 bool errorIfNotExist,
11162 bool recursive,
11163 StringUnorderedSet *killSet,
11164 StringUnorderedSet *paths
11165 )
11166{
11167 std::string dirName = fi->absFilePath();
11168 if (paths && !dirName.empty())
11169 {
11170 paths->insert(dirName);
11171 }
11172 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11173 if (fi->isSymLink())
11174 {
11175 dirName = resolveSymlink(dirName);
11176 if (dirName.empty())
11177 {
11178 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11179 return; // recursive symlink
11180 }
11181 }
11182
11183 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11184 {
11185 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11186 return; // already visited path
11187 }
11188 g_pathsVisited.insert(dirName);
11189
11190 Dir dir(dirName);
11191 msg("Searching for files in directory {}\n", fi->absFilePath());
11192 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11193
11194 StringVector dirResultList;
11195
11196 for (const auto &dirEntry : dir.iterator())
11197 {
11198 FileInfo cfi(dirEntry.path());
11199 auto checkPatterns = [&]() -> bool
11200 {
11201 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11202 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11203 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11204 };
11205
11206 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11207 { // file should not be excluded
11208 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11209 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11210 {
11211 }
11212 else if (!cfi.exists() || !cfi.isReadable())
11213 {
11214 if (errorIfNotExist && checkPatterns())
11215 {
11216 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11217 }
11218 }
11219 else if (cfi.isFile() && checkPatterns())
11220 {
11221 std::string name=cfi.fileName();
11222 std::string path=cfi.dirPath()+"/";
11223 std::string fullName=path+name;
11224 if (fnMap)
11225 {
11226 auto fd = createFileDef(path,name);
11227 FileName *fn=nullptr;
11228 if (!name.empty())
11229 {
11230 fn = fnMap->add(name);
11231 fn->push_back(std::move(fd));
11232 }
11233 }
11234 dirResultList.push_back(fullName);
11235 if (resultSet) resultSet->insert(fullName);
11236 if (killSet) killSet->insert(fullName);
11237 }
11238 else if (recursive &&
11239 cfi.isDir() &&
11240 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11241 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11242 {
11243 FileInfo acfi(cfi.absFilePath());
11244 readDir(&acfi,fnMap,exclSet,
11245 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11246 recursive,killSet,paths);
11247 }
11248 }
11249 }
11250 if (resultList && !dirResultList.empty())
11251 {
11252 // sort the resulting list to make the order platform independent.
11253 std::stable_sort(dirResultList.begin(),
11254 dirResultList.end(),
11255 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11256
11257 // append the sorted results to resultList
11258 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11259 }
11260}
11261
11262
11263//----------------------------------------------------------------------------
11264// read a file or all files in a directory and append their contents to the
11265// input string. The names of the files are appended to the 'fiList' list.
11266
11268 FileNameLinkedMap *fnMap,
11269 StringUnorderedSet *exclSet,
11270 const StringVector *patList,
11271 const StringVector *exclPatList,
11272 StringVector *resultList,
11273 StringUnorderedSet *resultSet,
11274 bool recursive,
11275 bool errorIfNotExist,
11276 StringUnorderedSet *killSet,
11277 StringUnorderedSet *paths
11278 )
11279{
11280 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11281 // strip trailing slashes
11282 if (s.isEmpty()) return;
11283
11284 g_pathsVisited.clear();
11285
11286 FileInfo fi(s.str());
11287 //printf("readFileOrDirectory(%s)\n",s);
11288 {
11289 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11290 {
11291 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11292 {
11293 }
11294 else if (!fi.exists() || !fi.isReadable())
11295 {
11296 if (errorIfNotExist)
11297 {
11298 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11299 }
11300 }
11301 else if (fi.isFile())
11302 {
11303 std::string dirPath = fi.dirPath(true);
11304 std::string filePath = fi.absFilePath();
11305 if (paths && !dirPath.empty())
11306 {
11307 paths->insert(dirPath);
11308 }
11309 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11310 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11311 {
11312 std::string name=fi.fileName();
11313 if (fnMap)
11314 {
11315 auto fd = createFileDef(dirPath+"/",name);
11316 if (!name.empty())
11317 {
11318 FileName *fn = fnMap->add(name);
11319 fn->push_back(std::move(fd));
11320 }
11321 }
11322 if (resultList || resultSet)
11323 {
11324 if (resultList) resultList->push_back(filePath);
11325 if (resultSet) resultSet->insert(filePath);
11326 }
11327
11328 if (killSet) killSet->insert(fi.absFilePath());
11329 }
11330 }
11331 else if (fi.isDir()) // readable dir
11332 {
11333 readDir(&fi,fnMap,exclSet,patList,
11334 exclPatList,resultList,resultSet,errorIfNotExist,
11335 recursive,killSet,paths);
11336 }
11337 }
11338 }
11339}
11340
11341//----------------------------------------------------------------------------
11342
11344{
11345 QCString anchor;
11347 {
11348 MemberDef *md = toMemberDef(d);
11349 anchor=":"+md->anchor();
11350 }
11351 QCString scope;
11352 QCString fn = d->getOutputFileBase();
11355 {
11356 scope = fn;
11357 }
11358 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11359 << fn+anchor << "','"
11360 << scope << "','"
11361 << d->name() << "','"
11362 << d->getDefFileName() << "','"
11363 << d->getDefLine()
11364 << "');\n";
11365}
11366
11367static void dumpSymbolMap()
11368{
11369 std::ofstream f = Portable::openOutputStream("symbols.sql");
11370 if (f.is_open())
11371 {
11372 TextStream t(&f);
11373 for (const auto &[name,symList] : *Doxygen::symbolMap)
11374 {
11375 for (const auto &def : symList)
11376 {
11377 dumpSymbol(t,def);
11378 }
11379 }
11380 }
11381}
11382
11383// print developer options of Doxygen
11384static void devUsage()
11385{
11387 msg("Developer parameters:\n");
11388 msg(" -m dump symbol map\n");
11389 msg(" -b making messages output unbuffered\n");
11390 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11391#if ENABLE_TRACING
11392 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11393 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11394 " and include time and thread information\n");
11395#endif
11396 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11398}
11399
11400
11401//----------------------------------------------------------------------------
11402// print the version of Doxygen
11403
11404static void version(const bool extended)
11405{
11407 QCString versionString = getFullVersion();
11408 msg("{}\n",versionString);
11409 if (extended)
11410 {
11411 QCString extVers;
11412 if (!extVers.isEmpty()) extVers+= ", ";
11413 extVers += "sqlite3 ";
11414 extVers += sqlite3_libversion();
11415#if USE_LIBCLANG
11416 if (!extVers.isEmpty()) extVers+= ", ";
11417 extVers += "clang support ";
11418 extVers += CLANG_VERSION_STRING;
11419#endif
11420 if (!extVers.isEmpty())
11421 {
11422 int lastComma = extVers.findRev(',');
11423 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11424 msg(" with {}.\n",extVers);
11425 }
11426 }
11427}
11428
11429//----------------------------------------------------------------------------
11430// print the usage of Doxygen
11431
11432static void usage(const QCString &name,const QCString &versionString)
11433{
11435 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11436 "You can use Doxygen in a number of ways:\n\n"
11437 "1) Use Doxygen to generate a template configuration file*:\n"
11438 " {1} [-s] -g [configName]\n\n"
11439 "2) Use Doxygen to update an old configuration file*:\n"
11440 " {1} [-s] -u [configName]\n\n"
11441 "3) Use Doxygen to generate documentation using an existing "
11442 "configuration file*:\n"
11443 " {1} [configName]\n\n"
11444 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11445 " generated documentation:\n"
11446 " {1} -l [layoutFileName]\n\n"
11447 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11448 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11449 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11450 " RTF: {1} -w rtf styleSheetFile\n"
11451 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11452 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11453 "6) Use Doxygen to generate a rtf extensions file\n"
11454 " {1} -e rtf extensionsFile\n\n"
11455 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11456 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11457 " {1} -x [configFile]\n\n"
11458 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11459 " without replacing the environment variables or CMake type replacement variables\n"
11460 " {1} -x_noenv [configFile]\n\n"
11461 "8) Use Doxygen to show a list of built-in emojis.\n"
11462 " {1} -f emoji outputFileName\n\n"
11463 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11464 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11465 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11466 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11467 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11468 "-v print version string, -V print extended version information\n"
11469 "-h,-? prints usage help information\n"
11470 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11471}
11472
11473//----------------------------------------------------------------------------
11474// read the argument of option 'c' from the comment argument list and
11475// update the option index 'optInd'.
11476
11477static const char *getArg(int argc,char **argv,int &optInd)
11478{
11479 char *s=nullptr;
11480 if (qstrlen(&argv[optInd][2])>0)
11481 s=&argv[optInd][2];
11482 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11483 s=argv[++optInd];
11484 return s;
11485}
11486
11487//----------------------------------------------------------------------------
11488
11489/** @brief /dev/null outline parser */
11491{
11492 public:
11493 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11494 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11495 void parsePrototype(const QCString &) override {}
11496};
11497
11498
11499template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11500{
11501 return []() { return std::make_unique<T>(); };
11502}
11503
11505{
11506 initResources();
11507 QCString lang = Portable::getenv("LC_ALL");
11508 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11509 std::setlocale(LC_ALL,"");
11510 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11511 std::setlocale(LC_NUMERIC,"C");
11512
11514
11538
11539 // register any additional parsers here...
11540
11542
11543#if USE_LIBCLANG
11545#endif
11554 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11555 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11556 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11558
11559 // initialization of these globals depends on
11560 // configuration switches so we need to postpone these
11561 Doxygen::globalScope = nullptr;
11571
11572}
11573
11606
11607void readConfiguration(int argc, char **argv)
11608{
11609 QCString versionString = getFullVersion();
11610
11611 // helper that calls \a func to write to file \a fileName via a TextStream
11612 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11613 {
11614 std::ofstream f;
11615 if (openOutputFile(fileName,f))
11616 {
11617 TextStream t(&f);
11618 func(t);
11619 return true;
11620 }
11621 return false;
11622 };
11623
11624
11625 /**************************************************************************
11626 * Handle arguments *
11627 **************************************************************************/
11628
11629 int optInd=1;
11630 QCString configName;
11631 QCString traceName;
11632 bool genConfig=false;
11633 bool shortList=false;
11634 bool traceTiming=false;
11636 bool updateConfig=false;
11637 bool quiet = false;
11638 while (optInd<argc && argv[optInd][0]=='-' &&
11639 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11640 argv[optInd][1]=='-')
11641 )
11642 {
11643 switch(argv[optInd][1])
11644 {
11645 case 'g':
11646 {
11647 genConfig=TRUE;
11648 }
11649 break;
11650 case 'l':
11651 {
11652 QCString layoutName;
11653 if (optInd+1>=argc)
11654 {
11655 layoutName="DoxygenLayout.xml";
11656 }
11657 else
11658 {
11659 layoutName=argv[optInd+1];
11660 }
11661 writeDefaultLayoutFile(layoutName);
11663 exit(0);
11664 }
11665 break;
11666 case 'c':
11667 if (optInd+1>=argc) // no file name given
11668 {
11669 msg("option \"-c\" is missing the file name to read\n");
11670 devUsage();
11672 exit(1);
11673 }
11674 else
11675 {
11676 g_commentFileName=argv[optInd+1];
11677 optInd++;
11678 }
11679 g_singleComment=true;
11680 quiet=true;
11681 break;
11682 case 'd':
11683 {
11684 QCString debugLabel=getArg(argc,argv,optInd);
11685 if (debugLabel.isEmpty())
11686 {
11687 devUsage();
11689 exit(0);
11690 }
11691 int retVal = Debug::setFlagStr(debugLabel);
11692 if (!retVal)
11693 {
11694 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11695 devUsage();
11697 exit(1);
11698 }
11699 }
11700 break;
11701 case 't':
11702 {
11703#if ENABLE_TRACING
11704 if (!strcmp(argv[optInd]+1,"t_time"))
11705 {
11706 traceTiming = true;
11707 }
11708 else if (!strcmp(argv[optInd]+1,"t"))
11709 {
11710 traceTiming = false;
11711 }
11712 else
11713 {
11714 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11716 exit(1);
11717 }
11718 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11719 {
11720 traceName="stdout";
11721 }
11722 else
11723 {
11724 traceName=argv[optInd+1];
11725 optInd++;
11726 }
11727#else
11728 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11730 exit(1);
11731#endif
11732 }
11733 break;
11734 case 'x':
11735 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11736 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11737 else
11738 {
11739 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11741 exit(1);
11742 }
11743 break;
11744 case 's':
11745 shortList=TRUE;
11746 break;
11747 case 'u':
11748 updateConfig=TRUE;
11749 break;
11750 case 'e':
11751 {
11752 QCString formatName=getArg(argc,argv,optInd);
11753 if (formatName.isEmpty())
11754 {
11755 err("option \"-e\" is missing format specifier rtf.\n");
11757 exit(1);
11758 }
11759 if (qstricmp(formatName.data(),"rtf")==0)
11760 {
11761 if (optInd+1>=argc)
11762 {
11763 err("option \"-e rtf\" is missing an extensions file name\n");
11765 exit(1);
11766 }
11767 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11769 exit(0);
11770 }
11771 err("option \"-e\" has invalid format specifier.\n");
11773 exit(1);
11774 }
11775 break;
11776 case 'f':
11777 {
11778 QCString listName=getArg(argc,argv,optInd);
11779 if (listName.isEmpty())
11780 {
11781 err("option \"-f\" is missing list specifier.\n");
11783 exit(1);
11784 }
11785 if (qstricmp(listName.data(),"emoji")==0)
11786 {
11787 if (optInd+1>=argc)
11788 {
11789 err("option \"-f emoji\" is missing an output file name\n");
11791 exit(1);
11792 }
11793 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11795 exit(0);
11796 }
11797 err("option \"-f\" has invalid list specifier.\n");
11799 exit(1);
11800 }
11801 break;
11802 case 'w':
11803 {
11804 QCString formatName=getArg(argc,argv,optInd);
11805 if (formatName.isEmpty())
11806 {
11807 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11809 exit(1);
11810 }
11811 if (qstricmp(formatName.data(),"rtf")==0)
11812 {
11813 if (optInd+1>=argc)
11814 {
11815 err("option \"-w rtf\" is missing a style sheet file name\n");
11817 exit(1);
11818 }
11819 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11820 {
11821 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11823 exit(1);
11824 }
11826 exit(0);
11827 }
11828 else if (qstricmp(formatName.data(),"html")==0)
11829 {
11830 Config::init();
11831 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11832 // explicit config file mentioned or default found on disk
11833 {
11834 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11835 if (!Config::parse(df)) // parse the config file
11836 {
11837 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11839 exit(1);
11840 }
11841 }
11842 if (optInd+3>=argc)
11843 {
11844 err("option \"-w html\" does not have enough arguments\n");
11846 exit(1);
11847 }
11851 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11852 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11853 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11854 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11856 exit(0);
11857 }
11858 else if (qstricmp(formatName.data(),"latex")==0)
11859 {
11860 Config::init();
11861 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11862 {
11863 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11864 if (!Config::parse(df))
11865 {
11866 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11868 exit(1);
11869 }
11870 }
11871 if (optInd+3>=argc)
11872 {
11873 err("option \"-w latex\" does not have enough arguments\n");
11875 exit(1);
11876 }
11880 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11881 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11882 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11883 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11885 exit(0);
11886 }
11887 else
11888 {
11889 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11891 exit(1);
11892 }
11893 }
11894 break;
11895 case 'm':
11897 break;
11898 case 'v':
11899 version(false);
11901 exit(0);
11902 break;
11903 case 'V':
11904 version(true);
11906 exit(0);
11907 break;
11908 case '-':
11909 if (qstrcmp(&argv[optInd][2],"help")==0)
11910 {
11911 usage(argv[0],versionString);
11912 exit(0);
11913 }
11914 else if (qstrcmp(&argv[optInd][2],"version")==0)
11915 {
11916 version(false);
11918 exit(0);
11919 }
11920 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11921 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11922 {
11923 version(true);
11925 exit(0);
11926 }
11927 else
11928 {
11929 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11930 usage(argv[0],versionString);
11931 exit(1);
11932 }
11933 break;
11934 case 'b':
11935 setvbuf(stdout,nullptr,_IONBF,0);
11936 break;
11937 case 'q':
11938 quiet = true;
11939 break;
11940 case 'h':
11941 case '?':
11942 usage(argv[0],versionString);
11943 exit(0);
11944 break;
11945 default:
11946 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11947 usage(argv[0],versionString);
11948 exit(1);
11949 }
11950 optInd++;
11951 }
11952
11953 /**************************************************************************
11954 * Parse or generate the config file *
11955 **************************************************************************/
11956
11957 initTracing(traceName.data(),traceTiming);
11958 TRACE("Doxygen version used: {}",getFullVersion());
11959 Config::init();
11960
11961 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11962 if (optInd>=argc)
11963 {
11964 if (configFileInfo1.exists())
11965 {
11966 configName="Doxyfile";
11967 }
11968 else if (configFileInfo2.exists())
11969 {
11970 configName="doxyfile";
11971 }
11972 else if (genConfig)
11973 {
11974 configName="Doxyfile";
11975 }
11976 else
11977 {
11978 err("Doxyfile not found and no input file specified!\n");
11979 usage(argv[0],versionString);
11980 exit(1);
11981 }
11982 }
11983 else
11984 {
11985 FileInfo fi(argv[optInd]);
11986 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11987 {
11988 configName=argv[optInd];
11989 }
11990 else
11991 {
11992 err("configuration file {} not found!\n",argv[optInd]);
11993 usage(argv[0],versionString);
11994 exit(1);
11995 }
11996 }
11997
11998 if (genConfig)
11999 {
12000 generateConfigFile(configName,shortList);
12002 exit(0);
12003 }
12004
12005 if (!Config::parse(configName,updateConfig,diffList))
12006 {
12007 err("could not open or read configuration file {}!\n",configName);
12009 exit(1);
12010 }
12011
12012 if (diffList!=Config::CompareMode::Full)
12013 {
12015 compareDoxyfile(diffList);
12017 exit(0);
12018 }
12019
12020 if (updateConfig)
12021 {
12023 generateConfigFile(configName,shortList,TRUE);
12025 exit(0);
12026 }
12027
12028 /* Perlmod wants to know the path to the config file.*/
12029 FileInfo configFileInfo(configName.str());
12030 setPerlModDoxyfile(configFileInfo.absFilePath());
12031
12032 /* handle -q option */
12033 if (quiet) Config_updateBool(QUIET,TRUE);
12034}
12035
12036/** check and resolve config options */
12038{
12039 AUTO_TRACE();
12040
12045}
12046
12047/** adjust globals that depend on configuration settings. */
12049{
12050 AUTO_TRACE();
12051 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12062
12063 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12064
12065 /* Set the global html file extension. */
12066 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12067
12068
12070 Config_getBool(CALLER_GRAPH) ||
12071 Config_getBool(REFERENCES_RELATION) ||
12072 Config_getBool(REFERENCED_BY_RELATION);
12073
12074 /**************************************************************************
12075 * Add custom extension mappings
12076 **************************************************************************/
12077
12078 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
12079 for (const auto &mapping : extMaps)
12080 {
12081 QCString mapStr = mapping;
12082 int i=mapStr.find('=');
12083 if (i==-1)
12084 {
12085 continue;
12086 }
12087 else
12088 {
12089 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
12090 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12091 if (ext.isEmpty() || language.isEmpty())
12092 {
12093 continue;
12094 }
12095
12096 if (!updateLanguageMapping(ext,language))
12097 {
12098 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12099 "Check the EXTENSION_MAPPING setting in the config file.\n",
12100 ext,language);
12101 }
12102 else
12103 {
12104 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12105 ext,language);
12106 }
12107 }
12108 }
12109 // create input file exncodings
12110
12111 // check INPUT_ENCODING
12112 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12113 if (cd==reinterpret_cast<void *>(-1))
12114 {
12115 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12116 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12117 Config_getString(INPUT_ENCODING),strerror(errno));
12118 }
12119 else
12120 {
12122 }
12123
12124 // check and split INPUT_FILE_ENCODING
12125 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
12126 for (const auto &mapping : fileEncod)
12127 {
12128 QCString mapStr = mapping;
12129 int i=mapStr.find('=');
12130 if (i==-1)
12131 {
12132 continue;
12133 }
12134 else
12135 {
12136 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
12137 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12138 if (pattern.isEmpty() || encoding.isEmpty())
12139 {
12140 continue;
12141 }
12142 cd = portable_iconv_open("UTF-8",encoding.data());
12143 if (cd==reinterpret_cast<void *>(-1))
12144 {
12145 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12146 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12147 encoding,strerror(errno));
12148 }
12149 else
12150 {
12152 }
12153
12154 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12155 }
12156 }
12157
12158 // add predefined macro name to a dictionary
12159 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
12160 for (const auto &s : expandAsDefinedList)
12161 {
12163 }
12164
12165 // read aliases and store them in a dictionary
12166 readAliases();
12167
12168 // store number of spaces in a tab into Doxygen::spaces
12169 int tabSize = Config_getInt(TAB_SIZE);
12170 Doxygen::spaces.resize(tabSize);
12171 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12172 Doxygen::spaces.at(tabSize)='\0';
12173}
12174
12175#ifdef HAS_SIGNALS
12176static void stopDoxygen(int)
12177{
12178 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12179 Dir thisDir;
12180 msg("Cleaning up...\n");
12181 if (!Doxygen::filterDBFileName.isEmpty())
12182 {
12183 thisDir.remove(Doxygen::filterDBFileName.str());
12184 }
12185 killpg(0,SIGINT);
12187 exitTracing();
12188 exit(1);
12189}
12190#endif
12191
12192static void writeTagFile()
12193{
12194 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12195 if (generateTagFile.isEmpty()) return;
12196
12197 std::ofstream f = Portable::openOutputStream(generateTagFile);
12198 if (!f.is_open())
12199 {
12200 err("cannot open tag file {} for writing\n", generateTagFile);
12201 return;
12202 }
12203 TextStream tagFile(&f);
12204 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12205 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12206 std::string gitVersion = getGitVersion();
12207 if (!gitVersion.empty())
12208 {
12209 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12210 }
12211 tagFile << ">\n";
12212
12213 // for each file
12214 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12215 {
12216 for (const auto &fd : *fn)
12217 {
12218 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12219 }
12220 }
12221 // for each class
12222 for (const auto &cd : *Doxygen::classLinkedMap)
12223 {
12224 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12225 if (cdm && cdm->isLinkableInProject())
12226 {
12227 cdm->writeTagFile(tagFile);
12228 }
12229 }
12230 // for each concept
12231 for (const auto &cd : *Doxygen::conceptLinkedMap)
12232 {
12233 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12234 if (cdm && cdm->isLinkableInProject())
12235 {
12236 cdm->writeTagFile(tagFile);
12237 }
12238 }
12239 // for each namespace
12240 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12241 {
12243 if (ndm && nd->isLinkableInProject())
12244 {
12245 ndm->writeTagFile(tagFile);
12246 }
12247 }
12248 // for each group
12249 for (const auto &gd : *Doxygen::groupLinkedMap)
12250 {
12251 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12252 }
12253 // for each module
12254 for (const auto &mod : ModuleManager::instance().modules())
12255 {
12256 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12257 }
12258 // for each page
12259 for (const auto &pd : *Doxygen::pageLinkedMap)
12260 {
12261 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12262 }
12263 // for requirements
12265 // for each directory
12266 for (const auto &dd : *Doxygen::dirLinkedMap)
12267 {
12268 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12269 }
12270 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12271
12272 tagFile << "</tagfile>\n";
12273}
12274
12275static void exitDoxygen() noexcept
12276{
12277 if (!g_successfulRun) // premature exit
12278 {
12279 Dir thisDir;
12280 msg("Exiting...\n");
12281 if (!Doxygen::filterDBFileName.isEmpty())
12282 {
12283 thisDir.remove(Doxygen::filterDBFileName.str());
12284 }
12285 }
12286}
12287
12288static QCString createOutputDirectory(const QCString &baseDirName,
12289 const QCString &formatDirName,
12290 const char *defaultDirName)
12291{
12292 QCString result = formatDirName;
12293 if (result.isEmpty())
12294 {
12295 result = baseDirName + defaultDirName;
12296 }
12297 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12298 {
12299 result.prepend(baseDirName+"/");
12300 }
12301 Dir formatDir(result.str());
12302 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12303 {
12304 term("Could not create output directory {}\n", result);
12305 }
12306 return result;
12307}
12308
12310{
12311 StringUnorderedSet killSet;
12312
12313 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12314 bool alwaysRecursive = Config_getBool(RECURSIVE);
12315 StringUnorderedSet excludeNameSet;
12316
12317 // gather names of all files in the include path
12318 g_s.begin("Searching for include files...\n");
12319 killSet.clear();
12320 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12321 for (const auto &s : includePathList)
12322 {
12323 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12324 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12325 Config_getList(INCLUDE_FILE_PATTERNS);
12326 readFileOrDirectory(s, // s
12328 nullptr, // exclSet
12329 &pl, // patList
12330 &exclPatterns, // exclPatList
12331 nullptr, // resultList
12332 nullptr, // resultSet
12333 false, // INCLUDE_PATH isn't recursive
12334 TRUE, // errorIfNotExist
12335 &killSet); // killSet
12336 }
12337 g_s.end();
12338
12339 g_s.begin("Searching for example files...\n");
12340 killSet.clear();
12341 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12342 for (const auto &s : examplePathList)
12343 {
12344 readFileOrDirectory(s, // s
12346 nullptr, // exclSet
12347 &Config_getList(EXAMPLE_PATTERNS), // patList
12348 nullptr, // exclPatList
12349 nullptr, // resultList
12350 nullptr, // resultSet
12351 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12352 TRUE, // errorIfNotExist
12353 &killSet); // killSet
12354 }
12355 g_s.end();
12356
12357 g_s.begin("Searching for images...\n");
12358 killSet.clear();
12359 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12360 for (const auto &s : imagePathList)
12361 {
12362 readFileOrDirectory(s, // s
12364 nullptr, // exclSet
12365 nullptr, // patList
12366 nullptr, // exclPatList
12367 nullptr, // resultList
12368 nullptr, // resultSet
12369 alwaysRecursive, // recursive
12370 TRUE, // errorIfNotExist
12371 &killSet); // killSet
12372 }
12373 g_s.end();
12374
12375 g_s.begin("Searching for dot files...\n");
12376 killSet.clear();
12377 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12378 for (const auto &s : dotFileList)
12379 {
12380 readFileOrDirectory(s, // s
12382 nullptr, // exclSet
12383 nullptr, // patList
12384 nullptr, // exclPatList
12385 nullptr, // resultList
12386 nullptr, // resultSet
12387 alwaysRecursive, // recursive
12388 TRUE, // errorIfNotExist
12389 &killSet); // killSet
12390 }
12391 g_s.end();
12392
12393 g_s.begin("Searching for msc files...\n");
12394 killSet.clear();
12395 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12396 for (const auto &s : mscFileList)
12397 {
12398 readFileOrDirectory(s, // s
12400 nullptr, // exclSet
12401 nullptr, // patList
12402 nullptr, // exclPatList
12403 nullptr, // resultList
12404 nullptr, // resultSet
12405 alwaysRecursive, // recursive
12406 TRUE, // errorIfNotExist
12407 &killSet); // killSet
12408 }
12409 g_s.end();
12410
12411 g_s.begin("Searching for dia files...\n");
12412 killSet.clear();
12413 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12414 for (const auto &s : diaFileList)
12415 {
12416 readFileOrDirectory(s, // s
12418 nullptr, // exclSet
12419 nullptr, // patList
12420 nullptr, // exclPatList
12421 nullptr, // resultList
12422 nullptr, // resultSet
12423 alwaysRecursive, // recursive
12424 TRUE, // errorIfNotExist
12425 &killSet); // killSet
12426 }
12427 g_s.end();
12428
12429 g_s.begin("Searching for plantuml files...\n");
12430 killSet.clear();
12431 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12432 for (const auto &s : plantUmlFileList)
12433 {
12434 readFileOrDirectory(s, // s
12436 nullptr, // exclSet
12437 nullptr, // patList
12438 nullptr, // exclPatList
12439 nullptr, // resultList
12440 nullptr, // resultSet
12441 alwaysRecursive, // recursive
12442 TRUE, // errorIfNotExist
12443 &killSet); // killSet
12444 }
12445 g_s.end();
12446
12447 g_s.begin("Searching for mermaid files...\n");
12448 killSet.clear();
12449 const StringVector &mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12450 for (const auto &s : mermaidFileList)
12451 {
12452 readFileOrDirectory(s, // s
12454 nullptr, // exclSet
12455 nullptr, // patList
12456 nullptr, // exclPatList
12457 nullptr, // resultList
12458 nullptr, // resultSet
12459 alwaysRecursive, // recursive
12460 TRUE, // errorIfNotExist
12461 &killSet); // killSet
12462 }
12463 g_s.end();
12464
12465 g_s.begin("Searching for files to exclude\n");
12466 const StringVector &excludeList = Config_getList(EXCLUDE);
12467 for (const auto &s : excludeList)
12468 {
12469 readFileOrDirectory(s, // s
12470 nullptr, // fnDict
12471 nullptr, // exclSet
12472 &Config_getList(FILE_PATTERNS), // patList
12473 nullptr, // exclPatList
12474 nullptr, // resultList
12475 &excludeNameSet, // resultSet
12476 alwaysRecursive, // recursive
12477 FALSE); // errorIfNotExist
12478 }
12479 g_s.end();
12480
12481 /**************************************************************************
12482 * Determine Input Files *
12483 **************************************************************************/
12484
12485 g_s.begin("Searching INPUT for files to process...\n");
12486 killSet.clear();
12487 Doxygen::inputPaths.clear();
12488 const StringVector &inputList=Config_getList(INPUT);
12489 for (const auto &s : inputList)
12490 {
12491 QCString path = s;
12492 size_t l = path.length();
12493 if (l>0)
12494 {
12495 // strip trailing slashes
12496 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12497
12499 path, // s
12501 &excludeNameSet, // exclSet
12502 &Config_getList(FILE_PATTERNS), // patList
12503 &exclPatterns, // exclPatList
12504 &g_inputFiles, // resultList
12505 nullptr, // resultSet
12506 alwaysRecursive, // recursive
12507 TRUE, // errorIfNotExist
12508 &killSet, // killSet
12509 &Doxygen::inputPaths); // paths
12510 }
12511 }
12512
12513 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12514 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12515 {
12516 if (fileName->size()>1)
12517 {
12518 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12519 {
12520 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12521 });
12522 }
12523 }
12524 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12526 [](const auto &f1,const auto &f2)
12527 {
12528 return qstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12529 });
12530 if (Doxygen::inputNameLinkedMap->empty())
12531 {
12532 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12533 }
12534 g_s.end();
12535}
12536
12537
12539{
12540 if (Config_getBool(MARKDOWN_SUPPORT))
12541 {
12542 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12543 if (mdfileAsMainPage.isEmpty()) return;
12544 FileInfo fi(mdfileAsMainPage.data());
12545 if (!fi.exists())
12546 {
12547 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12548 return;
12549 }
12550 bool ambig = false;
12551 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12552 {
12553 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12554 return;
12555 }
12556 }
12557}
12558
12560{
12561 AUTO_TRACE();
12562 std::atexit(exitDoxygen);
12563
12564 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12565
12566#if USE_LIBCLANG
12567 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12568#endif
12569
12570 // we would like to show the versionString earlier, but we first have to handle the configuration file
12571 // to know the value of the QUIET setting.
12572 QCString versionString = getFullVersion();
12573 msg("Doxygen version used: {}\n",versionString);
12574
12576
12577 /**************************************************************************
12578 * Make sure the output directory exists
12579 **************************************************************************/
12580 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12581 if (!g_singleComment)
12582 {
12583 if (outputDirectory.isEmpty())
12584 {
12585 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12586 }
12587 else
12588 {
12589 Dir dir(outputDirectory.str());
12590 if (!dir.exists())
12591 {
12593 if (!dir.mkdir(outputDirectory.str()))
12594 {
12595 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12596 "exist and cannot be created\n",outputDirectory);
12597 }
12598 else
12599 {
12600 msg("Notice: Output directory '{}' does not exist. "
12601 "I have created it for you.\n", outputDirectory);
12602 }
12603 dir.setPath(outputDirectory.str());
12604 }
12605 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12606 }
12607 }
12608 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12609
12610 /**************************************************************************
12611 * Initialize global lists and dictionaries
12612 **************************************************************************/
12613
12614#ifdef HAS_SIGNALS
12615 signal(SIGINT, stopDoxygen);
12616#endif
12617
12618 uint32_t pid = Portable::pid();
12619 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12620 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12621
12622 /**************************************************************************
12623 * Check/create output directories *
12624 **************************************************************************/
12625
12626 bool generateHtml = Config_getBool(GENERATE_HTML);
12627 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12628 bool generateXml = Config_getBool(GENERATE_XML);
12629 bool generateLatex = Config_getBool(GENERATE_LATEX);
12630 bool generateRtf = Config_getBool(GENERATE_RTF);
12631 bool generateMan = Config_getBool(GENERATE_MAN);
12632 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12633 QCString htmlOutput;
12634 QCString docbookOutput;
12635 QCString xmlOutput;
12636 QCString latexOutput;
12637 QCString rtfOutput;
12638 QCString manOutput;
12639 QCString sqlOutput;
12640
12641 if (!g_singleComment)
12642 {
12643 if (generateHtml)
12644 {
12645 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12646 Config_updateString(HTML_OUTPUT,htmlOutput);
12647
12648 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12649 bool generateSitemap = !sitemapUrl.isEmpty();
12650 if (generateSitemap && !sitemapUrl.endsWith("/"))
12651 {
12652 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12653 }
12654
12655 // add HTML indexers that are enabled
12656 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12657 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12658 bool generateQhp = Config_getBool(GENERATE_QHP);
12659 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12660 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12661 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12662 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12663 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12664 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12665 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12666 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12667 Doxygen::indexList->addIndex<Crawlmap>();
12668 Doxygen::indexList->initialize();
12669 }
12670
12671 if (generateDocbook)
12672 {
12673 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12674 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12675 }
12676
12677 if (generateXml)
12678 {
12679 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12680 Config_updateString(XML_OUTPUT,xmlOutput);
12681 }
12682
12683 if (generateLatex)
12684 {
12685 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12686 Config_updateString(LATEX_OUTPUT,latexOutput);
12687 }
12688
12689 if (generateRtf)
12690 {
12691 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12692 Config_updateString(RTF_OUTPUT,rtfOutput);
12693 }
12694
12695 if (generateMan)
12696 {
12697 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12698 Config_updateString(MAN_OUTPUT,manOutput);
12699 }
12700
12701 if (generateSql)
12702 {
12703 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12704 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12705 }
12706 }
12707
12708 if (Config_getBool(HAVE_DOT))
12709 {
12710 QCString curFontPath = Config_getString(DOT_FONTPATH);
12711 if (curFontPath.isEmpty())
12712 {
12713 Portable::getenv("DOTFONTPATH");
12714 QCString newFontPath = ".";
12715 if (!curFontPath.isEmpty())
12716 {
12717 newFontPath+=Portable::pathListSeparator();
12718 newFontPath+=curFontPath;
12719 }
12720 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12721 }
12722 else
12723 {
12724 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12725 }
12726 }
12727
12728 /**************************************************************************
12729 * Handle layout file *
12730 **************************************************************************/
12731
12733 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12734 bool defaultLayoutUsed = FALSE;
12735 if (layoutFileName.isEmpty())
12736 {
12737 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12738 defaultLayoutUsed = TRUE;
12739 }
12740 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12741
12742 FileInfo fi(layoutFileName.str());
12743 if (fi.exists())
12744 {
12745 msg("Parsing layout file {}...\n",layoutFileName);
12746 LayoutDocManager::instance().parse(layoutFileName);
12747 }
12748 else if (!defaultLayoutUsed)
12749 {
12750 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12751 }
12752 printLayout();
12753
12754 /**************************************************************************
12755 * Read and preprocess input *
12756 **************************************************************************/
12757
12758 // prevent search in the output directories
12759 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12760 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12761 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12762 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12763 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12764 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12765 if (generateMan) exclPatterns.push_back(manOutput.str());
12766 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12767
12768 if (!g_singleComment)
12769 {
12771
12773 }
12774
12775 // Notice: the order of the function calls below is very important!
12776
12777 if (generateHtml && !Config_getBool(USE_MATHJAX))
12778 {
12780 }
12781 if (generateRtf)
12782 {
12784 }
12785 if (generateDocbook)
12786 {
12788 }
12789
12791
12792 /**************************************************************************
12793 * Handle Tag Files *
12794 **************************************************************************/
12795
12796 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12797
12798 if (!g_singleComment)
12799 {
12800 msg("Reading and parsing tag files\n");
12801 const StringVector &tagFileList = Config_getList(TAGFILES);
12802 for (const auto &s : tagFileList)
12803 {
12804 readTagFile(root,s.c_str());
12805 }
12806 }
12807
12808 /**************************************************************************
12809 * Parse source files *
12810 **************************************************************************/
12811
12812 addSTLSupport(root);
12813
12814 g_s.begin("Parsing files\n");
12815 if (g_singleComment)
12816 {
12817 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12818 if (g_commentFileName=="-")
12819 {
12820 std::string text = fileToString(g_commentFileName).str();
12821 addTerminalCharIfMissing(text,'\n');
12822 generateHtmlForComment("stdin.md",text);
12823 }
12824 else if (FileInfo(g_commentFileName.str()).isFile())
12825 {
12826 std::string text;
12828 addTerminalCharIfMissing(text,'\n');
12830 }
12831 else
12832 {
12833 }
12835 exit(0);
12836 }
12837 else
12838 {
12839 if (Config_getInt(NUM_PROC_THREADS)==1)
12840 {
12842 }
12843 else
12844 {
12846 }
12847 }
12848 g_s.end();
12849
12850 /**************************************************************************
12851 * Gather information *
12852 **************************************************************************/
12853
12854 g_s.begin("Building macro definition list...\n");
12856 g_s.end();
12857
12858 g_s.begin("Building group list...\n");
12859 buildGroupList(root.get());
12860 organizeSubGroups(root.get());
12861 g_s.end();
12862
12863 g_s.begin("Building directory list...\n");
12865 findDirDocumentation(root.get());
12866 g_s.end();
12867
12868 g_s.begin("Building namespace list...\n");
12869 buildNamespaceList(root.get());
12870 findUsingDirectives(root.get());
12871 g_s.end();
12872
12873 g_s.begin("Building file list...\n");
12874 buildFileList(root.get());
12875 g_s.end();
12876
12877 g_s.begin("Building class list...\n");
12878 buildClassList(root.get());
12879 g_s.end();
12880
12881 g_s.begin("Building concept list...\n");
12882 buildConceptList(root.get());
12883 g_s.end();
12884
12885 // build list of using declarations here (global list)
12886 buildListOfUsingDecls(root.get());
12887 g_s.end();
12888
12889 g_s.begin("Computing nesting relations for classes...\n");
12891 g_s.end();
12892 // 1.8.2-20121111: no longer add nested classes to the group as well
12893 //distributeClassGroupRelations();
12894
12895 // calling buildClassList may result in cached relations that
12896 // become invalid after resolveClassNestingRelations(), that's why
12897 // we need to clear the cache here
12899 // we don't need the list of using declaration anymore
12900 g_usingDeclarations.clear();
12901
12902 g_s.begin("Associating documentation with classes...\n");
12903 buildClassDocList(root.get());
12904 g_s.end();
12905
12906 g_s.begin("Associating documentation with concepts...\n");
12907 buildConceptDocList(root.get());
12909 g_s.end();
12910
12911 g_s.begin("Associating documentation with modules...\n");
12912 findModuleDocumentation(root.get());
12913 g_s.end();
12914
12915 g_s.begin("Building example list...\n");
12916 buildExampleList(root.get());
12917 g_s.end();
12918
12919 g_s.begin("Searching for enumerations...\n");
12920 findEnums(root.get());
12921 g_s.end();
12922
12923 // Since buildVarList calls isVarWithConstructor
12924 // and this calls getResolvedClass we need to process
12925 // typedefs first so the relations between classes via typedefs
12926 // are properly resolved. See bug 536385 for an example.
12927 g_s.begin("Searching for documented typedefs...\n");
12928 buildTypedefList(root.get());
12929 g_s.end();
12930
12931 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12932 {
12933 g_s.begin("Searching for documented sequences...\n");
12934 buildSequenceList(root.get());
12935 g_s.end();
12936
12937 g_s.begin("Searching for documented dictionaries...\n");
12938 buildDictionaryList(root.get());
12939 g_s.end();
12940 }
12941
12942 g_s.begin("Searching for members imported via using declarations...\n");
12943 // this should be after buildTypedefList in order to properly import
12944 // used typedefs
12945 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12946 findUsingDeclarations(root.get(),FALSE); // then the rest
12947 g_s.end();
12948
12949 g_s.begin("Searching for included using directives...\n");
12951 g_s.end();
12952
12953 g_s.begin("Searching for documented variables...\n");
12954 buildVarList(root.get());
12955 g_s.end();
12956
12957 g_s.begin("Building interface member list...\n");
12958 buildInterfaceAndServiceList(root.get()); // UNO IDL
12959
12960 g_s.begin("Building member list...\n"); // using class info only !
12961 buildFunctionList(root.get());
12962 g_s.end();
12963
12964 g_s.begin("Searching for friends...\n");
12965 findFriends();
12966 g_s.end();
12967
12968 g_s.begin("Searching for documented defines...\n");
12969 findDefineDocumentation(root.get());
12970 g_s.end();
12971
12972 g_s.begin("Computing class inheritance relations...\n");
12973 findClassEntries(root.get());
12975 g_s.end();
12976
12977 g_s.begin("Computing class usage relations...\n");
12979 g_s.end();
12980
12981 g_s.begin("Flushing cached template relations that have become invalid...\n");
12983 g_s.end();
12984
12985 g_s.begin("Warn for undocumented namespaces...\n");
12987 g_s.end();
12988
12989 g_s.begin("Computing class relations...\n");
12992 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12993 {
12995 }
12997 g_classEntries.clear();
12998 g_s.end();
12999
13000 g_s.begin("Add enum values to enums...\n");
13001 addEnumValuesToEnums(root.get());
13002 findEnumDocumentation(root.get());
13003 g_s.end();
13004
13005 g_s.begin("Searching for member function documentation...\n");
13006 findObjCMethodDefinitions(root.get());
13007 findMemberDocumentation(root.get()); // may introduce new members !
13008 findUsingDeclImports(root.get()); // may introduce new members !
13009 g_usingClassMap.clear();
13013 g_s.end();
13014
13015 // moved to after finding and copying documentation,
13016 // as this introduces new members see bug 722654
13017 g_s.begin("Creating members for template instances...\n");
13019 g_s.end();
13020
13021 g_s.begin("Searching for tag less structs...\n");
13023 g_s.end();
13024
13025 g_s.begin("Building page list...\n");
13026 buildPageList(root.get());
13027 g_s.end();
13028
13029 g_s.begin("Building requirements list...\n");
13030 buildRequirementsList(root.get());
13031 g_s.end();
13032
13033 g_s.begin("Search for main page...\n");
13034 findMainPage(root.get());
13035 findMainPageTagFiles(root.get());
13036 g_s.end();
13037
13038 g_s.begin("Computing page relations...\n");
13039 computePageRelations(root.get());
13041 g_s.end();
13042
13043 g_s.begin("Determining the scope of groups...\n");
13044 findGroupScope(root.get());
13045 g_s.end();
13046
13047 g_s.begin("Computing module relations...\n");
13048 auto &mm = ModuleManager::instance();
13049 mm.resolvePartitions();
13050 mm.resolveImports();
13051 mm.collectExportedSymbols();
13052 g_s.end();
13053
13054 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13055 {
13056 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13057 n2->memberName().data()+getPrefixIndex(n2->memberName())
13058 )<0;
13059 };
13060
13061 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13062 {
13063 if (Config_getBool(SORT_BY_SCOPE_NAME))
13064 {
13065 return qstricmp_sort(c1->name(), c2->name())<0;
13066 }
13067 else
13068 {
13069 int i = qstricmp_sort(c1->className(), c2->className());
13070 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
13071 }
13072 };
13073
13074 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13075 {
13076 return qstricmp_sort(n1->name(),n2->name())<0;
13077 };
13078
13079 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13080 {
13081 return qstricmp_sort(c1->name(),c2->name())<0;
13082 };
13083
13084 g_s.begin("Sorting lists...\n");
13085 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13087 memberNameComp);
13088 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13090 memberNameComp);
13091 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13093 classComp);
13094 std::stable_sort(Doxygen::classLinkedMap->begin(),
13096 classComp);
13097 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13099 conceptComp);
13100 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13102 namespaceComp);
13103 g_s.end();
13104
13105 g_s.begin("Determining which enums are documented\n");
13107 g_s.end();
13108
13109 g_s.begin("Computing member relations...\n");
13112 g_s.end();
13113
13114 g_s.begin("Building full member lists recursively...\n");
13116 g_s.end();
13117
13118 g_s.begin("Adding members to member groups.\n");
13120 g_s.end();
13121
13122 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13123 {
13124 g_s.begin("Distributing member group documentation.\n");
13126 g_s.end();
13127 }
13128
13129 g_s.begin("Computing member references...\n");
13131 g_s.end();
13132
13133 if (Config_getBool(INHERIT_DOCS))
13134 {
13135 g_s.begin("Inheriting documentation...\n");
13137 g_s.end();
13138 }
13139
13140
13141 // compute the shortest possible names of all files
13142 // without losing the uniqueness of the file names.
13143 g_s.begin("Generating disk names...\n");
13145 g_s.end();
13146
13147 g_s.begin("Adding source references...\n");
13149 g_s.end();
13150
13151 g_s.begin("Adding xrefitems...\n");
13154 g_s.end();
13155
13156 g_s.begin("Adding requirements...\n");
13159 g_s.end();
13160
13161 g_s.begin("Sorting member lists...\n");
13163 g_s.end();
13164
13165 g_s.begin("Setting anonymous enum type...\n");
13167 g_s.end();
13168
13169 g_s.begin("Computing dependencies between directories...\n");
13171 g_s.end();
13172
13173 g_s.begin("Generating citations page...\n");
13175 g_s.end();
13176
13177 g_s.begin("Counting data structures...\n");
13179 g_s.end();
13180
13181 g_s.begin("Resolving user defined references...\n");
13183 g_s.end();
13184
13185 g_s.begin("Finding anchors and sections in the documentation...\n");
13187 g_s.end();
13188
13189 g_s.begin("Transferring function references...\n");
13191 g_s.end();
13192
13193 g_s.begin("Combining using relations...\n");
13195 g_s.end();
13196
13198 g_s.begin("Adding members to index pages...\n");
13200 addToIndices();
13201 g_s.end();
13202
13203 g_s.begin("Correcting members for VHDL...\n");
13205 g_s.end();
13206
13207 g_s.begin("Computing tooltip texts...\n");
13209 g_s.end();
13210
13211 if (Config_getBool(SORT_GROUP_NAMES))
13212 {
13213 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13215 [](const auto &g1,const auto &g2)
13216 { return g1->groupTitle() < g2->groupTitle(); });
13217
13218 for (const auto &gd : *Doxygen::groupLinkedMap)
13219 {
13220 gd->sortSubGroups();
13221 }
13222 }
13223
13224 printNavTree(root.get(),0);
13226}
13227
13229{
13230 AUTO_TRACE();
13231 /**************************************************************************
13232 * Initialize output generators *
13233 **************************************************************************/
13234
13235 /// add extra languages for which we can only produce syntax highlighted code
13237
13238 //// dump all symbols
13239 if (g_dumpSymbolMap)
13240 {
13241 dumpSymbolMap();
13242 exit(0);
13243 }
13244
13245 bool generateHtml = Config_getBool(GENERATE_HTML);
13246 bool generateLatex = Config_getBool(GENERATE_LATEX);
13247 bool generateMan = Config_getBool(GENERATE_MAN);
13248 bool generateRtf = Config_getBool(GENERATE_RTF);
13249 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13250
13251
13253 if (generateHtml)
13254 {
13258 }
13259 if (generateLatex)
13260 {
13263 }
13264 if (generateDocbook)
13265 {
13268 }
13269 if (generateMan)
13270 {
13271 g_outputList->add<ManGenerator>();
13273 }
13274 if (generateRtf)
13275 {
13276 g_outputList->add<RTFGenerator>();
13278 }
13279 if (Config_getBool(USE_HTAGS))
13280 {
13282 QCString htmldir = Config_getString(HTML_OUTPUT);
13283 if (!Htags::execute(htmldir))
13284 err("USE_HTAGS is YES but htags(1) failed. \n");
13285 else if (!Htags::loadFilemap(htmldir))
13286 err("htags(1) ended normally but failed to load the filemap. \n");
13287 }
13288
13289 /**************************************************************************
13290 * Generate documentation *
13291 **************************************************************************/
13292
13293 g_s.begin("Generating style sheet...\n");
13294 //printf("writing style info\n");
13295 g_outputList->writeStyleInfo(0); // write first part
13296 g_s.end();
13297
13298 bool searchEngine = Config_getBool(SEARCHENGINE);
13299 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13300
13301 g_s.begin("Generating search indices...\n");
13302 if (searchEngine && !serverBasedSearch && generateHtml)
13303 {
13305 }
13306
13307 // generate search indices (need to do this before writing other HTML
13308 // pages as these contain a drop down menu with options depending on
13309 // what categories we find in this function.
13310 if (generateHtml && searchEngine)
13311 {
13312 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13313 Dir searchDir(searchDirName.str());
13314 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13315 {
13316 term("Could not create search results directory '{}' $PWD='{}'\n",
13317 searchDirName,Dir::currentDirPath());
13318 }
13319 HtmlGenerator::writeSearchData(searchDirName);
13320 if (!serverBasedSearch) // client side search index
13321 {
13323 }
13324 }
13325 g_s.end();
13326
13327 // copy static stuff
13328 if (generateHtml)
13329 {
13331 copyLogo(Config_getString(HTML_OUTPUT),true);
13332 copyIcon(Config_getString(HTML_OUTPUT),true);
13333 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13334 }
13335 if (generateLatex)
13336 {
13338 copyLogo(Config_getString(LATEX_OUTPUT),false);
13339 copyIcon(Config_getString(LATEX_OUTPUT),false);
13340 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13341 }
13342 if (generateDocbook)
13343 {
13344 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13345 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13346 }
13347 if (generateRtf)
13348 {
13349 copyLogo(Config_getString(RTF_OUTPUT),false);
13350 copyIcon(Config_getString(RTF_OUTPUT),false);
13351 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13352 }
13353
13355 if (fm.hasFormulas() && generateHtml
13356 && !Config_getBool(USE_MATHJAX))
13357 {
13358 g_s.begin("Generating images for formulas in HTML...\n");
13359 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13361 g_s.end();
13362 }
13363 if (fm.hasFormulas() && generateRtf)
13364 {
13365 g_s.begin("Generating images for formulas in RTF...\n");
13367 g_s.end();
13368 }
13369
13370 if (fm.hasFormulas() && generateDocbook)
13371 {
13372 g_s.begin("Generating images for formulas in Docbook...\n");
13374 g_s.end();
13375 }
13376
13377 g_s.begin("Generating example documentation...\n");
13379 g_s.end();
13380
13381 g_s.begin("Generating file sources...\n");
13383 g_s.end();
13384
13385 g_s.begin("Counting members...\n");
13386 // needs to be done after generating the sources
13387 // but before generating the compound documentation, see bug #12233
13388 countMembers();
13389 g_s.end();
13390
13391 g_s.begin("Generating file documentation...\n");
13393 g_s.end();
13394
13395 g_s.begin("Generating page documentation...\n");
13397 g_s.end();
13398
13399 g_s.begin("Generating group documentation...\n");
13401 g_s.end();
13402
13403 g_s.begin("Generating class documentation...\n");
13405 g_s.end();
13406
13407 g_s.begin("Generating concept documentation...\n");
13409 g_s.end();
13410
13411 g_s.begin("Generating module documentation...\n");
13413 g_s.end();
13414
13415 g_s.begin("Generating namespace documentation...\n");
13417 g_s.end();
13418
13419 if (Config_getBool(GENERATE_LEGEND))
13420 {
13421 g_s.begin("Generating graph info page...\n");
13423 g_s.end();
13424 }
13425
13426 g_s.begin("Generating directory documentation...\n");
13428 g_s.end();
13429
13430 if (g_outputList->size()>0)
13431 {
13433 }
13434
13435 g_s.begin("finalizing index lists...\n");
13436 Doxygen::indexList->finalize();
13437 g_s.end();
13438
13439 g_s.begin("writing tag file...\n");
13440 writeTagFile();
13441 g_s.end();
13442
13443 if (Config_getBool(GENERATE_XML))
13444 {
13445 g_s.begin("Generating XML output...\n");
13447 generateXML();
13449 g_s.end();
13450 }
13451 if (Config_getBool(GENERATE_SQLITE3))
13452 {
13453 g_s.begin("Generating SQLITE3 output...\n");
13455 g_s.end();
13456 }
13457
13458 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13459 {
13460 g_s.begin("Generating AutoGen DEF output...\n");
13461 generateDEF();
13462 g_s.end();
13463 }
13464 if (Config_getBool(GENERATE_PERLMOD))
13465 {
13466 g_s.begin("Generating Perl module output...\n");
13468 g_s.end();
13469 }
13470 if (generateHtml && searchEngine && serverBasedSearch)
13471 {
13472 g_s.begin("Generating search index\n");
13473 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13474 {
13476 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13477 }
13478 else // write data for external search index
13479 {
13481 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13482 if (searchDataFile.isEmpty())
13483 {
13484 searchDataFile="searchdata.xml";
13485 }
13486 if (!Portable::isAbsolutePath(searchDataFile.data()))
13487 {
13488 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13489 }
13490 Doxygen::searchIndex.write(searchDataFile);
13491 }
13492 g_s.end();
13493 }
13494
13495 if (generateRtf)
13496 {
13497 g_s.begin("Combining RTF output...\n");
13498 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13499 {
13500 err("An error occurred during post-processing the RTF files!\n");
13501 }
13502 g_s.end();
13503 }
13504
13505 if (PlantumlManager::instance().needToRun())
13506 {
13507 g_s.begin("Running plantuml with JAVA...\n");
13509 g_s.end();
13510 }
13511
13512 if (MermaidManager::instance().needToRun())
13513 {
13514 g_s.begin("Running mermaid (mmdc)...\n");
13516 g_s.end();
13517 }
13518
13519 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13520 {
13521 g_s.begin("Running dot...\n");
13523 g_s.end();
13524 }
13525
13526 if (generateHtml &&
13527 Config_getBool(GENERATE_HTMLHELP) &&
13528 !Config_getString(HHC_LOCATION).isEmpty())
13529 {
13530 g_s.begin("Running html help compiler...\n");
13532 g_s.end();
13533 }
13534
13535 if ( generateHtml &&
13536 Config_getBool(GENERATE_QHP) &&
13537 !Config_getString(QHG_LOCATION).isEmpty())
13538 {
13539 g_s.begin("Running qhelpgenerator...\n");
13541 g_s.end();
13542 }
13543
13544 g_outputList->cleanup();
13546
13548
13550 {
13551
13552 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13553 if (numThreads<1) numThreads=1;
13554 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13555 (static_cast<double>(Debug::elapsedTime())),
13556 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13557 );
13558 g_s.print();
13559
13561 msg("finished...\n");
13563 }
13564 else
13565 {
13566 msg("finished...\n");
13567 }
13568
13569
13570 /**************************************************************************
13571 * Start cleaning up *
13572 **************************************************************************/
13573
13575
13577 Dir thisDir;
13578 thisDir.remove(Doxygen::filterDBFileName.str());
13580 exitTracing();
13582 delete Doxygen::clangUsrMap;
13584
13585 //dumpDocNodeSizes();
13586}
void readAliases()
Definition aliases.cpp:161
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:147
This class represents an function or template argument list.
Definition arguments.h:65
RefQualifierType refQualifier() const
Definition arguments.h:116
bool noParameters() const
Definition arguments.h:117
bool pureSpecifier() const
Definition arguments.h:113
iterator end()
Definition arguments.h:94
void setTrailingReturnType(const QCString &s)
Definition arguments.cpp:36
bool hasParameters() const
Definition arguments.h:76
bool isDeleted() const
Definition arguments.h:115
QCString trailingReturnType() const
Definition arguments.h:114
size_t size() const
Definition arguments.h:100
void setPureSpecifier(bool b)
Definition arguments.h:121
bool constSpecifier() const
Definition arguments.h:111
void push_back(const Argument &a)
Definition arguments.h:102
bool empty() const
Definition arguments.h:99
void setConstSpecifier(bool b)
Definition arguments.h:119
void setRefQualifier(RefQualifierType t)
Definition arguments.h:126
void setIsDeleted(bool b)
Definition arguments.h:125
iterator begin()
Definition arguments.h:93
bool volatileSpecifier() const
Definition arguments.h:112
void setNoParameters(bool b)
Definition arguments.h:127
void setVolatileSpecifier(bool b)
Definition arguments.h:120
static CitationManager & instance()
Definition cite.cpp:85
void clear()
clears the database
Definition cite.cpp:110
void generatePage()
Generate the citations page.
Definition cite.cpp:331
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:104
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual QCString compoundTypeString() const =0
Returns the type of compound as a string.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns TRUE if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual const MemberDef * getMemberByName(const QCString &) const =0
Returns the member with the given name.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const QCString &templSpec=QCString()) const =0
Returns TRUE iff bcd is a direct or indirect base class of this class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual bool isForwardDeclared() const =0
Returns TRUE if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns TRUE iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual QCString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual std::unique_ptr< ClassDef > deepCopy(const QCString &name) const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:109
@ Singleton
Definition classdef.h:117
@ Interface
Definition classdef.h:112
@ Exception
Definition classdef.h:115
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e. class/struct/union/...
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the namespace this compound is in, or 0 if it has a global scope.
virtual QCString requiresClause() const =0
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void setClassName(const QCString &name)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void makeTemplateArgument(bool b=TRUE)=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const QCString &spec)=0
virtual void setMetaData(const QCString &md)=0
virtual void setFileDef(FileDef *fd)=0
virtual void addUsedClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual ClassDef * insertTemplateInstance(const QCString &fileName, int startLine, int startColumn, const QCString &templSpec, bool &freshInstance)=0
virtual void insertBaseClass(ClassDef *, const QCString &name, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void setTagLessReference(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void findSectionsInDocumentation()=0
virtual void addUsedByClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const QCString &templSpec)=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void setSubGrouping(bool enabled)=0
virtual void addCodePart(const QCString &code, int lineNr, int colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void setInitializer(const QCString &init)=0
virtual void addDocPart(const QCString &doc, int lineNr, int colNr)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
@ ExtCmd
Definition debug.h:36
@ Sections
Definition debug.h:48
@ Time
Definition debug.h:35
@ Qhp
Definition debug.h:44
@ Entries
Definition debug.h:47
static void printFlags()
Definition debug.cpp:138
static void clearFlag(const DebugMask mask)
Definition debug.cpp:123
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:133
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
static double elapsedTime()
Definition debug.cpp:201
static void startTimer()
Definition debug.cpp:196
static bool setFlagStr(const QCString &label)
Definition debug.cpp:104
static void setFlag(const DebugMask mask)
Definition debug.cpp:118
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual QCString docFile() const =0
virtual const QCString & localName() const =0
virtual int getEndBodyLine() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual QCString getDefFileName() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual QCString briefDescription(bool abbreviate=FALSE) const =0
virtual bool isAnonymous() const =0
virtual bool isHidden() const =0
virtual const Definition * findInnerCompound(const QCString &name) const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual QCString documentation() const =0
virtual QCString qualifiedName() const =0
virtual QCString displayName(bool includeScope=TRUE) const =0
virtual bool isArtificial() const =0
virtual QCString briefFile() const =0
virtual QCString getOutputFileBase() const =0
virtual Definition * getOuterScope() const =0
virtual int getStartBodyLine() const =0
virtual int getDefColumn() const =0
virtual bool isReference() const =0
virtual QCString inbodyDocumentation() const =0
virtual QCString inbodyFile() const =0
virtual const QCString & name() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setName(const QCString &name)=0
virtual void setHidden(bool b)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setDocumentation(const QCString &d, const QCString &docFile, int docLine, bool stripWhiteSpace=TRUE)=0
virtual void setDefFile(const QCString &df, int defLine, int defColumn)=0
virtual void addInnerCompound(Definition *d)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setInbodyDocumentation(const QCString &d, const QCString &docFile, int docLine)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setId(const QCString &name)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setBriefDescription(const QCString &b, const QCString &briefFile, int briefLine)=0
virtual void setReference(const QCString &r)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:110
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
std::string absPath() const
Definition dir.cpp:364
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
void setPath(const std::string &path)
Definition dir.cpp:229
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
DirIterator iterator() const
Definition dir.cpp:239
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:357
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:175
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:119
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:115
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:97
static bool suppressDocWarnings
Definition doxygen.h:130
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static bool parseSourcesNeeded
Definition doxygen.h:123
static StringUnorderedSet inputPaths
Definition doxygen.h:103
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:100
static bool clangAssistedParsing
Definition doxygen.h:136
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:119
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ParserManager * parserManager
Definition doxygen.h:129
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:138
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:112
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:110
static QCString verifiedDotPath
Definition doxygen.h:137
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:118
static QCString spaces
Definition doxygen.h:133
static IndexList * indexList
Definition doxygen.h:132
static StaticInitMap staticInitMap
Definition doxygen.h:141
static StringMap tagDestinationMap
Definition doxygen.h:116
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:139
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:96
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static QCString htmlFileExtension
Definition doxygen.h:122
static QCString filterDBFileName
Definition doxygen.h:131
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static bool generatingXmlOutput
Definition doxygen.h:134
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:120
static DefinesPerFileList macroDefinitions
Definition doxygen.h:135
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:127
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:113
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:111
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:125
static StringUnorderedSet tagFileSet
Definition doxygen.h:117
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:101
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static DirRelationLinkedMap dirRelations
Definition doxygen.h:128
static std::mutex addExampleMutex
Definition doxygen.h:140
static ClangUsrMap * clangUsrMap
Definition doxygen.h:126
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:114
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:117
TextStream initializer
initial value (for variables)
Definition entry.h:198
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:184
bool subGrouping
automatically group class members?
Definition entry.h:189
RequirementRefs rqli
references to requirements
Definition entry.h:228
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:140
bool proto
prototype ?
Definition entry.h:188
GroupDocType groupDocType
Definition entry.h:232
QCString metaData
Slice metadata.
Definition entry.h:235
int docLine
line number at which the documentation was found
Definition entry.h:202
QCString bitfields
member's bit fields
Definition entry.h:194
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:216
void markAsProcessed() const
Definition entry.h:168
int endBodyLine
line number where the definition ends
Definition entry.h:219
bool exported
is the symbol exported from a C++20 module
Definition entry.h:190
const TagInfo * tagInfo() const
Definition entry.h:178
QCString includeName
include name (3 arg of \class)
Definition entry.h:200
QCString id
libclang id
Definition entry.h:233
ArgumentLists tArgLists
template argument declarations
Definition entry.h:196
LocalToc localToc
Definition entry.h:234
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:182
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:122
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:229
Entry * parent() const
Definition entry.h:135
QCString inbodyDocs
documentation inside the body of a function
Definition entry.h:207
int startColumn
start column of entry in the source
Definition entry.h:226
QCString relates
related class (doc block)
Definition entry.h:210
bool explicitExternal
explicitly defined as external?
Definition entry.h:187
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:223
QCString fileName
file this entry was extracted from
Definition entry.h:224
RelatesType relatesType
how relates is handled
Definition entry.h:211
QCString write
property write accessor
Definition entry.h:213
QCString args
member argument string
Definition entry.h:193
QCString type
member type
Definition entry.h:174
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:222
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:191
QCString exception
throw specification
Definition entry.h:215
int startLine
start line of entry in the source
Definition entry.h:225
QCString req
C++20 requires clause.
Definition entry.h:236
ArgumentList argList
member arguments as a list
Definition entry.h:195
QCString name
member name
Definition entry.h:175
QCString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:199
int inbodyLine
line number at which the body doc was found
Definition entry.h:208
EntryType section
entry type (see Sections);
Definition entry.h:173
QCString briefFile
file in which the brief desc. was found
Definition entry.h:206
int bodyLine
line number of the body in the source
Definition entry.h:217
int mGrpId
member group id
Definition entry.h:220
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:221
Specifier virt
virtualness of the entry
Definition entry.h:192
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:237
QCString doc
documentation block (partly parsed)
Definition entry.h:201
QCString read
property read accessor
Definition entry.h:212
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:227
QCString docFile
file in which the documentation was found
Definition entry.h:203
Protection protection
class protection
Definition entry.h:181
bool artificial
Artificially introduced item.
Definition entry.h:231
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:230
QCString brief
brief description (doc block)
Definition entry.h:204
int briefLine
line number at which the brief desc. was found
Definition entry.h:205
FileDef * fileDef() const
Definition entry.h:170
int initLines
define/variable initializer lines to show
Definition entry.h:185
bool isStatic
static ?
Definition entry.h:186
QCString inbodyFile
file in which the body doc was found
Definition entry.h:209
TypeSpecifier spec
class/member specifiers
Definition entry.h:183
QCString inside
name of the class in which documents are found
Definition entry.h:214
Wrapper class for the Entry type.
Definition types.h:856
constexpr bool isCompoundDoc() const noexcept
Definition types.h:866
constexpr bool isFile() const noexcept
Definition types.h:865
constexpr bool isDoc() const noexcept
Definition types.h:867
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:863
std::string to_string() const
Definition types.h:868
constexpr bool isScope() const noexcept
Definition types.h:864
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:99
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual QCString absFilePath() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void setDiskName(const QCString &name)=0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual const QCString & docName() const =0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
std::string readLink() const
Definition fileinfo.cpp:84
bool isRelative() const
Definition fileinfo.cpp:58
bool isSymLink() const
Definition fileinfo.cpp:77
bool exists() const
Definition fileinfo.cpp:30
std::string fileName() const
Definition fileinfo.cpp:118
bool isReadable() const
Definition fileinfo.cpp:44
bool isDir() const
Definition fileinfo.cpp:70
bool isFile() const
Definition fileinfo.cpp:63
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
Class representing all files with a certain base name.
Definition filename.h:30
Ordered dictionary of FileName objects.
Definition filename.h:69
void initFromRepository(const QCString &dir)
Definition formula.cpp:59
bool hasFormulas() const
Definition formula.cpp:720
void generateImages(const QCString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
void checkRepositories()
Definition formula.cpp:172
static FormulaManager & instance()
Definition formula.cpp:53
A model of a group of symbols.
Definition groupdef.h:52
virtual QCString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual void setGroupTitle(const QCString &newtitle)=0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:96
static void init()
Definition htmlgen.cpp:1248
static void writeSearchPage()
Definition htmlgen.cpp:3238
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1595
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1412
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1421
static void writeExternalSearchPage()
Definition htmlgen.cpp:3337
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1583
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1589
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const QCString hhpFileName
Definition htmlhelp.h:89
static Index & instance()
Definition index.cpp:108
void countDataStructures()
Definition index.cpp:264
A list of index interfaces.
Definition indexlist.h:64
Generator for LaTeX output.
Definition latexgen.h:94
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:696
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:702
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:690
static void init()
Definition latexgen.cpp:632
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1437
void parse(const QCString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1470
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:272
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual QCString typeString() const =0
virtual QCString requiresClause() const =0
virtual bool isFriend() const =0
virtual bool isForeign() const =0
virtual QCString definition() const =0
virtual bool isRelated() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual QCString excpString() const =0
virtual const ClassDef * getClassDef() const =0
virtual const ArgumentList & templateArguments() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual QCString bitfieldString() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual QCString argsString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual const QCString & initializer() const =0
virtual void setClassDefOfAnonymousType(const ClassDef *cd)=0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual void setDefinition(const QCString &d)=0
virtual void setToAnonymousMember(MemberDef *m)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setExplicitExternal(bool b, const QCString &df, int line, int column)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setBitfields(const QCString &s)=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=FALSE)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void setPrototype(bool p, const QCString &df, int line, int column)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setArgsString(const QCString &as)=0
virtual void setInitializer(const QCString &i)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void setAnonymousEnumType(const MemberDef *md)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void setDeclFile(const QCString &df, int line, int column)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:125
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:51
size_t size() const
Definition membername.h:48
iterator begin()
Definition membername.h:37
iterator end()
Definition membername.h:38
void push_back(Ptr &&p)
Definition membername.h:54
Ordered dictionary of MemberName objects.
Definition membername.h:63
const MemberDef * find(const QCString &name) const
Definition memberlist.h:93
const MemberDef * findRev(const QCString &name) const
Definition memberlist.h:106
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:252
static MermaidManager & instance()
Definition mermaid.cpp:33
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void setMetaData(const QCString &m)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void insertMember(MemberDef *md)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void setFileName(const QCString &fn)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
bool needsPreprocessing(const QCString &) const override
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
void parseInput(const QCString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const QCString &) override
Callback function called by the comment block scanner.
Abstract interface for outline parsers.
Definition parserintf.h:42
virtual bool needsPreprocessing(const QCString &extension) const =0
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:315
A model of a page symbol.
Definition pagedef.h:26
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const QCString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:183
static PlantumlManager & instance()
Definition plantuml.cpp:232
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:390
void processFile(const QCString &fileName, const std::string &input, std::string &output)
Definition pre.l:4276
void addSearchDir(const QCString &dir)
Definition pre.l:4258
This is an alternative implementation of QCString.
Definition qcstring.h:103
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:426
int toInt(bool *ok=nullptr, int base=10) const
Definition qcstring.cpp:254
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:170
bool startsWith(const char *s) const
Definition qcstring.h:511
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:245
QCString lower() const
Definition qcstring.h:253
bool endsWith(const char *s) const
Definition qcstring.h:528
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:597
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:167
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:264
QCString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:197
const std::string & str() const
Definition qcstring.h:556
QCString & setNum(short n)
Definition qcstring.h:463
QCString right(size_t len) const
Definition qcstring.h:238
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:173
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:150
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
QCString & replace(size_t index, size_t len, const char *s)
Definition qcstring.cpp:217
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:176
QCString left(size_t len) const
Definition qcstring.h:233
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:148
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:217
void clear()
Definition qcstring.h:186
Definition qhp.h:27
static const QCString qhpFileName
Definition qhp.h:47
static QCString getQchFileName()
Definition qhp.cpp:426
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:461
static bool preProcessFileInplace(const QCString &path, const QCString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2461
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:394
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:409
static RefListManager & instance()
Definition reflist.h:121
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
class that provide information about a section.
Definition section.h:58
QCString ref() const
Definition section.h:72
QCString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
SectionInfo * replace(const QCString &label, const QCString &fileName, int lineNr, const QCString &title, SectionType type, int level, const QCString &ref=QCString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:270
void print()
Definition doxygen.cpp:247
void begin(const char *name)
Definition doxygen.cpp:234
void end()
Definition doxygen.cpp:240
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:271
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
const Definition * resolveSymbol(const Definition *scope, const QCString &name, const QCString &args=QCString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
const ClassDef * resolveClass(const Definition *scope, const QCString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
QCString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
ClassDefMutable * resolveClassMutable(const Definition *scope, const QCString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:216
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
Wrapper class for a number of boolean properties.
Definition types.h:694
std::string to_string() const
Definition types.h:738
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const QCString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:799
std::unique_ptr< ClassDef > createClassDef(const QCString &fileName, int startLine, int startColumn, const QCString &name, ClassDef::CompoundType ct, const QCString &ref, const QCString &fName, bool isSymbol, bool isJavaEnum)
Factory method to create a new ClassDef object.
Definition classdef.cpp:573
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:95
std::map< std::string, int > TemplateNameMap
Definition classdef.h:93
ClassDefMutable * getClassMutable(const QCString &key)
Definition classdef.h:470
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:230
Object representing the matching results.
Definition regex.h:151
First pass comment processing.
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
ConceptDefMutable * toConceptDefMutable(Definition *d)
std::unique_ptr< ConceptDef > createConceptDef(const QCString &fileName, int startLine, int startColumn, const QCString &name, const QCString &tagRef, const QCString &tagFile)
ConceptDefMutable * getConceptMutable(const QCString &key)
Definition conceptdef.h:106
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_updateString(name, value)
Definition config.h:39
#define Config_updateBool(name, value)
Definition config.h:40
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_updateList(name,...)
Definition config.h:43
#define Config_getEnum(name)
Definition config.h:35
std::set< std::string > StringSet
Definition containers.h:31
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::map< std::string, std::string > StringMap
Definition containers.h:30
std::vector< std::string > StringVector
Definition containers.h:33
void parseFuncDecl(const QCString &decl, const SrcLangExt lang, QCString &clName, QCString &type, QCString &name, QCString &args, QCString &funcTempList, QCString &exceptions)
Definition declinfo.l:325
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const QCString &argsString, QCString *extraTypeChars=nullptr)
Definition defargs.l:826
void generateDEF()
Definition defgen.cpp:464
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:170
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
void buildDirectories()
Definition dirdef.cpp:1119
void computeDirDependencies()
Definition dirdef.cpp:1193
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1210
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:49
#define AUTO_TRACE(...)
Definition docnode.cpp:48
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:50
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5415
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:939
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5430
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3243
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:432
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6799
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2172
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9549
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4973
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9209
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const QCString &fn, ClangTUParser *clangParser, bool newTU)
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3039
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6100
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const QCString &type, const QCString &name, const QCString &args, bool fromAnnScope, MemberDef *fromAnnMemb, Protection prot, Relationship related)
Definition doxygen.cpp:2592
static void computeTemplateClassRelations()
Definition doxygen.cpp:5509
static std::string resolveSymlink(const std::string &path)
void adjustConfiguration()
adjust globals that depend on configuration settings.
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1557
static void findDEV(const MemberNameLinkedMap &mnsd)
Definition doxygen.cpp:8263
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1165
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:330
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:589
static StringVector g_inputFiles
Definition doxygen.cpp:188
void printSectionsTree()
static void copyExtraFiles(const StringVector &files, const QCString &filesOption, const QCString &outputOption, bool toIndex)
class Statistics g_s
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const QCString &fileName, const QCString &memName)
Definition doxygen.cpp:2276
static void generateXRefPages()
Definition doxygen.cpp:5688
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2325
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:287
@ Undocumented
Definition doxygen.cpp:290
@ TemplateInstances
Definition doxygen.cpp:288
@ DocumentedOnly
Definition doxygen.cpp:289
void distributeClassGroupRelations()
Definition doxygen.cpp:1496
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9749
void checkConfiguration()
check and resolve config options
QCString stripTemplateSpecifiers(const QCString &s)
Definition doxygen.cpp:686
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:5015
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4943
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:472
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5464
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4622
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:2003
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1326
static void resolveClassNestingRelations()
Definition doxygen.cpp:1381
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static void findMember(const Entry *root, const QCString &relates, const QCString &type, const QCString &args, QCString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6842
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5378
static void addMemberFunction(const Entry *root, MemberName *mn, const QCString &scopeName, const QCString &namespaceName, const QCString &className, const QCString &funcTyp, const QCString &funcName, const QCString &funcArgs, const QCString &funcTempList, const QCString &exceptions, const QCString &type, const QCString &args, bool isFriend, TypeSpecifier spec, const QCString &relates, const QCString &funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6312
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8507
static void generateExampleDocs()
void generateOutput()
static void stopDoxygen(int)
static void findTemplateInstanceRelation(const Entry *root, Definition *context, ClassDefMutable *templateClass, const QCString &templSpec, const TemplateNameMap &templateNames, bool isArtificial)
Definition doxygen.cpp:4892
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6214
static void computeMemberReferences()
Definition doxygen.cpp:5578
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4536
static void addMembersToMemberGroup()
Definition doxygen.cpp:9414
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:8144
static void addOverloaded(const Entry *root, MemberName *mn, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &funcDecl, const QCString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6731
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9919
static void distributeConceptGroups()
Definition doxygen.cpp:1348
static void transferFunctionDocumentation()
Definition doxygen.cpp:4455
static void setAnonymousEnumType()
Definition doxygen.cpp:9154
static void sortMemberLists()
Definition doxygen.cpp:9059
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8635
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4585
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7648
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3524
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:447
static void generateFileDocs()
Definition doxygen.cpp:8872
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9662
static void findUsedClassesForClass(const Entry *root, Definition *context, ClassDefMutable *masterCd, ClassDefMutable *instanceCd, bool isArtificial, const ArgumentList *actualArgs=nullptr, const TemplateNameMap &templateNames=TemplateNameMap())
Definition doxygen.cpp:4685
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7618
static void addLocalObjCMethod(const Entry *root, const QCString &scopeName, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &exceptions, const QCString &funcDecl, TypeSpecifier spec)
Definition doxygen.cpp:6258
static bool findGlobalMember(const Entry *root, const QCString &namespaceName, const QCString &type, const QCString &name, const QCString &tempArg, const QCString &, const QCString &decl, TypeSpecifier)
Definition doxygen.cpp:5892
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9452
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7852
static void generatePageDocs()
static void resolveUserReferences()
Definition doxygen.cpp:9981
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9810
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:311
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3661
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3624
static void generateFileSources()
Definition doxygen.cpp:8706
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6130
static void usage(const QCString &name, const QCString &versionString)
static MemberDef * addVariableToFile(const Entry *root, MemberType mtype, const QCString &scope, const QCString &type, const QCString &name, const QCString &args, bool fromAnnScope, MemberDef *fromAnnMemb)
Definition doxygen.cpp:2775
static void generateClassDocs()
Definition doxygen.cpp:9307
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1834
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2575
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9635
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5702
static void countMembers()
Definition doxygen.cpp:9168
static void processTagLessClasses(const Definition *root, const Container *cd, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const QCString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1690
void clearAll()
Definition doxygen.cpp:202
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3693
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:491
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2264
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4358
static void findEnums(const Entry *root)
Definition doxygen.cpp:7676
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9284
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8102
static void addListReferences()
Definition doxygen.cpp:5679
static void exitDoxygen() noexcept
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5356
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const QCString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:782
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const QCString &name)
Definition doxygen.cpp:4650
static void copyLogo(const QCString &outputOption, bool toIndex)
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:361
static void runHtmlHelpCompiler()
static QCString extractClassName(const Entry *root)
Definition doxygen.cpp:5387
static void addMembersToIndex()
Definition doxygen.cpp:8297
static bool g_dumpSymbolMap
Definition doxygen.cpp:192
static void version(const bool extended)
static void addGlobalFunction(const Entry *root, const QCString &rname, const QCString &sc)
Definition doxygen.cpp:3944
static OutputList * g_outputList
Definition doxygen.cpp:189
static void findMainPage(Entry *root)
Definition doxygen.cpp:9849
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:897
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2016
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:867
static bool g_successfulRun
Definition doxygen.cpp:191
static void addSourceReferences()
Definition doxygen.cpp:8932
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1530
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9354
static void flushUnresolvedRelations()
Definition doxygen.cpp:9591
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9101
void readConfiguration(int argc, char **argv)
static void readDir(FileInfo *fi, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool errorIfNotExist, bool recursive, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static QCString g_commentFileName
Definition doxygen.cpp:193
static void findDocumentedEnumValues()
Definition doxygen.cpp:8289
static void findTagLessClasses()
Definition doxygen.cpp:1782
static void copyIcon(const QCString &outputOption, bool toIndex)
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8339
static void computeClassRelations()
Definition doxygen.cpp:5484
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4053
static void checkPageRelations()
Definition doxygen.cpp:9961
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1316
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8177
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9931
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7468
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3099
static StringSet g_usingDeclarations
Definition doxygen.cpp:190
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3642
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9624
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1151
static void buildPageList(Entry *root)
Definition doxygen.cpp:9822
static void writeTagFile()
static Definition * buildScopeFromQualifiedName(const QCString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:713
static void addRequirementReferences()
Definition doxygen.cpp:5671
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:194
static void findSectionsInDocumentation()
Definition doxygen.cpp:9490
static void mergeCategories()
Definition doxygen.cpp:8654
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:199
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6116
static void buildFileList(const Entry *root)
Definition doxygen.cpp:503
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1141
static void addMethodToClass(const Entry *root, ClassDefMutable *cd, const QCString &rtype, const QCString &rname, const QCString &rargs, bool isFriend, Protection protection, bool stat, Specifier virt, TypeSpecifier spec, const QCString &relates)
Definition doxygen.cpp:3808
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5448
static void computeTooltipTexts()
Definition doxygen.cpp:9108
void readFileOrDirectory(const QCString &s, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool recursive, bool errorIfNotExist, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3311
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &funcDecl, const QCString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6668
void cleanUpDoxygen()
static void findBaseClassesForClass(const Entry *root, Definition *context, ClassDefMutable *masterCd, ClassDefMutable *instanceCd, FindBaseClassRelation_Mode mode, bool isArtificial, const ArgumentList *actualArgs=nullptr, const TemplateNameMap &templateNames=TemplateNameMap())
Definition doxygen.cpp:4841
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8676
static void generateConceptDocs()
Definition doxygen.cpp:9333
static void combineUsingRelations()
Definition doxygen.cpp:9389
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5853
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2323
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1336
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4985
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5868
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5614
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4488
static void buildDefineList()
Definition doxygen.cpp:9011
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3756
static void computeMemberRelations()
Definition doxygen.cpp:8619
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2159
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8539
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:187
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:80
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:84
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:86
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:82
std::unique_ptr< FileDef > createFileDef(const QCString &p, const QCString &n, const QCString &ref, const QCString &dn)
Definition filedef.cpp:268
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1966
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:44
static void startScope(yyscan_t yyscanner)
start scope
void addNamespaceToGroups(const Entry *root, NamespaceDef *nd)
void addGroupToGroups(const Entry *root, GroupDef *subGroup)
void addClassToGroups(const Entry *root, ClassDef *cd)
void addDirToGroups(const Entry *root, DirDef *dd)
std::unique_ptr< GroupDef > createGroupDef(const QCString &fileName, int line, const QCString &name, const QCString &title, const QCString &refFileName)
Definition groupdef.cpp:178
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void startTitle(OutputList &ol, const QCString &fileName, const DefinitionMutable *def)
Definition index.cpp:386
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const QCString &navPath)
Definition index.cpp:429
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4102
void endTitle(OutputList &ol, const QCString &fileName, const QCString &name)
Definition index.cpp:396
void startFile(OutputList &ol, const QCString &name, bool isSource, const QCString &manName, const QCString &title, HighlightedItem hli, bool additionalIndices, const QCString &altSidebarName, int hierarchyLevel, const QCString &allMembersFile)
Definition index.cpp:403
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5816
Translator * theTranslator
Definition language.cpp:71
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:73
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:22
void writeDefaultLayoutFile(const QCString &fileName)
Definition layout.cpp:1734
void printLayout()
Definition layout.cpp:1822
std::unique_ptr< MemberDef > createMemberDefAlias(const Definition *newScope, const MemberDef *aliasMd)
MemberDefMutable * toMemberDefMutable(Definition *d)
void combineDeclarationAndDefinition(MemberDefMutable *mdec, MemberDefMutable *mdef)
MemberDef * toMemberDef(Definition *d)
std::unique_ptr< MemberDef > createMemberDef(const QCString &defFileName, int defLine, int defColumn, const QCString &type, const QCString &name, const QCString &args, const QCString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const QCString &metaData)
Factory method to create a new instance of a MemberDef.
std::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:27
QCString warn_line(const QCString &file, int line)
Definition message.cpp:214
void initWarningFormat()
Definition message.cpp:236
void warn_flush()
Definition message.cpp:229
void finishWarnExit()
Definition message.cpp:294
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void deinit()
bool parse(const QCString &fileName, bool update=FALSE, CompareMode compareMode=CompareMode::Full)
void init()
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=FALSE)
void updateObsolete()
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:516
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:497
FILE * popen(const QCString &name, const QCString &type)
Definition portable.cpp:479
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:648
double getSysElapsedTime()
Definition portable.cpp:97
QCString pathListSeparator()
Definition portable.cpp:383
uint32_t pid()
Definition portable.cpp:248
int pclose(FILE *stream)
Definition portable.cpp:488
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
void setenv(const QCString &variable, const QCString &value)
Definition portable.cpp:286
const char * commandExtension()
Definition portable.cpp:461
QCString getenv(const QCString &variable)
Definition portable.cpp:321
void setShortDir()
Definition portable.cpp:553
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
void replaceNamespaceAliases(QCString &name)
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
std::unique_ptr< NamespaceDef > createNamespaceDef(const QCString &defFileName, int defLine, int defColumn, const QCString &name, const QCString &ref, const QCString &refFile, const QCString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
NamespaceDef * getResolvedNamespace(const QCString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const QCString &key)
std::unordered_set< const NamespaceDef * > NamespaceDefSet
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:844
std::unique_ptr< PageDef > createPageDef(const QCString &f, int l, const QCString &n, const QCString &d, const QCString &t)
Definition pagedef.cpp:82
void generatePerlMod()
void setPerlModDoxyfile(const QCString &qs)
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
void * portable_iconv_open(const char *tocode, const char *fromcode)
int qstricmp(const char *s1, const char *s2)
Definition qcstring.cpp:530
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571
int qstricmp_sort(const char *str1, const char *str2)
Definition qcstring.h:86
const char * qPrint(const char *s)
Definition qcstring.h:691
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
uint32_t qstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition qcstring.h:58
#define ASSERT(x)
Definition qcstring.h:39
int qstrcmp(const char *str1, const char *str2)
Definition qcstring.h:69
void initSearchIndexer()
void finalizeSearchIndexer()
static void addMemberToSearchIndex(const MemberDef *md)
void createJavaScriptSearchIndex()
void writeJavaScriptSearchIndex()
Javascript based search engine.
void generateHtmlForComment(const std::string &fn, const std::string &text)
Helper for implemented the -c option of doxygen, which produces HTML output for a given doxygen forma...
void generateSqlite3()
void addSTLSupport(std::shared_ptr< Entry > &root)
Add stub entries for the most used classes in the standard template library.
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:84
This class contains the information about the argument of a function or template.
Definition arguments.h:27
QCString type
Definition arguments.h:42
QCString name
Definition arguments.h:44
QCString defval
Definition arguments.h:46
QCString array
Definition arguments.h:45
Class that contains information about an inheritance relation.
Definition classdef.h:55
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:60
This class stores information about an inheritance relation.
Definition entry.h:91
Protection prot
inheritance type
Definition entry.h:96
Specifier virt
virtualness
Definition entry.h:97
QCString name
the name of the base class
Definition entry.h:95
Data associated with description found in the body.
Definition definition.h:64
Grouping info.
Definition types.h:227
QCString groupname
name of the group
Definition types.h:257
static constexpr const char * getGroupPriName(GroupPri_t priority) noexcept
Definition types.h:240
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
GroupPri_t pri
priority of this definition
Definition types.h:258
static bool execute(const QCString &htmldir)
Definition htags.cpp:38
static bool loadFilemap(const QCString &htmldir)
Definition htags.cpp:107
static bool useHtags
Definition htags.h:23
stat(const char *n, double el)
Definition doxygen.cpp:268
const char * name
Definition doxygen.cpp:265
This struct is used to capture the tag file information for an Entry.
Definition entry.h:104
QCString fileName
Definition entry.h:106
QCString tagName
Definition entry.h:105
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:52
void initTracing(const QCString &logFile, bool timing)
Definition trace.cpp:22
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:569
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const QCString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const QCString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:2031
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:567
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4637
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4343
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5253
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5977
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2275
void addCodeOnlyMappings()
Definition util.cpp:5247
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4408
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4258
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:881
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:230
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:329
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5357
bool readInputFile(const QCString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:5592
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5746
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6336
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1299
void addRefItem(const RefItemVector &sli, const QCString &key, const QCString &prefix, const QCString &name, const QCString &title, const QCString &args, const Definition *scope)
Definition util.cpp:4867
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3054
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1494
QCString filterTitle(const QCString &title)
Definition util.cpp:5672
QCString resolveTypeDef(const Definition *context, const QCString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:373
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:4959
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6864
void initDefaultExtensionMapping()
Definition util.cpp:5180
bool findAndRemoveWord(QCString &sentence, const char *word)
removes occurrences of whole word from sentence, while keeps internal spaces and reducing multiple se...
Definition util.cpp:5029
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3543
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5931
EntryType guessSection(const QCString &name)
Definition util.cpp:338
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3736
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1254
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5937
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6901
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6953
QCString projectLogoFile()
Definition util.cpp:3176
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2131
bool copyFile(const QCString &src, const QCString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:5897
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4570
QCString getOverloadDocs()
Definition util.cpp:4132
void cleanupInlineGraph()
Definition util.cpp:7030
int getPrefixIndex(const QCString &name)
Definition util.cpp:3267
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:870
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5148
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5295
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:218
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2919
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4682
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4964
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2317