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;
165bool Doxygen::parseSourcesNeeded = false;
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
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 DString &funcDecl,
276 const ArgumentList *al,bool over_load,TypeSpecifier spec);
277static void findMember(const Entry *root,
278 const DString &relates,
279 const DString &type,
280 const DString &args,
281 DString 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 DString 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.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
336 }
337 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
338 DString 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.empty() &&
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 {
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.empty() ? root->doc : DString(" "),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
436 buildGroupListFiltered(root,false,false);
437 // then process the @addtogroup, @weakgroup blocks
438 buildGroupListFiltered(root,true,false);
439
440 // --- then also process external groups
441 // first process the @defgroups blocks
442 buildGroupListFiltered(root,false,true);
443 // then process the @addtogroup, @weakgroup blocks
444 buildGroupListFiltered(root,true,true);
445}
446
447static void findGroupScope(const Entry *root)
448{
449 if (root->section.isGroupDoc() && !root->name.empty() &&
450 root->parent() && !root->parent()->name.empty())
451 {
453 if (gd)
454 {
455 DString 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.empty())
475 {
476 AUTO_TRACE("additional={}",additional);
477 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
478 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
479 {
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
495 organizeSubGroupsFiltered(root,false);
496 //printf("Additional groups\n");
497 // then process the @addtogroup, @weakgroup blocks
498 organizeSubGroupsFiltered(root,true);
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.empty() && !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 DString fn = root->fileName;
517 size_t newIndex=fn.rfind('/');
518 if (newIndex==DString::npos)
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;
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 {
566 DString text(4096, DString::ExplicitSize);
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().empty() ||
593 !root->brief.stripWhiteSpace().empty() ||
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 DString includeFile = root->includeFile;
602 if (!includeFile.empty() && includeFile.at(0)=='"')
603 {
604 local = true;
605 includeFile=includeFile.mid(1,includeFile.length()-2);
606 }
607 else if (!includeFile.empty() && 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.empty() &&
618 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
619 )
620 { // explicit request
621 DString 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.empty() && 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 DString iName = !root->includeName.empty() ?
651 root->includeName : includeFile;
652 if (!iName.empty()) // 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.empty())
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.empty());
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 DString 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 DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
714{
715 DString 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 DString fullScope;
722 while (i<level)
723 {
724 int idx=getScopeFragment(name,p,&l);
725 if (idx==-1) return prevScope;
726 DString nsName = name.mid(idx,l);
727 if (nsName.empty()) return prevScope;
728 if (!fullScope.empty()) fullScope+="::";
729 fullScope+=nsName;
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('<')==DString::npos) // 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:DString(),
748 tagInfo?tagInfo->fileName:DString())));
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 DString 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 DString fqn = usedName + scope.mid(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 DString &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 size_t i=0, p=0;
874 auto alIt = tArgLists.begin();
875 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
876 {
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 DString scName;
945 if (root->parent()->section.isScope())
946 {
947 scName=root->parent()->name;
948 }
949 // name without parent's scope
950 DString fullName = root->name;
951
952 // strip off any template parameters (but not those for specializations)
953 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
954 {
955 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
956 }
957 fullName=stripTemplateSpecifiersFromScope(fullName);
958
959 // name with scope (if not present already)
960 DString qualifiedName = fullName;
961 if (!scName.empty() && !leftScopeMatch(scName,fullName))
962 {
963 qualifiedName.prepend(scName+"::");
964 }
965
966 // see if we already found the class before
967 ClassDefMutable *cd = getClassMutable(qualifiedName);
968
969 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
970
971 if (cd)
972 {
973 fullName=cd->name();
974 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
975 //if (cd->templateArguments()==0)
976 //{
977 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
978 // cd->setTemplateArguments(tArgList);
979 //}
980
981 cd->setDocumentation(root->doc,root->docFile,root->docLine);
982 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
983 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
984 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
985
986 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
987 {
988 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
989 if (root->bodyLine!=-1)
990 {
991 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
992 cd->setBodyDef(fd);
993 }
994 }
995
996 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
997 {
998 // this happens if a template class declared with @class is found
999 // before the actual definition or if a forward declaration has different template
1000 // parameter names.
1001 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1002 if (tArgList)
1003 {
1004 cd->setTemplateArguments(*tArgList);
1005 }
1006 }
1007 if (cd->requiresClause().empty() && !root->req.empty())
1008 {
1009 cd->setRequiresClause(root->req);
1010 }
1011
1013
1014 cd->setMetaData(root->metaData);
1015 }
1016 else // new class
1017 {
1019
1020 DString className;
1021 DString namespaceName;
1022 extractNamespaceName(fullName,className,namespaceName);
1023
1024 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1025 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1026
1027 DString tagName;
1028 DString refFileName;
1029 const TagInfo *tagInfo = root->tagInfo();
1030 if (tagInfo)
1031 {
1032 tagName = tagInfo->tagName;
1033 refFileName = tagInfo->fileName;
1034 if (fullName.find("::")!=DString::npos)
1035 // symbols imported via tag files may come without the parent scope,
1036 // so we artificially create it here
1037 {
1038 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1039 }
1040 }
1041 std::unique_ptr<ArgumentList> tArgList;
1042 size_t i=0;
1043 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1044 (i=fullName.find('<'))!=DString::npos)
1045 {
1046 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1047 // name and template arguments here
1048 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1049 if (i!=DString::npos && 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
1050 // A -> A
1051 // A<T> -> A-1-g
1052 // A<T,S> -> A-2-g
1053 {
1054 fullName=mangleCSharpGenericName(fullName);
1055 }
1056 else
1057 {
1058 fullName=fullName.left(i);
1059 }
1060 }
1061 else
1062 {
1063 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1064 }
1065 // add class to the list
1066 cd = toClassDefMutable(
1067 Doxygen::classLinkedMap->add(fullName,
1068 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1069 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1070 if (cd)
1071 {
1072 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1073 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1074 fmt::ptr(tagInfo),root->hidden,root->artificial);
1075 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1076 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1077 cd->setLanguage(root->lang);
1078 cd->setId(root->id);
1079 cd->setHidden(root->hidden);
1080 cd->setArtificial(root->artificial);
1081 cd->setClassSpecifier(root->spec);
1082 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1083 {
1085 }
1086 cd->addQualifiers(root->qualifiers);
1087 cd->setTypeConstraints(root->typeConstr);
1088 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1089 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1090
1091 if (tArgList)
1092 {
1093 cd->setTemplateArguments(*tArgList);
1094 }
1095 cd->setRequiresClause(root->req);
1096 cd->setProtection(root->protection);
1097 cd->setIsStatic(root->isStatic);
1098
1099 // file definition containing the class cd
1100 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1101 cd->setBodyDef(fd);
1102
1103 cd->setMetaData(root->metaData);
1104
1105 cd->insertUsedFile(fd);
1106 }
1107 else
1108 {
1109 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1110 }
1111 }
1112
1113 if (cd)
1114 {
1116 if (!root->subGrouping) cd->setSubGrouping(false);
1117 if (!root->spec.isForwardDecl())
1118 {
1119 if (cd->hasDocumentation())
1120 {
1121 addIncludeFile(cd,fd,root);
1122 }
1123 if (fd && root->section.isCompound())
1124 {
1125 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1126 cd->setFileDef(fd);
1127 fd->insertClass(cd);
1128 }
1129 }
1130 addClassToGroups(root,cd);
1132 cd->setRefItems(root->sli);
1133 cd->setRequirementReferences(root->rqli);
1134 }
1135}
1136
1137//----------------------------------------------------------------------
1138// build a list of all classes mentioned in the documentation
1139// and all classes that have a documentation block before their definition.
1140static void buildClassList(const Entry *root)
1141{
1142 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1143 {
1144 AUTO_TRACE();
1145 addClassToContext(root);
1146 }
1147 for (const auto &e : root->children()) buildClassList(e.get());
1148}
1149
1150static void buildClassDocList(const Entry *root)
1151{
1152 if ((root->section.isCompoundDoc()) && !root->name.empty())
1153 {
1154 AUTO_TRACE();
1155 addClassToContext(root);
1156 }
1157 for (const auto &e : root->children()) buildClassDocList(e.get());
1158}
1159
1160//----------------------------------------------------------------------
1161// build a list of all classes mentioned in the documentation
1162// and all classes that have a documentation block before their definition.
1163
1164static void addConceptToContext(const Entry *root)
1165{
1166 AUTO_TRACE();
1167 FileDef *fd = root->fileDef();
1168
1169 DString scName;
1170 if (root->parent()->section.isScope())
1171 {
1172 scName=root->parent()->name;
1173 }
1174
1175 // name with scope (if not present already)
1176 DString qualifiedName = root->name;
1177 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1178 {
1179 qualifiedName.prepend(scName+"::");
1180 }
1181
1182 // see if we already found the concept before
1183 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1184
1185 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1186
1187 if (cd)
1188 {
1189 qualifiedName=cd->name();
1190 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1191
1192 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1193 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1194
1195 addIncludeFile(cd,fd,root);
1196 }
1197 else // new concept
1198 {
1199 DString className;
1200 DString namespaceName;
1201 extractNamespaceName(qualifiedName,className,namespaceName);
1202
1203 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1204 qualifiedName,namespaceName,className,root->brief,root->doc);
1205
1206 DString tagName;
1207 DString refFileName;
1208 const TagInfo *tagInfo = root->tagInfo();
1209 if (tagInfo)
1210 {
1211 tagName = tagInfo->tagName;
1212 refFileName = tagInfo->fileName;
1213 if (qualifiedName.find("::")!=DString::npos)
1214 // symbols imported via tag files may come without the parent scope,
1215 // so we artificially create it here
1216 {
1217 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1218 }
1219 }
1220 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1221 // add concept to the list
1223 Doxygen::conceptLinkedMap->add(qualifiedName,
1224 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1225 qualifiedName,tagName,refFileName)));
1226 if (cd)
1227 {
1228 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1229 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1230 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1231 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1232 cd->setLanguage(root->lang);
1233 cd->setId(root->id);
1234 cd->setHidden(root->hidden);
1235 cd->setGroupId(root->mGrpId);
1236 if (tArgList)
1237 {
1238 cd->setTemplateArguments(*tArgList);
1239 }
1240 cd->setInitializer(root->initializer.str());
1241 // file definition containing the class cd
1242 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1243 cd->setBodyDef(fd);
1245 cd->setRefItems(root->sli);
1246 cd->setRequirementReferences(root->rqli);
1247 addIncludeFile(cd,fd,root);
1248
1249 // also add namespace to the correct structural context
1250 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1252 {
1254 if (dm)
1255 {
1256 dm->addInnerCompound(cd);
1257 }
1258 cd->setOuterScope(d);
1259 }
1260 for (const auto &ce : root->children())
1261 {
1262 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1263 if (ce->section.isConceptDocPart())
1264 {
1265 cd->addSectionsToDefinition(ce->anchors);
1266 cd->setRefItems(ce->sli);
1267 cd->setRequirementReferences(ce->rqli);
1268 if (!ce->brief.empty())
1269 {
1270 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1271 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1272 }
1273 if (!ce->doc.empty())
1274 {
1275 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1276 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1277 }
1278 }
1279 else if (ce->section.isConceptCodePart())
1280 {
1281 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1282 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1283 }
1284 }
1285 }
1286 else
1287 {
1288 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1289 }
1290 }
1291
1292 if (cd)
1293 {
1295 for (const auto &ce : root->children())
1296 {
1297 if (ce->section.isConceptDocPart())
1298 {
1299 cd->addSectionsToDefinition(ce->anchors);
1300 }
1301 }
1302 if (fd)
1303 {
1304 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1305 cd->setFileDef(fd);
1306 fd->insertConcept(cd);
1307 }
1308 addConceptToGroups(root,cd);
1310 cd->setRefItems(root->sli);
1311 cd->setRequirementReferences(root->rqli);
1312 }
1313}
1314
1315static void findModuleDocumentation(const Entry *root)
1316{
1317 if (root->section.isModuleDoc())
1318 {
1319 AUTO_TRACE();
1321 }
1322 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1323}
1324
1325static void buildConceptList(const Entry *root)
1326{
1327 if (root->section.isConcept())
1328 {
1329 AUTO_TRACE();
1330 addConceptToContext(root);
1331 }
1332 for (const auto &e : root->children()) buildConceptList(e.get());
1333}
1334
1335static void buildConceptDocList(const Entry *root)
1336{
1337 if (root->section.isConceptDoc())
1338 {
1339 AUTO_TRACE();
1340 addConceptToContext(root);
1341 }
1342 for (const auto &e : root->children()) buildConceptDocList(e.get());
1343}
1344
1345// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1346// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1348{
1349 AUTO_TRACE();
1350 for (const auto &cd : *Doxygen::conceptLinkedMap)
1351 {
1352 if (cd->groupId()!=DOX_NOGROUP)
1353 {
1354 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1355 {
1356 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1357 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1358 {
1359 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1360 if (ocdm)
1361 {
1362 for (const auto &gd : cd->partOfGroups())
1363 {
1364 if (gd)
1365 {
1366 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1367 ocdm->makePartOfGroup(gd);
1368 gd->addConcept(ocd.get());
1369 }
1370 }
1371 }
1372 }
1373 }
1374 }
1375 }
1376}
1377
1378//----------------------------------------------------------------------
1379
1381{
1382 ClassDefSet visitedClasses;
1383
1384 bool done=false;
1385 //int iteration=0;
1386 while (!done)
1387 {
1388 done=true;
1389 //++iteration;
1390 struct ClassAlias
1391 {
1392 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1393 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1394 DString aliasFullName;
1395 std::unique_ptr<ClassDef> aliasCd;
1396 DefinitionMutable *aliasContext;
1397 };
1398 std::vector<ClassAlias> aliases;
1399 for (const auto &icd : *Doxygen::classLinkedMap)
1400 {
1401 ClassDefMutable *cd = toClassDefMutable(icd.get());
1402 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1403 {
1404 DString name = stripAnonymousNamespaceScope(icd->name());
1405 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1406 // also add class to the correct structural context
1408 name,icd->getFileDef(),nullptr);
1409 if (d)
1410 {
1411 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1413 if (dm)
1414 {
1415 dm->addInnerCompound(cd);
1416 }
1417 cd->setOuterScope(d);
1418
1419 // for inline namespace add an alias of the class to the outer scope
1421 {
1423 //printf("nd->isInline()=%d\n",nd->isInline());
1424 if (nd && nd->isInline())
1425 {
1426 d = d->getOuterScope();
1427 if (d)
1428 {
1429 dm = toDefinitionMutable(d);
1430 if (dm)
1431 {
1432 auto aliasCd = createClassDefAlias(d,cd);
1433 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1434 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1435 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1436 }
1437 }
1438 }
1439 else
1440 {
1441 break;
1442 }
1443 }
1444
1445 visitedClasses.insert(icd.get());
1446 done=false;
1447 }
1448 //else
1449 //{
1450 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1451 //}
1452 }
1453 }
1454 // add aliases
1455 for (auto &alias : aliases)
1456 {
1457 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1458 if (aliasCd)
1459 {
1460 alias.aliasContext->addInnerCompound(aliasCd);
1461 }
1462 }
1463 }
1464
1465 //give warnings for unresolved compounds
1466 for (const auto &icd : *Doxygen::classLinkedMap)
1467 {
1468 ClassDefMutable *cd = toClassDefMutable(icd.get());
1469 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1470 {
1472 /// create the scope artificially
1473 // anyway, so we can at least relate scopes properly.
1474 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1475 if (d && d!=cd && !cd->getDefFileName().empty())
1476 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1477 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1478 // also avoid warning for stuff imported via a tagfile.
1479 {
1481 if (dm)
1482 {
1483 dm->addInnerCompound(cd);
1484 }
1485 cd->setOuterScope(d);
1486 warn(cd->getDefFileName(),cd->getDefLine(),
1487 "Incomplete input: scope for class {} not found!{}",name,
1488 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1489 );
1490 }
1491 }
1492 }
1493}
1494
1496{
1497 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1498 //if (!inlineGroupedClasses) return;
1499 //printf("** distributeClassGroupRelations()\n");
1500
1501 ClassDefSet visitedClasses;
1502 for (const auto &cd : *Doxygen::classLinkedMap)
1503 {
1504 //printf("Checking %s\n",qPrint(cd->name()));
1505 // distribute the group to nested classes as well
1506 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1507 {
1508 //printf(" Candidate for merging\n");
1509 GroupDef *gd = cd->partOfGroups().front();
1510 for (auto &ncd : cd->getClasses())
1511 {
1513 if (ncdm && ncdm->partOfGroups().empty())
1514 {
1515 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1516 // gd->groupTitle());
1517 ncdm->makePartOfGroup(gd);
1518 gd->addClass(ncdm);
1519 }
1520 }
1521 visitedClasses.insert(cd.get()); // only visit every class once
1522 }
1523 }
1524}
1525
1526//----------------------------------------------------------------------
1527
1528template<typename Container>
1530 const Container *cd,
1531 const MemberDef *enumTypeMember,
1532 MemberListType mlFilter)
1533{
1534 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1535 {
1536 MemberList *eiml = cd->getMemberList(mlFilter);
1537 if (eiml)
1538 {
1539 for (const auto &eimd : *eiml)
1540 {
1541 DString vtype = eimd->typeString();
1542 if (vtype.find(md->name())!=DString::npos)
1543 {
1545 if (mimd)
1546 {
1547 mimd->setAnonymousEnumType(enumTypeMember);
1548 break;
1549 }
1550 }
1551 }
1552 }
1553 }
1554}
1555
1556static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName)
1557{
1558 DString n = templ->name();
1559 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1560 if (size_t sn = n.find('@'); sn!=DString::npos)
1561 {
1562 const char *p = n.data()+sn;
1563 char c;
1564 while ((c=*p))
1565 {
1566 if (!isdigit(c) && c!='@' && c!=':') break;
1567 p++;
1568 }
1569 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1570 }
1571 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1572 DString fullName = n+"."+fieldName;
1573
1574 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1576 Doxygen::classLinkedMap->add(fullName,
1578 templ->getDefLine(),
1579 templ->getDefColumn(),
1580 fullName,
1581 templ->compoundType())));
1582 if (cd)
1583 {
1584 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1585 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1586 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1587 cd->setLanguage(templ->getLanguage());
1588 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1589 cd->setBodyDef(templ->getBodyDef());
1590
1591 if (root!=Doxygen::globalScope)
1592 {
1593 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1594 if (root && root->definitionType()==Definition::TypeFile)
1595 {
1596 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1597 fd->insertClass(cd);
1598 cd->setFileDef(fd);
1600 }
1601 else if (outerScope)
1602 {
1603 outerScope->addInnerCompound(cd);
1604 cd->setOuterScope(const_cast<Definition*>(root));
1605 }
1606 }
1607
1608 for (auto &gd : root->partOfGroups())
1609 {
1610 cd->makePartOfGroup(gd);
1611 gd->addClass(cd);
1612 }
1613
1614 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1615 {
1616 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1617 md->typeString(),md->name(),md->argsString(),md->excpString(),
1618 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1619 md->memberType(),
1620 ArgumentList(),ArgumentList(),"");
1621 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1622 imd->setMemberClass(cd);
1623 imd->setDefinition(md->definition());
1624 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1625 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1626 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1627 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1628 imd->setId(md->id());
1629 imd->addQualifiers(md->getQualifiers());
1630 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1631 imd->setMemberGroupId(md->getMemberGroupId());
1632 imd->setInitializer(md->initializer());
1633 imd->setRequiresClause(md->requiresClause());
1634 imd->setMaxInitLines(md->initializerLines());
1635 imd->setBitfields(md->bitfieldString());
1636 imd->setLanguage(md->getLanguage());
1638 cd->insertMember(imd);
1639 associateVariableWithAnonymousEnumType(md,cd,imd,MemberListType::PubAttribs());
1640 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1641 mn->push_back(std::move(newMd));
1642 return imd;
1643
1644 };
1645
1646 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1647 if (ml)
1648 {
1649 for (const auto &md : *ml)
1650 {
1651 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1652 addMember(md);
1653 }
1654 }
1655 ml = templ->getMemberList(MemberListType::PubTypes());
1656 if (ml)
1657 {
1658 for (const auto &md : *ml)
1659 {
1660 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1661 MemberDefMutable *mdm = addMember(md);
1662 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1663 {
1664 for (const auto &emd : md->enumFieldList())
1665 {
1666 //printf(" enum field %s\n",qPrint(emd->name()));
1667 MemberDefMutable *emdm = addMember(emd);
1668 mdm->insertEnumField(emdm);
1669 emdm->setEnumScope(md);
1670 }
1671 }
1672 }
1673 }
1674 }
1675 return cd;
1676}
1677
1678/** Look through the members of class \a cd and its public members.
1679 * If there is a member m of a tag less struct/union,
1680 * then we create a duplicate of the struct/union with the name of the
1681 * member to identify it.
1682 * So if cd has name S, then the tag less struct/union will get name S.m
1683 * Since tag less structs can be nested we need to call this function
1684 * recursively. Later on we need to patch the member types so we keep
1685 * track of the hierarchy of classes we create.
1686 */
1687template<typename Container, typename TagContainer>
1688static void processTagLessClasses(const Definition *root,
1689 const Container *cd,
1690 const TagContainer *tagParent,
1691 MemberListType varFilter,
1692 MemberListType typeFilter,
1693 const DString &prefix,int count)
1694{
1695 AUTO_TRACE("count={} name={}\n",count,cd->name());
1696 if (tagParent /*&& !cd->getClasses().empty()*/)
1697 {
1698 MemberList *ml = cd->getMemberList(varFilter);
1699 if (ml)
1700 {
1701 int pos=0;
1702 for (const auto &md : *ml)
1703 {
1704 DString type = md->typeString();
1705 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1706 if ((cd->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // part namespace members only if cd is a namespace
1707 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1708 {
1709 std::vector<const ClassDef *> candidates;
1710 for (const auto &icd : cd->getClasses())
1711 {
1712 candidates.push_back(icd);
1713 }
1714 for (const auto &icd : candidates)
1715 {
1716 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1717 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1718 {
1719 DString name = md->name();
1720 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__";
1721 if (!prefix.empty()) name.prepend(prefix+".");
1722 //printf(" found %s in scope %s\n",qPrint(name),qPrint(cd->name()));
1723 ClassDefMutable *ncd = createTagLessInstance(root,icd,name);
1724 if (ncd)
1725 {
1726 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1727 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1728 ncd->setTagLessReference(icd);
1729
1730 // associate the variable of the anonymous type with the type member
1731 MemberList *pml = tagParent->getMemberList(varFilter);
1732 if (pml)
1733 {
1734 for (const auto &pmd : *pml)
1735 {
1737 if (pmdm && pmd->name()==md->name())
1738 {
1739 pmdm->setClassDefOfAnonymousType(ncd);
1740 }
1741 }
1742 }
1743 }
1744 }
1745 else
1746 {
1747 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1748 }
1749 }
1750 }
1751 }
1752 }
1753 // associate the variable of the anonymous enum type with the type member
1754 ml = cd->getMemberList(typeFilter);
1755 if (ml)
1756 {
1757 for (const auto &md : *ml)
1758 {
1759 MemberListType mlFilter = cd->definitionType()==Definition::TypeClass ? MemberListType::PubAttribs() : MemberListType::DecVarMembers();
1760 associateVariableWithAnonymousEnumType(md,cd,md,mlFilter);
1761 }
1762 }
1763 }
1764}
1765
1766template<typename Container>
1767static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1768{
1769 for (const auto &icd : cd->getClasses())
1770 {
1771 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1772 {
1773 findTagLessClasses(candidates,icd);
1774 }
1775 }
1776
1777 candidates.insert(cd);
1778}
1779
1781{
1782 std::set<const Definition *> candidates;
1783 for (auto &cd : *Doxygen::classLinkedMap)
1784 {
1785 Definition *scope = cd->getOuterScope();
1786 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1787 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1788 {
1789 const NamespaceDef *nd = toNamespaceDef(scope);
1790 if (nd && nd==Doxygen::globalScope) // class at global namespace
1791 {
1792 const FileDef *fd = cd->getFileDef();
1793 if (fd)
1794 {
1795 findTagLessClasses(candidates,fd);
1796 }
1797 }
1798 else if (nd) // class in a namespace
1799 {
1800 findTagLessClasses(candidates,nd);
1801 }
1802 }
1803 }
1804
1805 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1806 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1807 for (const auto &d : candidates)
1808 {
1809 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1810 if (d->definitionType()==Definition::TypeNamespace)
1811 {
1812 const NamespaceDef *nd = toNamespaceDef(d);
1813 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1814 }
1815 else if (d->definitionType()==Definition::TypeFile)
1816 {
1817 const FileDef *fd = toFileDef(d);
1818 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1819 }
1820 else if (d->definitionType()==Definition::TypeClass)
1821 {
1822 const ClassDef *cd = toClassDef(d);
1823 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1824 }
1825 }
1826}
1827
1828
1829//----------------------------------------------------------------------
1830// build a list of all namespaces mentioned in the documentation
1831// and all namespaces that have a documentation block before their definition.
1832static void buildNamespaceList(const Entry *root)
1833{
1834 if (
1835 (root->section.isNamespace() ||
1836 root->section.isNamespaceDoc() ||
1837 root->section.isPackageDoc()
1838 ) &&
1839 !root->name.empty()
1840 )
1841 {
1842 AUTO_TRACE("name={}",root->name);
1843
1844 DString fName = root->name;
1845 if (root->section.isPackageDoc())
1846 {
1847 fName=substitute(fName,".","::");
1848 }
1849
1850 DString fullName = stripAnonymousNamespaceScope(fName);
1851 if (!fullName.empty())
1852 {
1853 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1855 if (ndi) // existing namespace
1856 {
1858 if (nd) // non-inline namespace
1859 {
1860 AUTO_TRACE_ADD("Existing namespace");
1861 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1862 nd->setName(fullName); // change name to match docs
1864 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1865 if (nd->getLanguage()==SrcLangExt::Unknown)
1866 {
1867 nd->setLanguage(root->lang);
1868 }
1869 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
1870 // if we previously found namespace nd in a tag file and now we find a
1871 // documented namespace with the same name in the project, then remove
1872 // the tag file reference
1873 {
1874 nd->setReference("");
1875 nd->setFileName(fullName);
1876 }
1877 nd->setMetaData(root->metaData);
1878
1879 // file definition containing the namespace nd
1880 FileDef *fd=root->fileDef();
1881 if (nd->isArtificial())
1882 {
1883 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
1884 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1885 }
1886 // insert the namespace in the file definition
1887 if (fd) fd->insertNamespace(nd);
1888 addNamespaceToGroups(root,nd);
1889 nd->setRefItems(root->sli);
1890 nd->setRequirementReferences(root->rqli);
1891 }
1892 }
1893 else // fresh namespace
1894 {
1895 DString tagName;
1896 DString tagFileName;
1897 const TagInfo *tagInfo = root->tagInfo();
1898 if (tagInfo)
1899 {
1900 tagName = tagInfo->tagName;
1901 tagFileName = tagInfo->fileName;
1902 }
1903 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1904 // add namespace to the list
1906 Doxygen::namespaceLinkedMap->add(fullName,
1907 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1908 root->startColumn,fullName,tagName,tagFileName,
1909 root->type,root->spec.isPublished())));
1910 if (nd)
1911 {
1912 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1913 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1915 nd->setHidden(root->hidden);
1916 nd->setArtificial(root->artificial);
1917 nd->setLanguage(root->lang);
1918 nd->setId(root->id);
1919 nd->setMetaData(root->metaData);
1920 nd->setInline(root->spec.isInline());
1921 nd->setExported(root->exported);
1922
1923 addNamespaceToGroups(root,nd);
1924 nd->setRefItems(root->sli);
1925 nd->setRequirementReferences(root->rqli);
1926
1927 // file definition containing the namespace nd
1928 FileDef *fd=root->fileDef();
1929 // insert the namespace in the file definition
1930 if (fd) fd->insertNamespace(nd);
1931
1932 // the empty string test is needed for extract all case
1933 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1934 nd->insertUsedFile(fd);
1935 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1936 nd->setBodyDef(fd);
1937
1938 // also add namespace to the correct structural context
1939 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1940 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
1941 if (d==nullptr) // we didn't find anything, create the scope artificially
1942 // anyway, so we can at least relate scopes properly.
1943 {
1944 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1946 if (dm)
1947 {
1948 dm->addInnerCompound(nd);
1949 }
1950 nd->setOuterScope(d);
1951 // TODO: Due to the order in which the tag file is written
1952 // a nested class can be found before its parent!
1953 }
1954 else
1955 {
1957 if (dm)
1958 {
1959 dm->addInnerCompound(nd);
1960 }
1961 nd->setOuterScope(d);
1962 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1964 {
1965 NamespaceDef *pnd = toNamespaceDef(d);
1966 if (pnd && pnd->isInline())
1967 {
1968 d = d->getOuterScope();
1969 if (d)
1970 {
1971 dm = toDefinitionMutable(d);
1972 if (dm)
1973 {
1974 auto aliasNd = createNamespaceDefAlias(d,nd);
1975 dm->addInnerCompound(aliasNd.get());
1976 DString aliasName = aliasNd->name();
1977 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1978 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1979 }
1980 }
1981 else
1982 {
1983 break;
1984 }
1985 }
1986 else
1987 {
1988 break;
1989 }
1990 }
1991 }
1992 }
1993 }
1994 }
1995 }
1996 for (const auto &e : root->children()) buildNamespaceList(e.get());
1997}
1998
1999//----------------------------------------------------------------------
2000
2002 const DString &name)
2003{
2004 NamespaceDef *usingNd =nullptr;
2005 for (auto &und : unl)
2006 {
2007 DString uScope=und->name()+"::";
2008 usingNd = getResolvedNamespace(uScope+name);
2009 if (usingNd!=nullptr) break;
2010 }
2011 return usingNd;
2012}
2013
2014static void findUsingDirectives(const Entry *root)
2015{
2016 if (root->section.isUsingDir())
2017 {
2018 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2019 DString name=substitute(root->name,".","::");
2020 if (name.endsWith("::"))
2021 {
2022 name=name.left(name.length()-2);
2023 }
2024 if (!name.empty())
2025 {
2026 NamespaceDef *usingNd = nullptr;
2027 NamespaceDefMutable *nd = nullptr;
2028 FileDef *fd = root->fileDef();
2029 DString nsName;
2030
2031 // see if the using statement was found inside a namespace or inside
2032 // the global file scope.
2033 if (root->parent() && root->parent()->section.isNamespace() &&
2034 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2035 )
2036 {
2037 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2038 if (!nsName.empty())
2039 {
2040 nd = getResolvedNamespaceMutable(nsName);
2041 }
2042 }
2043
2044 // find the scope in which the 'using' namespace is defined by prepending
2045 // the possible scopes in which the using statement was found, starting
2046 // with the most inner scope and going to the most outer scope (i.e.
2047 // file scope).
2048 int scopeOffset = static_cast<int>(nsName.length());
2049 do
2050 {
2051 DString scope=scopeOffset>0 ?
2052 nsName.left(scopeOffset)+"::" : DString();
2053 usingNd = getResolvedNamespace(scope+name);
2054 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2055 if (scopeOffset==0)
2056 {
2057 scopeOffset=-1;
2058 }
2059 else
2060 {
2061 size_t o = nsName.rfind("::",scopeOffset-1);
2062 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2063 }
2064 } while (scopeOffset>=0 && usingNd==nullptr);
2065
2066 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2067 // or in one of the parent namespace scopes
2068 {
2069 const NamespaceDefMutable *pnd = nd;
2070 while (pnd && usingNd==nullptr)
2071 {
2072 // also try with one of the used namespaces found earlier
2074
2075 // goto the parent
2076 Definition *s = pnd->getOuterScope();
2078 {
2080 }
2081 else
2082 {
2083 pnd = nullptr;
2084 }
2085 }
2086 }
2087 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2088 // global scope
2089 {
2090 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2091 }
2092
2093 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2094
2095 // add the namespace the correct scope
2096 if (usingNd)
2097 {
2098 //printf("using fd=%p nd=%p\n",fd,nd);
2099 if (nd)
2100 {
2101 //printf("Inside namespace %s\n",qPrint(nd->name()));
2102 nd->addUsingDirective(usingNd);
2103 }
2104 else if (fd)
2105 {
2106 //printf("Inside file %s\n",qPrint(fd->name()));
2107 fd->addUsingDirective(usingNd);
2108 }
2109 }
2110 else // unknown namespace, but add it anyway.
2111 {
2112 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
2113 // add namespace to the list
2116 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2117 if (nd)
2118 {
2119 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2120 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2122 nd->setHidden(root->hidden);
2123 nd->setArtificial(true);
2124 nd->setLanguage(root->lang);
2125 nd->setId(root->id);
2126 nd->setMetaData(root->metaData);
2127 nd->setInline(root->spec.isInline());
2128 nd->setExported(root->exported);
2129
2130 for (const Grouping &g : root->groups)
2131 {
2132 GroupDef *gd=nullptr;
2134 gd->addNamespace(nd);
2135 }
2136
2137 // insert the namespace in the file definition
2138 if (fd)
2139 {
2140 fd->insertNamespace(nd);
2141 fd->addUsingDirective(nd);
2142 }
2143
2144 // the empty string test is needed for extract all case
2145 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2146 nd->insertUsedFile(fd);
2147 nd->setRefItems(root->sli);
2148 nd->setRequirementReferences(root->rqli);
2149 }
2150 }
2151 }
2152 }
2153 for (const auto &e : root->children()) findUsingDirectives(e.get());
2154}
2155
2156//----------------------------------------------------------------------
2157
2158static void buildListOfUsingDecls(const Entry *root)
2159{
2160 if (root->section.isUsingDecl() &&
2161 !root->parent()->section.isCompound() // not a class/struct member
2162 )
2163 {
2164 DString name = substitute(root->name,".","::");
2165 g_usingDeclarations.insert(name.str());
2166 }
2167 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2168}
2169
2170
2171static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2172{
2173 if (root->section.isUsingDecl() &&
2174 !root->parent()->section.isCompound() && // not a class/struct member
2175 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2176 )
2177 {
2178 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2179 root->name,root->startLine,root->fileName,root->parent()->section);
2180 if (!root->name.empty())
2181 {
2182 const Definition *usingDef = nullptr;
2183 NamespaceDefMutable *nd = nullptr;
2184 FileDef *fd = root->fileDef();
2185 DString scName;
2186
2187 // see if the using statement was found inside a namespace or inside
2188 // the global file scope.
2189 if (root->parent()->section.isNamespace())
2190 {
2191 scName=root->parent()->name;
2192 if (!scName.empty())
2193 {
2194 nd = getResolvedNamespaceMutable(scName);
2195 }
2196 }
2197
2198 // Assume the using statement was used to import a class.
2199 // Find the scope in which the 'using' namespace is defined by prepending
2200 // the possible scopes in which the using statement was found, starting
2201 // with the most inner scope and going to the most outer scope (i.e.
2202 // file scope).
2203
2204 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2205
2206 SymbolResolver resolver;
2207 const Definition *scope = nd;
2208 if (nd==nullptr) scope = fd;
2209 usingDef = resolver.resolveSymbol(scope,name);
2210
2211 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2212
2213 if (!usingDef)
2214 {
2215 usingDef = getClass(name); // try direct lookup, this is needed to get
2216 // builtin STL classes to properly resolve, e.g.
2217 // vector -> std::vector
2218 }
2219 if (!usingDef)
2220 {
2221 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2222 }
2223#if 0
2224 if (!usingDef)
2225 {
2226 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2227 name,root->section,root->tArgLists.size());
2230 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2231 if (usingCd)
2232 {
2233 usingCd->setArtificial(true);
2234 usingCd->setLanguage(root->lang);
2235 usingDef = usingCd;
2236 }
2237 }
2238#endif
2239 else
2240 {
2241 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2242 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2243 }
2244
2245 if (usingDef)
2246 {
2247 if (nd)
2248 {
2249 nd->addUsingDeclaration(usingDef);
2250 }
2251 else if (fd)
2252 {
2253 fd->addUsingDeclaration(usingDef);
2254 }
2255 }
2256 }
2257 }
2258 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2259}
2260
2261//----------------------------------------------------------------------
2262
2264{
2265 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2266 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2267 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2268 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2269 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2270 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2271}
2272
2273//----------------------------------------------------------------------
2274
2276 const DString &fileName,const DString &memName)
2277{
2278 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2279 const ArgumentList &templAl = md->templateArguments();
2280 const ArgumentList &al = md->argumentList();
2281 auto newMd = createMemberDef(
2282 fileName,root->startLine,root->startColumn,
2283 md->typeString(),memName,md->argsString(),
2284 md->excpString(),root->protection,root->virt,
2285 md->isStatic(),Relationship::Member,md->memberType(),
2286 templAl,al,root->metaData
2287 );
2288 auto newMmd = toMemberDefMutable(newMd.get());
2289 newMmd->setMemberClass(cd);
2290 cd->insertMember(newMd.get());
2291 if (!root->doc.empty() || !root->brief.empty())
2292 {
2293 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2294 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2295 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2296 }
2297 else
2298 {
2299 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2300 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2301 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2302 }
2303 newMmd->setDefinition(md->definition());
2304 applyMemberOverrideOptions(root,newMmd);
2305 newMmd->addQualifiers(root->qualifiers);
2306 newMmd->setBitfields(md->bitfieldString());
2307 newMmd->addSectionsToDefinition(root->anchors);
2308 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2309 newMmd->setBodyDef(md->getBodyDef());
2310 newMmd->setInitializer(md->initializer());
2311 newMmd->setRequiresClause(md->requiresClause());
2312 newMmd->setMaxInitLines(md->initializerLines());
2313 newMmd->setMemberGroupId(root->mGrpId);
2314 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2315 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2316 newMmd->setLanguage(root->lang);
2317 newMmd->setId(root->id);
2319 mn->push_back(std::move(newMd));
2320}
2321
2322static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2323
2324static void findUsingDeclImports(const Entry *root)
2325{
2326 if (root->section.isUsingDecl() &&
2327 root->parent()->section.isCompound() // in a class/struct member
2328 )
2329 {
2330 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2331 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2332 fullName=stripAnonymousNamespaceScope(fullName);
2333 fullName=stripTemplateSpecifiersFromScope(fullName);
2334 ClassDefMutable *cd = getClassMutable(fullName);
2335 if (cd)
2336 {
2337 AUTO_TRACE_ADD("found class '{}'",cd->name());
2338 size_t i=root->name.rfind("::");
2339 if (i!=DString::npos)
2340 {
2341 DString scope=root->name.left(i);
2342 DString memName=root->name.mid(i+2);
2343 SymbolResolver resolver;
2344 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2345 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2346 if (bcd && bcd!=cd)
2347 {
2348 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2350 const MemberNameInfo *mni = mnlm.find(memName);
2351 if (mni)
2352 {
2353 for (auto &mi : *mni)
2354 {
2355 const MemberDef *md = mi->memberDef();
2356 if (md && md->protection()!=Protection::Private)
2357 {
2358 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2359 DString fileName = root->fileName;
2360 if (fileName.empty() && root->tagInfo())
2361 {
2362 fileName = root->tagInfo()->tagName;
2363 }
2364 if (!cd->containsOverload(md))
2365 {
2366 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2367 // also insert the member into copies of the class
2368 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2369 if (it != g_usingClassMap.end())
2370 {
2371 for (const auto &copyCd : it->second)
2372 {
2373 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2374 }
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381 }
2382 }
2383 }
2384 else if (root->section.isUsingDecl() &&
2385 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2386 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2387 )
2388 {
2389 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2390 Definition *scope = nullptr;
2391 NamespaceDefMutable *nd = nullptr;
2392 FileDef *fd = root->parent()->fileDef();
2393 if (!root->parent()->name.empty())
2394 {
2395 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2396 fullName=stripAnonymousNamespaceScope(fullName);
2398 scope = nd;
2399 }
2400 else
2401 {
2402 scope = fd;
2403 }
2404 if (scope)
2405 {
2406 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2407 SymbolResolver resolver;
2408 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2409 if (def && def->definitionType()==Definition::TypeMember)
2410 {
2411 size_t i=root->name.rfind("::");
2412 DString memName;
2413 if (i!=DString::npos)
2414 {
2415 memName = root->name.right(root->name.length()-i-2);
2416 }
2417 else
2418 {
2419 memName = root->name;
2420 }
2421 const MemberDef *md = toMemberDef(def);
2422 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2423 DString fileName = root->fileName;
2424 if (fileName.empty() && root->tagInfo())
2425 {
2426 fileName = root->tagInfo()->tagName;
2427 }
2428 const ArgumentList &templAl = md->templateArguments();
2429 const ArgumentList &al = md->argumentList();
2430
2431 auto newMd = createMemberDef(
2432 fileName,root->startLine,root->startColumn,
2433 md->typeString(),memName,md->argsString(),
2434 md->excpString(),root->protection,root->virt,
2435 md->isStatic(),Relationship::Member,md->memberType(),
2436 templAl,al,root->metaData
2437 );
2438 auto newMmd = toMemberDefMutable(newMd.get());
2439 if (nd)
2440 {
2441 newMmd->setNamespace(nd);
2442 nd->insertMember(newMd.get());
2443 }
2444 if (fd)
2445 {
2446 newMmd->setFileDef(fd);
2447 fd->insertMember(newMd.get());
2448 }
2449 if (!root->doc.empty() || !root->brief.empty())
2450 {
2451 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2452 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2453 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2454 }
2455 else
2456 {
2457 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2458 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2459 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2460 }
2461 newMmd->setDefinition(md->definition());
2462 applyMemberOverrideOptions(root,newMmd);
2463 newMmd->addQualifiers(root->qualifiers);
2464 newMmd->setBitfields(md->bitfieldString());
2465 newMmd->addSectionsToDefinition(root->anchors);
2466 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2467 newMmd->setBodyDef(md->getBodyDef());
2468 newMmd->setInitializer(md->initializer());
2469 newMmd->setRequiresClause(md->requiresClause());
2470 newMmd->setMaxInitLines(md->initializerLines());
2471 newMmd->setMemberGroupId(root->mGrpId);
2472 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2473 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2474 newMmd->setLanguage(root->lang);
2475 newMmd->setId(root->id);
2477 mn->push_back(std::move(newMd));
2478#if 0 // insert an alias instead of a copy
2479 const MemberDef *md = toMemberDef(def);
2480 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2481 auto aliasMd = createMemberDefAlias(nd,md);
2482 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2483 if (nd && aliasMd.get())
2484 {
2485 nd->insertMember(aliasMd.get());
2486 }
2487 if (fd && aliasMd.get())
2488 {
2489 fd->insertMember(aliasMd.get());
2490 }
2491 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2492 mn->push_back(std::move(aliasMd));
2493#endif
2494 }
2495 else if (def && def->definitionType()==Definition::TypeClass)
2496 {
2497 const ClassDef *cd = toClassDef(def);
2498 DString copyFullName;
2499 if (nd==nullptr)
2500 {
2501 copyFullName = cd->localName();
2502 }
2503 else
2504 {
2505 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2506 }
2507 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2508 {
2510 Doxygen::classLinkedMap->add(copyFullName,
2511 cd->deepCopy(copyFullName)));
2512 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2513 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2514 if (ncdm)
2515 {
2516 if (nd) ncdm->moveTo(nd);
2517 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2518 {
2519 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2520 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2521 }
2522 else // use docs from used class
2523 {
2524 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2526 }
2527 if (nd)
2528 {
2529 nd->addInnerCompound(ncdm);
2530 nd->addUsingDeclaration(ncdm);
2531 }
2532 if (fd)
2533 {
2534 if (ncdm) ncdm->setFileDef(fd);
2535 fd->insertClass(ncdm);
2536 fd->addUsingDeclaration(ncdm);
2537 }
2538 }
2539 }
2540#if 0 // insert an alias instead of a copy
2541 auto aliasCd = createClassDefAlias(nd,cd);
2542 DString aliasFullName;
2543 if (nd==nullptr)
2544 {
2545 aliasFullName = aliasCd->localName();
2546 }
2547 else
2548 {
2549 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2550 }
2551 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2552 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2553 if (nd && acd)
2554 {
2555 nd->addInnerCompound(acd);
2556 }
2557 if (fd && acd)
2558 {
2559 fd->insertClass(acd);
2560 }
2561#endif
2562 }
2563 else if (scope)
2564 {
2565 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2566 }
2567 }
2568 }
2569 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2570}
2571
2572//----------------------------------------------------------------------
2573
2575{
2576 FileDefSet visitedFiles;
2577 // then recursively add using directives found in #include files
2578 // to files that have not been visited.
2579 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2580 {
2581 for (const auto &fd : *fn)
2582 {
2583 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2584 fd->addIncludedUsingDirectives(visitedFiles);
2585 }
2586 }
2587}
2588
2589//----------------------------------------------------------------------
2590
2592 const Entry *root,
2593 ClassDefMutable *cd,
2594 MemberType mtype,
2595 const DString &type,
2596 const DString &name,
2597 const DString &args,
2598 bool fromAnnScope,
2599 MemberDef *fromAnnMemb,
2600 Protection prot,
2601 Relationship related)
2602{
2604 DString scopeSeparator="::";
2605 SrcLangExt lang = cd->getLanguage();
2606 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2607 {
2608 qualScope = substitute(qualScope,"::",".");
2609 scopeSeparator=".";
2610 }
2611 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2612 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2613
2614 DString def;
2615 if (!type.empty())
2616 {
2617 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2618 {
2619 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2620 {
2621 if (lang==SrcLangExt::Python)
2622 {
2623 def="type "+name+args;
2624 }
2625 else
2626 {
2627 def="using "+name;
2628 }
2629 }
2630 else
2631 {
2632 def=type+" "+name+args;
2633 }
2634 }
2635 else
2636 {
2637 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2638 {
2639 if (lang==SrcLangExt::Python)
2640 {
2641 def="type "+qualScope+scopeSeparator+name+args;
2642 }
2643 else
2644 {
2645 def="using "+qualScope+scopeSeparator+name;
2646 }
2647 }
2648 else
2649 {
2650 def=type+" "+qualScope+scopeSeparator+name+args;
2651 }
2652 }
2653 }
2654 else
2655 {
2656 if (Config_getBool(HIDE_SCOPE_NAMES))
2657 {
2658 def=name+args;
2659 }
2660 else
2661 {
2662 def=qualScope+scopeSeparator+name+args;
2663 }
2664 }
2665 def.stripPrefix("static ");
2666
2667 // see if the member is already found in the same scope
2668 // (this may be the case for a static member that is initialized
2669 // outside the class)
2671 if (mn)
2672 {
2673 for (const auto &imd : *mn)
2674 {
2675 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2676 // md->getClassDef(),cd,qPrint(type),md->typeString());
2677 MemberDefMutable *md = toMemberDefMutable(imd.get());
2678 if (md &&
2679 md->getClassDef()==cd &&
2680 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2682 // member already in the scope
2683 {
2684
2685 if (root->lang==SrcLangExt::ObjC &&
2686 root->mtype==MethodTypes::Property &&
2687 md->memberType()==MemberType::Variable)
2688 { // Objective-C 2.0 property
2689 // turn variable into a property
2690 md->setProtection(root->protection);
2691 cd->reclassifyMember(md,MemberType::Property);
2692 }
2693 addMemberDocs(root,md,def,nullptr,false,root->spec);
2694 AUTO_TRACE_ADD("Member already found!");
2695 return md;
2696 }
2697 }
2698 }
2699
2700 DString fileName = root->fileName;
2701 if (fileName.empty() && root->tagInfo())
2702 {
2703 fileName = root->tagInfo()->tagName;
2704 }
2705
2706 // new member variable, typedef or enum value
2707 auto md = createMemberDef(
2708 fileName,root->startLine,root->startColumn,
2709 type,name,args,root->exception,
2710 prot,Specifier::Normal,root->isStatic,related,
2711 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2712 ArgumentList(), root->metaData);
2713 auto mmd = toMemberDefMutable(md.get());
2714 mmd->setTagInfo(root->tagInfo());
2715 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2716 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2717 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2718 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2719 mmd->setDefinition(def);
2720 mmd->setBitfields(root->bitfields);
2721 mmd->addSectionsToDefinition(root->anchors);
2722 mmd->setFromAnonymousScope(fromAnnScope);
2723 mmd->setFromAnonymousMember(fromAnnMemb);
2724 if (fromAnnMemb)
2725 {
2726 MemberDefMutable *fromAnnMmd = toMemberDefMutable(fromAnnMemb);
2727 if (fromAnnMmd)
2728 {
2729 fromAnnMmd->setToAnonymousMember(mmd);
2730 }
2731 }
2732 //md->setIndentDepth(indentDepth);
2733 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2734 mmd->setInitializer(root->initializer.str());
2735 mmd->setMaxInitLines(root->initLines);
2736 mmd->setMemberGroupId(root->mGrpId);
2737 mmd->setMemberSpecifiers(root->spec);
2738 mmd->setVhdlSpecifiers(root->vhdlSpec);
2739 mmd->setReadAccessor(root->read);
2740 mmd->setWriteAccessor(root->write);
2742 mmd->setHidden(root->hidden);
2743 mmd->setArtificial(root->artificial);
2744 mmd->setLanguage(root->lang);
2745 mmd->setId(root->id);
2746 addMemberToGroups(root,md.get());
2748 mmd->setBodyDef(root->fileDef());
2749 mmd->addQualifiers(root->qualifiers);
2750
2751 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2752 cd->insertMember(md.get());
2753 mmd->setRefItems(root->sli);
2754 mmd->setRequirementReferences(root->rqli);
2755
2756 cd->insertUsedFile(root->fileDef());
2757 root->markAsProcessed();
2758
2759 if (mtype==MemberType::Typedef)
2760 {
2761 resolveTemplateInstanceInType(root,cd,md.get());
2762 }
2763
2764 // add the member to the global list
2765 MemberDef *result = md.get();
2767 mn->push_back(std::move(md));
2768
2769 return result;
2770}
2771
2772//----------------------------------------------------------------------
2773
2775 const Entry *root,
2776 MemberType mtype,
2777 const DString &scope,
2778 const DString &type,
2779 const DString &name,
2780 const DString &args,
2781 bool fromAnnScope,
2782 MemberDef *fromAnnMemb)
2783{
2784 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2785 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2786
2787 FileDef *fd = root->fileDef();
2788
2789 // see if we have a typedef that should hide a struct or union
2790 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2791 {
2792 DString ttype = type;
2793 ttype.stripPrefix("typedef ");
2794 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2795 {
2796 static const reg::Ex re(R"(\a\w*)");
2797 reg::Match match;
2798 const std::string &typ = ttype.str();
2799 if (reg::search(typ,match,re))
2800 {
2801 DString typeValue = match.str();
2802 ClassDefMutable *cd = getClassMutable(typeValue);
2803 if (cd)
2804 {
2805 // this typedef should hide compound name cd, so we
2806 // change the name that is displayed from cd.
2807 cd->setClassName(name);
2808 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2809 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2810 return nullptr;
2811 }
2812 }
2813 }
2814 }
2815
2816 // see if the function is inside a namespace
2817 NamespaceDefMutable *nd = nullptr;
2818 if (!scope.empty())
2819 {
2820 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2821 nd = getResolvedNamespaceMutable(scope);
2822 }
2823 DString def;
2824
2825 // determine the definition of the global variable
2826 if (nd && !nd->isAnonymous() &&
2827 !Config_getBool(HIDE_SCOPE_NAMES)
2828 )
2829 // variable is inside a namespace, so put the scope before the name
2830 {
2831 SrcLangExt lang = nd->getLanguage();
2833
2834 if (!type.empty())
2835 {
2836 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2837 {
2838 if (lang==SrcLangExt::Python)
2839 {
2840 def="type "+nd->name()+sep+name+args;
2841 }
2842 else
2843 {
2844 def="using "+nd->name()+sep+name;
2845 }
2846 }
2847 else // normal member
2848 {
2849 def=type+" "+nd->name()+sep+name+args;
2850 }
2851 }
2852 else
2853 {
2854 def=nd->name()+sep+name+args;
2855 }
2856 }
2857 else
2858 {
2859 if (!type.empty() && !root->name.empty())
2860 {
2861 if (name.at(0)=='@') // dummy variable representing anonymous union
2862 {
2863 def=type;
2864 }
2865 else
2866 {
2867 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2868 {
2869 if (root->lang==SrcLangExt::Python)
2870 {
2871 def="type "+root->name+args;
2872 }
2873 else
2874 {
2875 def="using "+root->name;
2876 }
2877 }
2878 else // normal member
2879 {
2880 def=type+" "+name+args;
2881 }
2882 }
2883 }
2884 else
2885 {
2886 def=name+args;
2887 }
2888 }
2889 def.stripPrefix("static ");
2890
2892 if (mn)
2893 {
2894 //DString nscope=removeAnonymousScopes(scope);
2895 //NamespaceDef *nd=nullptr;
2896 //if (!nscope.empty())
2897 if (!scope.empty())
2898 {
2899 nd = getResolvedNamespaceMutable(scope);
2900 }
2901 for (const auto &imd : *mn)
2902 {
2903 MemberDefMutable *md = toMemberDefMutable(imd.get());
2904 if (md &&
2905 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2906 root->fileName==md->getFileDef()->absFilePath()
2907 ) // both variable names in the same file
2908 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2909 )
2910 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2911 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2912 )
2913 // variable already in the scope
2914 {
2915 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2916 md->argsString()!=args &&
2917 args.find('[')!=DString::npos;
2918 bool staticsInDifferentFiles =
2919 root->isStatic && md->isStatic() &&
2920 root->fileName!=md->getDefFileName();
2921
2922 if (md->getFileDef() &&
2923 !isPHPArray && // not a php array
2924 !staticsInDifferentFiles
2925 )
2926 // not a php array variable
2927 {
2928 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2929 addMemberDocs(root,md,def,nullptr,false,root->spec);
2930 md->setRefItems(root->sli);
2931 md->setRequirementReferences(root->rqli);
2932 // if md is a variable forward declaration and root is the definition that
2933 // turn md into the definition
2934 if (!root->explicitExternal && md->isExternal())
2935 {
2936 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2937 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
2938 }
2939 // if md is the definition and root point at a declaration, then add the
2940 // declaration info
2941 else if (root->explicitExternal && !md->isExternal())
2942 {
2943 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2944 }
2945 return md;
2946 }
2947 }
2948 }
2949 }
2950
2951 DString fileName = root->fileName;
2952 if (fileName.empty() && root->tagInfo())
2953 {
2954 fileName = root->tagInfo()->tagName;
2955 }
2956
2957 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
2958 // new global variable, enum value or typedef
2959 auto md = createMemberDef(
2960 fileName,root->startLine,root->startColumn,
2961 type,name,args,DString(),
2962 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2963 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2964 root->argList, root->metaData);
2965 auto mmd = toMemberDefMutable(md.get());
2966 mmd->setTagInfo(root->tagInfo());
2967 mmd->setMemberSpecifiers(root->spec);
2968 mmd->setVhdlSpecifiers(root->vhdlSpec);
2969 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2970 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2971 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2972 mmd->addSectionsToDefinition(root->anchors);
2973 mmd->setFromAnonymousScope(fromAnnScope);
2974 mmd->setFromAnonymousMember(fromAnnMemb);
2975 if (fromAnnMemb)
2976 {
2977 MemberDefMutable *fromAnnMmd = toMemberDefMutable(fromAnnMemb);
2978 if (fromAnnMmd)
2979 {
2980 fromAnnMmd->setToAnonymousMember(mmd);
2981 }
2982 }
2983 mmd->setInitializer(root->initializer.str());
2984 mmd->setMaxInitLines(root->initLines);
2985 mmd->setMemberGroupId(root->mGrpId);
2986 mmd->setDefinition(def);
2987 mmd->setLanguage(root->lang);
2988 mmd->setId(root->id);
2990 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2991 mmd->addQualifiers(root->qualifiers);
2992 //md->setOuterScope(fd);
2993 if (!root->explicitExternal)
2994 {
2995 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2996 mmd->setBodyDef(fd);
2997 }
2998 addMemberToGroups(root,md.get());
3000
3001 mmd->setRefItems(root->sli);
3002 mmd->setRequirementReferences(root->rqli);
3003 if (nd && !nd->isAnonymous())
3004 {
3005 mmd->setNamespace(nd);
3006 nd->insertMember(md.get());
3007 }
3008
3009 // add member to the file (we do this even if we have already inserted
3010 // it into the namespace.
3011 if (fd)
3012 {
3013 mmd->setFileDef(fd);
3014 fd->insertMember(md.get());
3015 }
3016
3017 root->markAsProcessed();
3018
3019 if (mtype==MemberType::Typedef)
3020 {
3021 resolveTemplateInstanceInType(root,nd,md.get());
3022 }
3023
3024 // add member definition to the list of globals
3025 MemberDef *result = md.get();
3027 mn->push_back(std::move(md));
3028
3029
3030
3031 return result;
3032}
3033
3034/*! See if the return type string \a type is that of a function pointer
3035 * \returns -1 if this is not a function pointer variable or
3036 * the index at which the closing brace of (...*name) was found.
3037 */
3038static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3039{
3040 AUTO_TRACE("type='{}' lang={}",type,lang);
3041 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3042 {
3043 return -1; // Fortran and VHDL do not have function pointers
3044 }
3045
3046 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3047 reg::Match match;
3048 size_t i=std::string::npos;
3049 size_t l=0;
3050 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3051 {
3052 i = match.position();
3053 l = match.length();
3054 }
3055 if (i!=std::string::npos)
3056 {
3057 size_t di = type.find("decltype(");
3058 if (di!=std::string::npos && di<i)
3059 {
3060 i = std::string::npos;
3061 }
3062 }
3063 size_t bb=type.find('<');
3064 size_t be=type.rfind('>');
3065 bool templFp = false;
3066 if (be!=std::string::npos) {
3067 size_t cc_ast = type.find("::*");
3068 size_t cc_amp = type.find("::&");
3069 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>::*)'
3070 }
3071
3072 if (!type.empty() && // return type is non-empty
3073 i!=std::string::npos && // contains (...*...)
3074 type.find("operator")==std::string::npos && // not an operator
3075 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3076 // not a function pointer return type
3077 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
3078 )
3079 {
3080 if (pLength) *pLength=static_cast<int>(l);
3081 //printf("findFunctionPtr=%d\n",(int)i);
3082 AUTO_TRACE_EXIT("result={}",i);
3083 return static_cast<int>(i);
3084 }
3085 else
3086 {
3087 //printf("findFunctionPtr=%d\n",-1);
3088 AUTO_TRACE_EXIT("result=-1");
3089 return -1;
3090 }
3091}
3092
3093//--------------------------------------------------------------------------------------
3094
3095/*! Returns true iff \a type is a class within scope \a context.
3096 * Used to detect variable declarations that look like function prototypes.
3097 */
3098static bool isVarWithConstructor(const Entry *root)
3099{
3100 bool result = false;
3101 bool typeIsClass = false;
3102 bool typePtrType = false;
3103 DString type;
3104 Definition *ctx = nullptr;
3105 FileDef *fd = root->fileDef();
3106 SymbolResolver resolver(fd);
3107
3108 AUTO_TRACE("isVarWithConstructor({})",root->name);
3109 if (root->parent()->section.isCompound())
3110 { // inside a class
3111 result=false;
3112 AUTO_TRACE_EXIT("inside class: result={}",result);
3113 return result;
3114 }
3115 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3116 { // inside a .c file
3117 result=false;
3118 AUTO_TRACE_EXIT("inside C file: result={}",result);
3119 return result;
3120 }
3121 if (root->type.empty())
3122 {
3123 result=false;
3124 AUTO_TRACE_EXIT("no type: result={}",result);
3125 return result;
3126 }
3127 if (!root->parent()->name.empty())
3128 {
3130 }
3131 type = root->type;
3132 // remove qualifiers
3133 findAndRemoveWord(type,"const");
3134 findAndRemoveWord(type,"static");
3135 findAndRemoveWord(type,"volatile");
3136 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3137 if (!typePtrType)
3138 {
3139 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3140 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3141 {
3142 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3143 }
3144 }
3145 if (typeIsClass) // now we still have to check if the arguments are
3146 // types or values. Since we do not have complete type info
3147 // we need to rely on heuristics :-(
3148 {
3149 if (root->argList.empty())
3150 {
3151 result=false; // empty arg list -> function prototype.
3152 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3153 return result;
3154 }
3155 for (const Argument &a : root->argList)
3156 {
3157 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3158 reg::Match match;
3159 if (!a.name.empty() || !a.defval.empty())
3160 {
3161 std::string name = a.name.str();
3162 if (reg::search(name,match,initChars) && match.position()==0)
3163 {
3164 result=true;
3165 }
3166 else
3167 {
3168 result=false; // arg has (type,name) pair -> function prototype
3169 }
3170 AUTO_TRACE_EXIT("function prototype: result={}",result);
3171 return result;
3172 }
3173 if (!a.type.empty() &&
3174 (a.type.at(a.type.length()-1)=='*' ||
3175 a.type.at(a.type.length()-1)=='&'))
3176 // type ends with * or & => pointer or reference
3177 {
3178 result=false;
3179 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3180 return result;
3181 }
3182 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3183 {
3184 result=false; // arg type is a known type
3185 AUTO_TRACE_EXIT("known type: result={}",result);
3186 return result;
3187 }
3188 if (checkIfTypedef(ctx,fd,a.type))
3189 {
3190 result=false; // argument is a typedef
3191 AUTO_TRACE_EXIT("typedef: result={}",result);
3192 return result;
3193 }
3194 std::string atype = a.type.str();
3195 if (reg::search(atype,match,initChars) && match.position()==0)
3196 {
3197 result=true; // argument type starts with typical initializer char
3198 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3199 return result;
3200 }
3201 std::string resType=resolveTypeDef(ctx,a.type).str();
3202 if (resType.empty()) resType=atype;
3203 static const reg::Ex idChars(R"(\a\w*)");
3204 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3205 {
3206 resType=match.str();
3207 if (resType=="int" || resType=="long" ||
3208 resType=="float" || resType=="double" ||
3209 resType=="char" || resType=="void" ||
3210 resType=="signed" || resType=="unsigned" ||
3211 resType=="const" || resType=="volatile" )
3212 {
3213 result=false; // type keyword -> function prototype
3214 AUTO_TRACE_EXIT("type keyword: result={}",result);
3215 return result;
3216 }
3217 }
3218 }
3219 result=true;
3220 }
3221
3222 AUTO_TRACE_EXIT("end: result={}",result);
3223 return result;
3224}
3225
3226//--------------------------------------------------------------------------------------
3227
3228/*! Searches for the end of a template in prototype \a s starting from
3229 * character position \a startPos. If the end was found the position
3230 * of the closing > is returned, otherwise -1 is returned.
3231 *
3232 * Handles exotic cases such as
3233 * \code
3234 * Class<(id<0)>
3235 * Class<bits<<2>
3236 * Class<"<">
3237 * Class<'<'>
3238 * Class<(")<")>
3239 * \endcode
3240 */
3241static int findEndOfTemplate(const DString &s,size_t startPos)
3242{
3243 // locate end of template
3244 size_t e=startPos;
3245 int brCount=1;
3246 int roundCount=0;
3247 size_t len = s.length();
3248 bool insideString=false;
3249 bool insideChar=false;
3250 char pc = 0;
3251 while (e<len && brCount!=0)
3252 {
3253 char c=s.at(e);
3254 switch(c)
3255 {
3256 case '<':
3257 if (!insideString && !insideChar)
3258 {
3259 if (e<len-1 && s.at(e+1)=='<')
3260 e++;
3261 else if (roundCount==0)
3262 brCount++;
3263 }
3264 break;
3265 case '>':
3266 if (!insideString && !insideChar)
3267 {
3268 if (e<len-1 && s.at(e+1)=='>')
3269 e++;
3270 else if (roundCount==0)
3271 brCount--;
3272 }
3273 break;
3274 case '(':
3275 if (!insideString && !insideChar)
3276 roundCount++;
3277 break;
3278 case ')':
3279 if (!insideString && !insideChar)
3280 roundCount--;
3281 break;
3282 case '"':
3283 if (!insideChar)
3284 {
3285 if (insideString && pc!='\\')
3286 insideString=false;
3287 else
3288 insideString=true;
3289 }
3290 break;
3291 case '\'':
3292 if (!insideString)
3293 {
3294 if (insideChar && pc!='\\')
3295 insideChar=false;
3296 else
3297 insideChar=true;
3298 }
3299 break;
3300 }
3301 pc = c;
3302 e++;
3303 }
3304 return brCount==0 ? static_cast<int>(e) : -1;
3305}
3306
3307//--------------------------------------------------------------------------------------
3308
3309static void addVariable(const Entry *root,int isFuncPtr=-1)
3310{
3311 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3312
3313 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3314 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3315 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3316
3317 DString type = root->type;
3318 DString name = root->name;
3319 DString args = root->args;
3320 if (type.empty() && name.find("operator")==DString::npos &&
3321 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3322 {
3323 // recover from parse error caused by redundant braces
3324 // like in "int *(var[10]);", which is parsed as
3325 // type="" name="int *" args="(var[10])"
3326
3327 type=name;
3328 std::string sargs = args.str();
3329 static const reg::Ex reName(R"(\a\w*)");
3330 reg::Match match;
3331 if (reg::search(sargs,match,reName))
3332 {
3333 name = match.str(); // e.g. 'var' in '(var[10])'
3334 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3335 size_t j = sargs.find(')');
3336 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3337 }
3338 }
3339 else
3340 {
3341 int i=isFuncPtr;
3342 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3343 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3344 if (i>=0) // function pointer
3345 {
3346 size_t ii = i;
3347 size_t ai = type.find('[',ii);
3348 if (ai>ii) // function pointer array
3349 {
3350 args.prepend(type.mid(ai));
3351 type=type.left(ai);
3352 }
3353 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3354 {
3355 type=type.left(type.length()-1);
3356 args.prepend(") ");
3357 }
3358 }
3359 }
3360 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3361
3362 DString scope;
3363 name=removeRedundantWhiteSpace(name);
3364
3365 // find the scope of this variable
3366 int index = computeQualifiedIndex(name);
3367 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3368 // grouped members are stored with full scope
3369 {
3370 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3371 scope=name.left(index);
3372 name=name.mid(index+2);
3373 }
3374 else
3375 {
3376 Entry *p = root->parent();
3377 while (p->section.isScope())
3378 {
3379 DString scopeName = p->name;
3380 if (!scopeName.empty())
3381 {
3382 scope.prepend(scopeName);
3383 break;
3384 }
3385 p=p->parent();
3386 }
3387 }
3388
3389 DString type_s = type;
3390 type=type.stripWhiteSpace();
3391 ClassDefMutable *cd=nullptr;
3392 bool isRelated=false;
3393 bool isMemberOf=false;
3394
3395 DString classScope=stripAnonymousNamespaceScope(scope);
3396 if (root->lang==SrcLangExt::CSharp)
3397 {
3398 classScope=mangleCSharpGenericName(classScope);
3399 }
3400 else
3401 {
3402 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3403 }
3404 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3405
3406
3407 // Look for last :: not part of template specifier
3408 int p=-1;
3409 for (size_t i=0;i<name.length()-1;i++)
3410 {
3411 if (name[i]==':' && name[i+1]==':')
3412 {
3413 p=static_cast<int>(i);
3414 }
3415 else if (name[i]=='<') // skip over template parts,
3416 // i.e. A::B<C::D> => p=1 and
3417 // A<B::C>::D => p=8
3418 {
3419 int e = findEndOfTemplate(name,i+1);
3420 if (e!=-1) i=static_cast<int>(e);
3421 }
3422 }
3423
3424 if (p!=-1) // found it
3425 {
3426 if (type=="friend class" || type=="friend struct" ||
3427 type=="friend union")
3428 {
3429 cd=getClassMutable(scope);
3430 if (cd)
3431 {
3432 addVariableToClass(root, // entry
3433 cd, // class to add member to
3434 MemberType::Friend, // type of member
3435 type, // type value as string
3436 name, // name of the member
3437 args, // arguments as string
3438 false, // from Anonymous scope
3439 nullptr, // anonymous member
3440 Protection::Public, // protection
3441 Relationship::Member // related to a class
3442 );
3443 }
3444 }
3445 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3446 {
3447 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3448 }
3449
3450
3451 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3452 return; /* skip this member, because it is a
3453 * static variable definition (always?), which will be
3454 * found in a class scope as well, but then we know the
3455 * correct protection level, so only then it will be
3456 * inserted in the correct list!
3457 */
3458 }
3459
3460 MemberType mtype = MemberType::Variable;
3461 if (type=="@")
3462 mtype=MemberType::EnumValue;
3463 else if (type_s.startsWith("typedef "))
3464 mtype=MemberType::Typedef;
3465 else if (type_s.startsWith("friend "))
3466 mtype=MemberType::Friend;
3467 else if (root->mtype==MethodTypes::Property)
3468 mtype=MemberType::Property;
3469 else if (root->mtype==MethodTypes::Event)
3470 mtype=MemberType::Event;
3471 else if (type.find("sequence<") != DString::npos)
3472 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3473 else if (type.find("dictionary<") != DString::npos)
3474 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3475
3476 if (!root->relates.empty()) // related variable
3477 {
3478 isRelated=true;
3479 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3480 if (getClass(root->relates)==nullptr && !scope.empty())
3481 scope=mergeScopes(scope,root->relates);
3482 else
3483 scope=root->relates;
3484 }
3485
3486 cd=getClassMutable(scope);
3487 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3488 if (cd)
3489 {
3490 MemberDef *md=nullptr;
3491
3492 // if cd is an anonymous (=tag less) scope we insert the member
3493 // into a non-anonymous parent scope as well. This is needed to
3494 // be able to refer to it using \var or \fn
3495
3496
3497 Relationship relationship = isMemberOf ? Relationship::Foreign :
3498 isRelated ? Relationship::Related :
3499 Relationship::Member ;
3500
3501 addVariableToClass(root, // entry
3502 cd, // class to add member to
3503 mtype, // member type
3504 type, // type value as string
3505 name, // name of the member
3506 args, // arguments as string
3507 false, // from anonymous scope
3508 md, // from anonymous member
3509 root->protection,
3510 relationship
3511 );
3512 }
3513 else if (!name.empty()) // global variable
3514 {
3515 addVariableToFile(root,mtype,scope,type,name,args,false,/*nullptr,*/nullptr);
3516 }
3517
3518}
3519
3520//----------------------------------------------------------------------
3521// Searches the Entry tree for typedef documentation sections.
3522// If found they are stored in their class or in the global list.
3523static void buildTypedefList(const Entry *root)
3524{
3525 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3526 if (!root->name.empty() &&
3527 root->section.isVariable() &&
3528 root->type.find("typedef ")!=DString::npos // its a typedef
3529 )
3530 {
3531 AUTO_TRACE();
3532 DString rname = removeRedundantWhiteSpace(root->name);
3533 DString scope;
3534 int index = computeQualifiedIndex(rname);
3535 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3536 // grouped members are stored with full scope
3537 {
3538 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3539 scope=rname.left(index);
3540 rname=rname.mid(index+2);
3541 }
3542 else
3543 {
3544 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3545 }
3549 bool found=false;
3550 if (mn) // symbol with the same name already found
3551 {
3552 for (auto &imd : *mn)
3553 {
3554 if (!imd->isTypedef())
3555 continue;
3556
3557 DString rtype = root->type;
3558 rtype.stripPrefix("typedef ");
3559
3560 // merge the typedefs only if they're not both grouped, and both are
3561 // either part of the same class, part of the same namespace, or both
3562 // are global (i.e., neither in a class or a namespace)
3563 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3564 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3565 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3566 // qPrint(imd->typeString()),qPrint(root->type));
3567 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3568 {
3569 MemberDefMutable *md = toMemberDefMutable(imd.get());
3570 if (md)
3571 {
3572 md->setDocumentation(root->doc,root->docFile,root->docLine);
3574 md->setDocsForDefinition(!root->proto);
3575 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3577 md->setRefItems(root->sli);
3578 md->setRequirementReferences(root->rqli);
3579 md->addQualifiers(root->qualifiers);
3580
3581 // merge ingroup specifiers
3582 if (md->getGroupDef()==nullptr && !root->groups.empty())
3583 {
3584 addMemberToGroups(root,md);
3585 }
3586 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3587 {
3588 //printf("existing member is grouped, new member not\n");
3589 }
3590 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3591 {
3592 //printf("both members are grouped\n");
3593 }
3594 found=true;
3595 break;
3596 }
3597 }
3598 }
3599 }
3600 if (found)
3601 {
3602 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3603 // mark the entry as processed, as we copied everything from it elsewhere
3604 // also, otherwise, due to containing `typedef` it may later get treated
3605 // as a function typedef in filterMemberDocumentation, which is incorrect
3606 root->markAsProcessed();
3607 }
3608 else
3609 {
3610 AUTO_TRACE_ADD("new typedef '{}'",rname);
3611 addVariable(root);
3612 }
3613
3614 }
3615 for (const auto &e : root->children())
3616 if (!e->section.isEnum())
3617 buildTypedefList(e.get());
3618}
3619
3620//----------------------------------------------------------------------
3621// Searches the Entry tree for sequence documentation sections.
3622// If found they are stored in the global list.
3623static void buildSequenceList(const Entry *root)
3624{
3625 if (!root->name.empty() &&
3626 root->section.isVariable() &&
3627 root->type.find("sequence<")!=DString::npos // it's a sequence
3628 )
3629 {
3630 AUTO_TRACE();
3631 addVariable(root);
3632 }
3633 for (const auto &e : root->children())
3634 if (!e->section.isEnum())
3635 buildSequenceList(e.get());
3636}
3637
3638//----------------------------------------------------------------------
3639// Searches the Entry tree for dictionary documentation sections.
3640// If found they are stored in the global list.
3641static void buildDictionaryList(const Entry *root)
3642{
3643 if (!root->name.empty() &&
3644 root->section.isVariable() &&
3645 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3646 )
3647 {
3648 AUTO_TRACE();
3649 addVariable(root);
3650 }
3651 for (const auto &e : root->children())
3652 if (!e->section.isEnum())
3653 buildDictionaryList(e.get());
3654}
3655
3656//----------------------------------------------------------------------
3657// Searches the Entry tree for Variable documentation sections.
3658// If found they are stored in their class or in the global list.
3659
3660static void buildVarList(const Entry *root)
3661{
3662 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3663 int isFuncPtr=-1;
3664 if (!root->name.empty() &&
3665 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3666 (
3667 (root->section.isVariable() && // it's a variable
3668 root->type.find("typedef ")==DString::npos // and not a typedef
3669 ) ||
3670 (root->section.isFunction() && // or maybe a function pointer variable
3671 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3672 ) ||
3673 (root->section.isFunction() && // class variable initialized by constructor
3675 )
3676 )
3677 ) // documented variable
3678 {
3679 AUTO_TRACE();
3680 addVariable(root,isFuncPtr);
3681 }
3682 for (const auto &e : root->children())
3683 if (!e->section.isEnum())
3684 buildVarList(e.get());
3685}
3686
3687//----------------------------------------------------------------------
3688// Searches the Entry tree for Interface sections (UNO IDL only).
3689// If found they are stored in their service or in the global list.
3690//
3691
3693 const Entry *root,
3694 ClassDefMutable *cd,
3695 DString const& rname)
3696{
3697 FileDef *fd = root->fileDef();
3698 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3699 DString fileName = root->fileName;
3700 if (fileName.empty() && root->tagInfo())
3701 {
3702 fileName = root->tagInfo()->tagName;
3703 }
3704 auto md = createMemberDef(
3705 fileName, root->startLine, root->startColumn, root->type, rname,
3706 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3707 type, ArgumentList(), root->argList, root->metaData);
3708 auto mmd = toMemberDefMutable(md.get());
3709 mmd->setTagInfo(root->tagInfo());
3710 mmd->setMemberClass(cd);
3711 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3712 mmd->setDocsForDefinition(false);
3713 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3714 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3715 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3716 mmd->setMemberSpecifiers(root->spec);
3717 mmd->setVhdlSpecifiers(root->vhdlSpec);
3718 mmd->setMemberGroupId(root->mGrpId);
3719 mmd->setTypeConstraints(root->typeConstr);
3720 mmd->setLanguage(root->lang);
3721 mmd->setBodyDef(fd);
3722 mmd->setFileDef(fd);
3723 mmd->addSectionsToDefinition(root->anchors);
3724 DString const def = root->type + " " + rname;
3725 mmd->setDefinition(def);
3727 mmd->addQualifiers(root->qualifiers);
3728
3729 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3730 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3731
3732 // add member to the class cd
3733 cd->insertMember(md.get());
3734 // also add the member as a "base" (to get nicer diagrams)
3735 // "optional" interface/service get Protected which turns into dashed line
3736 BaseInfo base(rname,
3737 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3738 TemplateNameMap templateNames;
3739 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3740 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3741 // add file to list of used files
3742 cd->insertUsedFile(fd);
3743
3744 addMemberToGroups(root,md.get());
3746 root->markAsProcessed();
3747 mmd->setRefItems(root->sli);
3748 mmd->setRequirementReferences(root->rqli);
3749
3750 // add member to the global list of all members
3752 mn->push_back(std::move(md));
3753}
3754
3755static void buildInterfaceAndServiceList(const Entry *root)
3756{
3757 if (root->section.isExportedInterface() || root->section.isIncludedService())
3758 {
3759 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3760 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3761 " mGrpId={} spec={} proto={} docFile='{}'",
3762 root->type, root->parent()->name, root->name, root->args,
3763 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3764 root->mGrpId, root->spec, root->proto, root->docFile);
3765
3766 DString rname = removeRedundantWhiteSpace(root->name);
3767
3768 if (!rname.empty())
3769 {
3770 DString scope = root->parent()->name;
3771 ClassDefMutable *cd = getClassMutable(scope);
3772 assert(cd);
3773 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3774 (ClassDef::Service == cd->compoundType()) ||
3776 {
3778 }
3779 else
3780 {
3781 assert(false); // was checked by scanner.l
3782 }
3783 }
3784 else if (rname.empty())
3785 {
3786 warn(root->fileName,root->startLine,
3787 "Illegal member name found.");
3788 }
3789 }
3790 // can only have these in IDL anyway
3791 switch (root->lang)
3792 {
3793 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3794 case SrcLangExt::IDL:
3795 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3796 break;
3797 default:
3798 return; // nothing to do here
3799 }
3800}
3801
3802
3803//----------------------------------------------------------------------
3804// Searches the Entry tree for Function sections.
3805// If found they are stored in their class or in the global list.
3806
3807static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3808 const DString &rtype,const DString &rname,const DString &rargs,
3809 bool isFriend,
3810 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3811 const DString &relates
3812 )
3813{
3814 FileDef *fd=root->fileDef();
3815
3816 DString type = rtype;
3817 DString args = rargs;
3818
3820 name.stripPrefix("::");
3821
3822 MemberType mtype = MemberType::Function;
3823 if (isFriend) mtype=MemberType::Friend;
3824 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3825 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3826 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3827
3828 // strip redundant template specifier for constructors
3829 size_t i = DString::npos;
3830 size_t j = DString::npos;
3831 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3832 !name.startsWith("operator ") && // not operator
3833 (i=name.find('<'))!=DString::npos && // containing <
3834 (j=name.find('>'))!=DString::npos && // or >
3835 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3836 )
3837 {
3838 name=name.left(i);
3839 }
3840
3841 DString fileName = root->fileName;
3842 if (fileName.empty() && root->tagInfo())
3843 {
3844 fileName = root->tagInfo()->tagName;
3845 }
3846
3847 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3848 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3849 // );
3850
3851 // adding class member
3852 Relationship relationship = relates.empty() ? Relationship::Member :
3853 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3854 Relationship::Related ;
3855 auto md = createMemberDef(
3856 fileName,root->startLine,root->startColumn,
3857 type,name,args,root->exception,
3858 protection,virt,
3859 stat && root->relatesType!=RelatesType::MemberOf,
3860 relationship,
3861 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3862 root->argList, root->metaData);
3863 auto mmd = toMemberDefMutable(md.get());
3864 mmd->setTagInfo(root->tagInfo());
3865 mmd->setMemberClass(cd);
3866 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3867 mmd->setDocsForDefinition(!root->proto);
3868 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3869 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3870 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3871 mmd->setMemberSpecifiers(spec);
3872 mmd->setVhdlSpecifiers(root->vhdlSpec);
3873 mmd->setMemberGroupId(root->mGrpId);
3874 mmd->setTypeConstraints(root->typeConstr);
3875 mmd->setLanguage(root->lang);
3876 mmd->setRequiresClause(root->req);
3877 mmd->setId(root->id);
3878 mmd->setBodyDef(fd);
3879 mmd->setFileDef(fd);
3880 mmd->addSectionsToDefinition(root->anchors);
3881 DString def;
3883 SrcLangExt lang = cd->getLanguage();
3884 DString scopeSeparator=getLanguageSpecificSeparator(lang);
3885 if (scopeSeparator!="::")
3886 {
3887 qualScope = substitute(qualScope,"::",scopeSeparator);
3888 }
3889 if (lang==SrcLangExt::PHP)
3890 {
3891 // for PHP we use Class::method and Namespace\method
3892 scopeSeparator="::";
3893 }
3894 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3895 {
3896 if (!type.empty())
3897 {
3898 def=type+" "+name; //+optArgs;
3899 }
3900 else
3901 {
3902 def=name; //+optArgs;
3903 }
3904 }
3905 else
3906 {
3907 if (!type.empty())
3908 {
3909 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3910 }
3911 else
3912 {
3913 def=qualScope+scopeSeparator+name; //+optArgs;
3914 }
3915 }
3916 def.stripPrefix("friend ");
3917 mmd->setDefinition(def);
3919 mmd->addQualifiers(root->qualifiers);
3920
3921 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3922 type, qualScope, rname, args, root->proto, def);
3923
3924 // add member to the class cd
3925 cd->insertMember(md.get());
3926 // add file to list of used files
3927 cd->insertUsedFile(fd);
3928
3929 addMemberToGroups(root,md.get());
3931 root->markAsProcessed();
3932 mmd->setRefItems(root->sli);
3933 mmd->setRequirementReferences(root->rqli);
3934
3935 // add member to the global list of all members
3936 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3938 mn->push_back(std::move(md));
3939}
3940
3941//------------------------------------------------------------------------------------------
3942
3943static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
3944{
3945 DString scope = sc;
3946
3947 // new global function
3949 auto md = createMemberDef(
3950 root->fileName,root->startLine,root->startColumn,
3951 root->type,name,root->args,root->exception,
3952 root->protection,root->virt,root->isStatic,Relationship::Member,
3953 MemberType::Function,
3954 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3955 root->argList,root->metaData);
3956 auto mmd = toMemberDefMutable(md.get());
3957 mmd->setTagInfo(root->tagInfo());
3958 mmd->setLanguage(root->lang);
3959 mmd->setId(root->id);
3960 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3961 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3962 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3963 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3964 mmd->setDocsForDefinition(!root->proto);
3965 mmd->setTypeConstraints(root->typeConstr);
3966 //md->setBody(root->body);
3967 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3968 FileDef *fd=root->fileDef();
3969 mmd->setBodyDef(fd);
3970 mmd->addSectionsToDefinition(root->anchors);
3971 mmd->setMemberSpecifiers(root->spec);
3972 mmd->setVhdlSpecifiers(root->vhdlSpec);
3973 mmd->setMemberGroupId(root->mGrpId);
3974 mmd->setRequiresClause(root->req);
3975 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3976
3977 NamespaceDefMutable *nd = nullptr;
3978 // see if the function is inside a namespace that was not part of
3979 // the name already (in that case nd should be non-zero already)
3980 if (root->parent()->section.isNamespace())
3981 {
3982 //DString nscope=removeAnonymousScopes(root->parent()->name);
3983 DString nscope=root->parent()->name;
3984 if (!nscope.empty())
3985 {
3986 nd = getResolvedNamespaceMutable(nscope);
3987 }
3988 }
3989 else if (root->parent()->section.isGroupDoc() && !scope.empty())
3990 {
3992 }
3993
3994 if (!scope.empty())
3995 {
3997 if (sep!="::")
3998 {
3999 scope = substitute(scope,"::",sep);
4000 }
4001 scope+=sep;
4002 }
4003
4004 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
4005 DString def;
4006 //DString optArgs = root->argList.empty() ? DString() : root->args;
4007 if (!root->type.empty())
4008 {
4009 def=root->type+" "+scope+name; //+optArgs;
4010 }
4011 else
4012 {
4013 def=scope+name; //+optArgs;
4014 }
4015 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4016 root->type,scope,rname,root->args,root->proto,def);
4017 mmd->setDefinition(def);
4019 mmd->addQualifiers(root->qualifiers);
4020
4021 mmd->setRefItems(root->sli);
4022 mmd->setRequirementReferences(root->rqli);
4023 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
4024 {
4025 // add member to namespace
4026 mmd->setNamespace(nd);
4027 nd->insertMember(md.get());
4028 }
4029 if (fd)
4030 {
4031 // add member to the file (we do this even if we have already
4032 // inserted it into the namespace)
4033 mmd->setFileDef(fd);
4034 fd->insertMember(md.get());
4035 }
4036
4037 addMemberToGroups(root,md.get());
4039 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4040 // allow find Member to pick it up
4041 {
4042 root->markAsProcessed(); // Otherwise we have finished with this entry.
4043 }
4044
4045 // add member to the list of file members
4047 mn->push_back(std::move(md));
4048}
4049
4050//------------------------------------------------------------------------------------------
4051
4052static void buildFunctionList(const Entry *root)
4053{
4054 if (root->section.isFunction())
4055 {
4056 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4057 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4058 " spec={} proto={} docFile='{}'",
4059 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4060 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4061 root->spec, root->proto, root->docFile);
4062
4063 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4064 DString rname = removeRedundantWhiteSpace(root->name);
4065 //printf("rname=%s\n",qPrint(rname));
4066
4067 DString scope;
4068 int index = computeQualifiedIndex(rname);
4069 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4070 // grouped members are stored with full scope
4071 {
4072 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4073 scope=rname.left(index);
4074 rname=rname.mid(index+2);
4075 }
4076 else
4077 {
4078 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4079 }
4080 if (!rname.empty() && scope.find('@')==DString::npos)
4081 {
4082 // check if this function's parent is a class
4083 if (root->lang==SrcLangExt::CSharp)
4084 {
4085 scope=mangleCSharpGenericName(scope);
4086 }
4087 else
4088 {
4089 scope=stripTemplateSpecifiersFromScope(scope,false);
4090 }
4091
4092 FileDef *rfd=root->fileDef();
4093
4094 size_t memIndex=rname.rfind("::");
4095
4097 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4098 {
4099 // strip scope from name
4100 rname=rname.mid(root->parent()->name.length()+2);
4101 }
4102
4103 bool isMember=false;
4104 if (memIndex!=DString::npos)
4105 {
4106 size_t ts=rname.find('<');
4107 size_t te=rname.find('>');
4108 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4109 {
4110 // note: the following code was replaced by inMember=true to deal with a
4111 // function rname='X::foo' of class X inside a namespace also called X...
4112 // bug id 548175
4113 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4114 //isMember = nd==nullptr;
4115 //if (nd)
4116 //{
4117 // // strip namespace scope from name
4118 // scope=rname.left(memIndex);
4119 // rname=rname.mid(memIndex+2);
4120 //}
4121 isMember = true;
4122 }
4123 else
4124 {
4125 isMember=memIndex<ts || memIndex>te;
4126 }
4127 }
4128
4129 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4130 {
4131 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4132 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4133 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4134 }
4135 else if (root->parent()->section.isObjcImpl() && cd)
4136 {
4137 const MemberDef *md = cd->getMemberByName(rname);
4138 if (md)
4139 {
4140 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4141 if (mdm)
4142 {
4143 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4144 mdm->setBodyDef(root->fileDef());
4145 }
4146 }
4147 }
4148 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4149 !isMember &&
4150 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4151 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4152 )
4153 // no member => unrelated function
4154 {
4155 /* check the uniqueness of the function name in the file.
4156 * A file could contain a function prototype and a function definition
4157 * or even multiple function prototypes.
4158 */
4159 bool found=false;
4160 MemberDef *md_found=nullptr;
4162 if (mn)
4163 {
4164 AUTO_TRACE_ADD("function '{}' already found",rname);
4165 for (const auto &imd : *mn)
4166 {
4167 MemberDefMutable *md = toMemberDefMutable(imd.get());
4168 if (md)
4169 {
4170 const NamespaceDef *mnd = md->getNamespaceDef();
4171 NamespaceDef *rnd = nullptr;
4172 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4173 DString fullScope = scope;
4174 DString parentScope = root->parent()->name;
4175 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4176 {
4177 if (!scope.empty()) fullScope.prepend("::");
4178 fullScope.prepend(parentScope);
4179 }
4180 //printf("fullScope=%s\n",qPrint(fullScope));
4181 rnd = getResolvedNamespace(fullScope);
4182 const FileDef *mfd = md->getFileDef();
4183 DString nsName,rnsName;
4184 if (mnd) nsName = mnd->name();
4185 if (rnd) rnsName = rnd->name();
4186 //printf("matching arguments for %s%s %s%s\n",
4187 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4188 const ArgumentList &mdAl = md->argumentList();
4189 const ArgumentList &mdTempl = md->templateArguments();
4190
4191 // in case of template functions, we need to check if the
4192 // functions have the same number of template parameters
4193 bool sameTemplateArgs = true;
4194 bool matchingReturnTypes = true;
4195 bool sameRequiresClause = true;
4196 if (!mdTempl.empty() && !root->tArgLists.empty())
4197 {
4198 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4199 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4200 {
4201 matchingReturnTypes = false;
4202 }
4203 if (md->requiresClause()!=root->req)
4204 {
4205 sameRequiresClause = false;
4206 }
4207 }
4208 else if (!mdTempl.empty() || !root->tArgLists.empty())
4209 { // if one has template parameters and the other doesn't then that also counts as a
4210 // difference
4211 sameTemplateArgs = false;
4212 }
4213
4214 bool staticsInDifferentFiles =
4215 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4216
4217 if (sameTemplateArgs &&
4218 matchingReturnTypes &&
4219 sameRequiresClause &&
4220 !staticsInDifferentFiles &&
4221 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4222 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4223 false,root->lang)
4224 )
4225 {
4226 GroupDef *gd=nullptr;
4227 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4228 {
4229 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4230 }
4231 //printf("match!\n");
4232 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4233 // see if we need to create a new member
4234 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4235 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4236 mfd->absFilePath()==root->fileName // prototype in the same file
4237 )
4238 );
4239 // otherwise, allow a duplicate global member with the same argument list
4240 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4241 {
4242 // member is already in the group, so we don't want to add it again.
4243 found=true;
4244 }
4245
4246 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4247
4248 if (found)
4249 {
4250 // merge argument lists
4251 ArgumentList mergedArgList = root->argList;
4252 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4253 // merge documentation
4254 if (md->documentation().empty() && !root->doc.empty())
4255 {
4256 if (root->proto)
4257 {
4259 }
4260 else
4261 {
4263 }
4264 }
4265
4266 md->setDocumentation(root->doc,root->docFile,root->docLine);
4268 md->setDocsForDefinition(!root->proto);
4269 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4270 {
4271 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4272 md->setBodyDef(rfd);
4273 }
4274
4275 if (md->briefDescription().empty() && !root->brief.empty())
4276 {
4277 md->setArgsString(root->args);
4278 }
4279 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4280
4282
4284 md->addQualifiers(root->qualifiers);
4285
4286 // merge ingroup specifiers
4287 if (md->getGroupDef()==nullptr && !root->groups.empty())
4288 {
4289 addMemberToGroups(root,md);
4290 }
4291 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4292 {
4293 //printf("existing member is grouped, new member not\n");
4294 }
4295 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4296 {
4297 //printf("both members are grouped\n");
4298 }
4300
4301 // if md is a declaration and root is the corresponding
4302 // definition, then turn md into a definition.
4303 if (md->isPrototype() && !root->proto)
4304 {
4305 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4306 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4307 }
4308 // if md is already the definition, then add the declaration info
4309 else if (!md->isPrototype() && root->proto)
4310 {
4311 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4312 }
4313 }
4314 }
4315 }
4316 if (found)
4317 {
4318 md_found = md;
4319 break;
4320 }
4321 }
4322 }
4323 if (!found) /* global function is unique with respect to the file */
4324 {
4325 addGlobalFunction(root,rname,scope);
4326 }
4327 else
4328 {
4329 FileDef *fd=root->fileDef();
4330 if (fd)
4331 {
4332 // add member to the file (we do this even if we have already
4333 // inserted it into the namespace)
4334 fd->insertMember(md_found);
4335 }
4336 }
4337
4338 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4339 }
4340 else
4341 {
4342 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4343 }
4344 }
4345 else if (rname.empty())
4346 {
4347 warn(root->fileName,root->startLine,
4348 "Illegal member name found."
4349 );
4350 }
4351 }
4352 for (const auto &e : root->children()) buildFunctionList(e.get());
4353}
4354
4355//----------------------------------------------------------------------
4356
4357static void findFriends()
4358{
4359 AUTO_TRACE();
4360 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4361 {
4362 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4363 if (mn)
4364 { // there are members with the same name
4365 // for each function with that name
4366 for (const auto &ifmd : *fn)
4367 {
4368 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4369 // for each member with that name
4370 for (const auto &immd : *mn)
4371 {
4372 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4373 //printf("Checking for matching arguments
4374 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4375 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4376 if (fmd && mmd &&
4377 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4378 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4379 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4380 true,mmd->getLanguage()
4381 )
4382
4383 ) // if the member is related and the arguments match then the
4384 // function is actually a friend.
4385 {
4386 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4387 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4388 const ArgumentList &mmdAl = mmd->argumentList();
4389 const ArgumentList &fmdAl = fmd->argumentList();
4390 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4391
4392 // reset argument lists to add missing default parameters
4393 DString mmdAlStr = argListToString(mmdAl);
4394 DString fmdAlStr = argListToString(fmdAl);
4395 mmd->setArgsString(mmdAlStr);
4396 fmd->setArgsString(fmdAlStr);
4397 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4398 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4399 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4400
4401 if (!fmd->documentation().empty())
4402 {
4403 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4404 }
4405 else if (!mmd->documentation().empty())
4406 {
4407 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4408 }
4409 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4410 {
4411 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4412 }
4413 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4414 {
4415 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4416 }
4417 if (!fmd->inbodyDocumentation().empty())
4418 {
4420 }
4421 else if (!mmd->inbodyDocumentation().empty())
4422 {
4424 }
4425 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4426 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4427 {
4428 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4429 mmd->setBodyDef(fmd->getBodyDef());
4430 //mmd->setBodyMember(fmd);
4431 }
4432 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4433 {
4434 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4435 fmd->setBodyDef(mmd->getBodyDef());
4436 //fmd->setBodyMember(mmd);
4437 }
4439
4441
4442 mmd->addQualifiers(fmd->getQualifiers());
4443 fmd->addQualifiers(mmd->getQualifiers());
4444
4445 }
4446 }
4447 }
4448 }
4449 }
4450}
4451
4452//----------------------------------------------------------------------
4453
4455{
4456 AUTO_TRACE();
4457
4458 // find matching function declaration and definitions.
4459 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4460 {
4461 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4462 /* find a matching function declaration and definition for this function */
4463 for (const auto &imdec : *mn)
4464 {
4465 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4466 if (mdec &&
4467 (mdec->isPrototype() ||
4468 (mdec->isVariable() && mdec->isExternal())
4469 ))
4470 {
4471 for (const auto &imdef : *mn)
4472 {
4473 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4474 if (mdef && mdec!=mdef &&
4475 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4476 {
4478 }
4479 }
4480 }
4481 }
4482 }
4483}
4484
4485//----------------------------------------------------------------------
4486
4488{
4489 AUTO_TRACE();
4490 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4491 {
4492 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4493 /* find a matching function declaration and definition for this function */
4494 for (const auto &imd : *mn)
4495 {
4496 MemberDefMutable *md = toMemberDefMutable(imd.get());
4497 if (md)
4498 {
4499 if (md->isPrototype())
4500 mdec=md;
4501 else if (md->isVariable() && md->isExternal())
4502 mdec=md;
4503
4504 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4505 mdef=md;
4506 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4507 mdef=md;
4508 }
4509
4510 if (mdef && mdec) break;
4511 }
4512 if (mdef && mdec)
4513 {
4514 const ArgumentList &mdefAl = mdef->argumentList();
4515 const ArgumentList &mdecAl = mdec->argumentList();
4516 if (
4517 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4518 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4519 true,mdef->getLanguage()
4520 )
4521 ) /* match found */
4522 {
4523 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4524 mdef->mergeReferences(mdec);
4525 mdec->mergeReferences(mdef);
4526 mdef->mergeReferencedBy(mdec);
4527 mdec->mergeReferencedBy(mdef);
4528 }
4529 }
4530 }
4531}
4532
4533//----------------------------------------------------------------------
4534
4536{
4537 AUTO_TRACE();
4538 // find match between function declaration and definition for
4539 // related functions
4540 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4541 {
4542 /* find a matching function declaration and definition for this function */
4543 // for each global function
4544 for (const auto &imd : *mn)
4545 {
4546 MemberDefMutable *md = toMemberDefMutable(imd.get());
4547 if (md)
4548 {
4549 //printf(" Function '%s'\n",qPrint(md->name()));
4551 if (rmn) // check if there is a member with the same name
4552 {
4553 //printf(" Member name found\n");
4554 // for each member with the same name
4555 for (const auto &irmd : *rmn)
4556 {
4557 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4558 //printf(" Member found: related='%d'\n",rmd->isRelated());
4559 if (rmd &&
4560 (rmd->isRelated() || rmd->isForeign()) && // related function
4561 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4562 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4563 true,md->getLanguage()
4564 )
4565 )
4566 {
4567 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4568 if (rmd->relatedAlso())
4569 md->setRelatedAlso(rmd->relatedAlso());
4570 else if (rmd->isForeign())
4571 md->makeForeign();
4572 else
4573 md->makeRelated();
4574 }
4575 }
4576 }
4577 }
4578 }
4579 }
4580}
4581
4582//----------------------------------------------------------------------
4583
4585{
4586 AUTO_TRACE();
4587 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4588 {
4589 size_t i=qualifiedName.rfind("::");
4590 if (i!=std::string::npos)
4591 {
4592 DString scope = qualifiedName.substr(0,i);
4593 DString name = qualifiedName.substr(i+2);
4595 if (mn)
4596 {
4597 for (const auto &imd : *mn)
4598 {
4599 MemberDefMutable *md = toMemberDefMutable(imd.get());
4600 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4601 {
4602 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4603 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4604 md->setBodySegment(bodyInfo.defLine,
4605 bodyInfo.startLine,
4606 bodyInfo.endLine);
4607 }
4608 }
4609 }
4610 }
4611 }
4612}
4613
4614//----------------------------------------------------------------------
4615
4616/*! make a dictionary of all template arguments of class cd
4617 * that are part of the base class name.
4618 * Example: A template class A with template arguments <R,S,T>
4619 * that inherits from B<T,T,S> will have T and S in the dictionary.
4620 */
4621static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4622{
4623 std::map<std::string,int> templateNames;
4624 int count=0;
4625 for (const Argument &arg : templateArguments)
4626 {
4627 static const reg::Ex re(R"(\a[\w:]*)");
4628 reg::Iterator it(name,re);
4630 for (; it!=end ; ++it)
4631 {
4632 const auto &match = *it;
4633 std::string n = match.str();
4634 if (n==arg.name.str())
4635 {
4636 if (templateNames.find(n)==templateNames.end())
4637 {
4638 templateNames.emplace(n,count);
4639 }
4640 }
4641 }
4642 }
4643 return templateNames;
4644}
4645
4646/*! Searches a class from within \a context and \a cd and returns its
4647 * definition if found (otherwise nullptr is returned).
4648 */
4650{
4651 ClassDef *result=nullptr;
4652 if (cd==nullptr)
4653 {
4654 return result;
4655 }
4656 FileDef *fd=cd->getFileDef();
4657 SymbolResolver resolver(fd);
4658 if (context && cd!=context)
4659 {
4660 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4661 }
4662 //printf("1. result=%p\n",result);
4663 if (result==nullptr)
4664 {
4665 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4666 }
4667 //printf("2. result=%p\n",result);
4668 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4669 {
4670 result = getClass(name);
4671 }
4672 //printf("3. result=%p\n",result);
4673 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4674 // qPrint(name),
4675 // context ? qPrint(context->name()) : "<none>",
4676 // cd ? qPrint(cd->name()) : "<none>",
4677 // result ? qPrint(result->name()) : "<none>",
4678 // Doxygen::classLinkedMap->find(name)
4679 // );
4680 return result;
4681}
4682
4683
4684static void findUsedClassesForClass(const Entry *root,
4685 Definition *context,
4686 ClassDefMutable *masterCd,
4687 ClassDefMutable *instanceCd,
4688 bool isArtificial,
4689 const ArgumentList *actualArgs = nullptr,
4690 const TemplateNameMap &templateNames = TemplateNameMap()
4691 )
4692{
4693 AUTO_TRACE();
4694 const ArgumentList &formalArgs = masterCd->templateArguments();
4695 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4696 {
4697 for (auto &mi : *mni)
4698 {
4699 const MemberDef *md=mi->memberDef();
4700 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4701 {
4702 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4703 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4704 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4705 if (!typedefValue.empty())
4706 {
4707 type = typedefValue;
4708 }
4709 int pos=0;
4710 DString usedClassName;
4711 DString templSpec;
4712 bool found=false;
4713 // the type can contain template variables, replace them if present
4714 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4715
4716 //printf(" template substitution gives=%s\n",qPrint(type));
4717 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4718 {
4719 // find the type (if any) that matches usedClassName
4720 SymbolResolver resolver(masterCd->getFileDef());
4721 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4722 //printf("====> usedClassName=%s -> typeCd=%s\n",
4723 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4724 if (typeCd)
4725 {
4726 usedClassName = typeCd->name();
4727 }
4728
4729 // replace any namespace aliases
4730 replaceNamespaceAliases(usedClassName);
4731 // add any template arguments to the class
4732 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4733 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4734
4735 TemplateNameMap formTemplateNames;
4736 if (templateNames.empty())
4737 {
4738 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4739 }
4740 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4741 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4742
4743 for (const Argument &arg : masterCd->templateArguments())
4744 {
4745 if (arg.name==usedName) // type is a template argument
4746 {
4747 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4748 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4749 if (usedCd==nullptr)
4750 {
4751 usedCdm = toClassDefMutable(
4752 Doxygen::hiddenClassLinkedMap->add(usedName,
4754 masterCd->getDefFileName(),masterCd->getDefLine(),
4755 masterCd->getDefColumn(),
4756 usedName,
4757 ClassDef::Class)));
4758 if (usedCdm)
4759 {
4760 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4761 usedCdm->makeTemplateArgument();
4762 usedCdm->setUsedOnly(true);
4763 usedCdm->setLanguage(masterCd->getLanguage());
4764 usedCd = usedCdm;
4765 }
4766 }
4767 if (usedCd)
4768 {
4769 found=true;
4770 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4771 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4772 if (usedCdm)
4773 {
4774 if (isArtificial) usedCdm->setArtificial(true);
4775 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4776 }
4777 }
4778 }
4779 }
4780
4781 if (!found)
4782 {
4783 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4784 //printf("Looking for used class %s: result=%s master=%s\n",
4785 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4786
4787 if (usedCd)
4788 {
4789 found=true;
4790 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4791 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4792 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4793 if (usedCdm)
4794 {
4795 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4796 }
4797 }
4798 }
4799 }
4800 if (!found && !type.empty()) // used class is not documented in any scope
4801 {
4803 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4804 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4805 {
4806 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4807 {
4808 type+=md->argsString();
4809 }
4810 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4811 usedCdm = toClassDefMutable(
4814 masterCd->getDefFileName(),masterCd->getDefLine(),
4815 masterCd->getDefColumn(),
4816 type,ClassDef::Class)));
4817 if (usedCdm)
4818 {
4819 usedCdm->setUsedOnly(true);
4820 usedCdm->setLanguage(masterCd->getLanguage());
4821 usedCd = usedCdm;
4822 }
4823 }
4824 if (usedCd)
4825 {
4826 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4827 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4828 if (usedCdm)
4829 {
4830 if (isArtificial) usedCdm->setArtificial(true);
4831 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4832 }
4833 }
4834 }
4835 }
4836 }
4837 }
4838}
4839
4841 const Entry *root,
4842 Definition *context,
4843 ClassDefMutable *masterCd,
4844 ClassDefMutable *instanceCd,
4846 bool isArtificial,
4847 const ArgumentList *actualArgs = nullptr,
4848 const TemplateNameMap &templateNames=TemplateNameMap()
4849 )
4850{
4851 AUTO_TRACE("name={}",root->name);
4852 // The base class could ofcouse also be a non-nested class
4853 const ArgumentList &formalArgs = masterCd->templateArguments();
4854 for (const BaseInfo &bi : root->extends)
4855 {
4856 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4857 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4858 TemplateNameMap formTemplateNames;
4859 if (templateNames.empty())
4860 {
4861 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4862 }
4863 BaseInfo tbi = bi;
4864 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4865 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4866
4867 if (mode==DocumentedOnly)
4868 {
4869 // find a documented base class in the correct scope
4870 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4871 {
4872 // 1.8.2: decided to show inheritance relations even if not documented,
4873 // we do make them artificial, so they do not appear in the index
4874 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4875 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
4876 //{
4877 // no documented base class -> try to find an undocumented one
4878 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4879 //}
4880 }
4881 }
4882 else if (mode==TemplateInstances)
4883 {
4884 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4885 }
4886 }
4887}
4888
4889//----------------------------------------------------------------------
4890
4891static void findTemplateInstanceRelation(const Entry *root,
4892 Definition *context,
4893 ClassDefMutable *templateClass,const DString &templSpec,
4894 const TemplateNameMap &templateNames,
4895 bool isArtificial)
4896{
4897 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4898 templateClass->name(),templSpec,isArtificial);
4899
4900 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4901 bool existingClass = templSpec==tempArgsStr;
4902 if (existingClass) return; // avoid recursion
4903
4904 bool freshInstance=false;
4905 ClassDefMutable *instanceClass = toClassDefMutable(
4906 templateClass->insertTemplateInstance(
4907 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4908 if (instanceClass)
4909 {
4910 if (freshInstance)
4911 {
4912 instanceClass->setArtificial(true);
4913 instanceClass->setLanguage(root->lang);
4914
4915 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4916 instanceClass->setTemplateBaseClassNames(templateNames);
4917
4918 // search for new template instances caused by base classes of
4919 // instanceClass
4920 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4921 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4922 {
4923 const Entry *templateRoot = it->second;
4924 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4925 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4926 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4927 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4928
4929 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4930 isArtificial,templArgs.get(),templateNames);
4931 }
4932 }
4933 else
4934 {
4935 AUTO_TRACE_ADD("instance already exists");
4936 }
4937 }
4938}
4939
4940//----------------------------------------------------------------------
4941
4942static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4943{
4944 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4945 // be used to match arguments (see issue #11111)
4946 AUTO_TRACE();
4947 DString ttype = md->typeString();
4948 ttype.stripPrefix("typedef ");
4949 if (size_t ti=ttype.find('<'); ti!=DString::npos)
4950 {
4951 DString templateClassName = ttype.left(ti);
4952 SymbolResolver resolver(root->fileDef());
4953 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4954 templateClassName, true, true);
4955 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4956 if (baseClass)
4957 {
4958 const ArgumentList &tl = baseClass->templateArguments();
4959 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4961 baseClass,
4962 ttype.mid(ti),
4963 templateNames,
4964 baseClass->isArtificial());
4965 }
4966 }
4967}
4968
4969//----------------------------------------------------------------------
4970
4971static bool isRecursiveBaseClass(const DString &scope,const DString &name)
4972{
4973 DString n=name;
4974 if (size_t index=n.find('<'); index!=DString::npos)
4975 {
4976 n=n.left(index);
4977 }
4978 bool result = rightScopeMatch(scope,n);
4979 return result;
4980}
4981
4983{
4984 if (name.empty()) return 0;
4985 int l = static_cast<int>(name.length());
4986 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4987 {
4988 int count=1;
4989 int i=l-2;
4990 char insideQuote=0;
4991 while (count>0 && i>=0)
4992 {
4993 char c = name[i--];
4994 switch (c)
4995 {
4996 case '>': if (!insideQuote) count++; break;
4997 case '<': if (!insideQuote) count--; break;
4998 case '\'': if (!insideQuote) insideQuote=c;
4999 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5000 break;
5001 case '"': if (!insideQuote) insideQuote=c;
5002 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5003 break;
5004 default: break;
5005 }
5006 }
5007 if (i>=0) l=i+1;
5008 }
5009 return l;
5010}
5011
5013 const Entry *root,
5014 Definition *context,
5015 ClassDefMutable *cd,
5016 const BaseInfo *bi,
5017 const TemplateNameMap &templateNames,
5019 bool isArtificial
5020 )
5021{
5022 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
5023
5024 DString biName=bi->name;
5025 bool explicitGlobalScope=false;
5026 if (biName.startsWith("::")) // explicit global scope
5027 {
5028 biName=biName.mid(2);
5029 explicitGlobalScope=true;
5030 }
5031
5032 Entry *parentNode=root->parent();
5033 bool lastParent=false;
5034 do // for each parent scope, starting with the largest scope
5035 // (in case of nested classes)
5036 {
5037 DString scopeName= parentNode ? parentNode->name : DString();
5038 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5039 do // try all parent scope prefixes, starting with the largest scope
5040 {
5041 //printf("scopePrefix='%s' biName='%s'\n",
5042 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5043
5044 DString baseClassName=biName;
5045 if (scopeOffset>0)
5046 {
5047 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5048 }
5049 if (root->lang==SrcLangExt::CSharp)
5050 {
5051 baseClassName = mangleCSharpGenericName(baseClassName);
5052 }
5053 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5054 SymbolResolver resolver(cd->getFileDef());
5055 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5056 baseClassName,
5057 mode==Undocumented,
5058 true
5059 );
5060 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5061 DString templSpec = resolver.getTemplateSpec();
5062 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5063 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5064 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5065 // cd ? qPrint(cd->name()):"<none>",
5066 // qPrint(baseClassName),
5067 // baseClass?qPrint(baseClass->name()):"<none>",
5068 // qPrint(templSpec)
5069 // );
5070 //if (baseClassName.left(root->name.length())!=root->name ||
5071 // baseClassName.at(root->name.length())!='<'
5072 // ) // Check for base class with the same name.
5073 // // If found then look in the outer scope for a match
5074 // // and prevent recursion.
5075 if (!isRecursiveBaseClass(root->name,baseClassName)
5076 || explicitGlobalScope
5077 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5078 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5079 || (root->lang==SrcLangExt::IDL &&
5080 (root->section.isExportedInterface() ||
5081 root->section.isIncludedService()))
5082 )
5083 {
5084 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5085 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5086
5087 int i=findTemplateSpecializationPosition(baseClassName);
5088 size_t si=baseClassName.rfind("::",i);
5089 if (si==DString::npos) si=0;
5090 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5091 // base class has template specifiers
5092 {
5093 // TODO: here we should try to find the correct template specialization
5094 // but for now, we only look for the unspecialized base class.
5095 int e=findEndOfTemplate(baseClassName,i+1);
5096 //printf("baseClass==0 i=%d e=%d\n",i,e);
5097 if (e!=-1) // end of template was found at e
5098 {
5099 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5100 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5101 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5102 baseClassName,
5103 mode==Undocumented,
5104 true
5105 );
5106 baseClassTypeDef = resolver.getTypedef();
5107 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5108 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5109 }
5110 }
5111 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5112 // know it is a template, so see if
5113 // we can also link to the explicit
5114 // instance (for instance if a class
5115 // derived from a template argument)
5116 {
5117 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5118 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5119 if (templClass)
5120 {
5121 // use the template instance instead of the template base.
5122 baseClass = templClass;
5123 templSpec.clear();
5124 }
5125 }
5126
5127 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5128 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5129 AUTO_TRACE_ADD("1. found={}",found);
5130 if (!found && si!=DString::npos)
5131 {
5132 // replace any namespace aliases
5133 replaceNamespaceAliases(baseClassName);
5134 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5135 baseClassName,
5136 mode==Undocumented,
5137 true
5138 );
5139 baseClassTypeDef = resolver.getTypedef();
5140 found=baseClass!=nullptr && baseClass!=cd;
5141 if (found) templSpec = resolver.getTemplateSpec();
5142 }
5143 AUTO_TRACE_ADD("2. found={}",found);
5144
5145 if (!found)
5146 {
5147 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5148 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5149 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5150 found = baseClass!=nullptr && baseClass!=cd;
5151
5152 }
5153 AUTO_TRACE_ADD("3. found={}",found);
5154 if (!found)
5155 {
5156 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5157 // the class name also in the alias mapping.
5158 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5159 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5160 {
5161 baseClass=getClassMutable(it->second.alias);
5162 found = baseClass!=nullptr && baseClass!=cd;
5163 }
5164 }
5165 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5166
5167 AUTO_TRACE_ADD("4. found={}",found);
5168 if (found)
5169 {
5170 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5171 // add base class to this class
5172
5173 // if templSpec is not empty then we should "instantiate"
5174 // the template baseClass. A new ClassDef should be created
5175 // to represent the instance. To be able to add the (instantiated)
5176 // members and documentation of a template class
5177 // (inserted in that template class at a later stage),
5178 // the template should know about its instances.
5179 // the instantiation process, should be done in a recursive way,
5180 // since instantiating a template may introduce new inheritance
5181 // relations.
5182 if (!templSpec.empty() && mode==TemplateInstances)
5183 {
5184 // if baseClass is actually a typedef then we should not
5185 // instantiate it, since typedefs are in a different namespace
5186 // see bug531637 for an example where this would otherwise hang
5187 // Doxygen
5188 if (baseClassTypeDef==nullptr)
5189 {
5190 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5191 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5192 }
5193 }
5194 else if (mode==DocumentedOnly || mode==Undocumented)
5195 {
5196 //printf(" => insert base class\n");
5197 DString usedName;
5198 if (baseClassTypeDef)
5199 {
5200 usedName=biName;
5201 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5202 }
5203 Protection prot = bi->prot;
5204 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5205 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5206 {
5207 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5208 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5209 // add this class as super class to the base class
5210 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5211 }
5212 else
5213 {
5214 warn(root->fileName,root->startLine,
5215 "Detected potential recursive class relation "
5216 "between class {} and base class {}!",
5217 cd->name(),baseClass->name()
5218 );
5219 }
5220 }
5221 return true;
5222 }
5223 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5224 {
5225 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5226 biName,baseClassName,templSpec,isArtificial);
5227 baseClass=nullptr;
5228 if (isATemplateArgument)
5229 {
5230 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5231 if (baseClass==nullptr) // not found (or alias)
5232 {
5233 baseClass= toClassDefMutable(
5234 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5235 createClassDef(root->fileName,root->startLine,root->startColumn,
5236 baseClassName,
5237 ClassDef::Class)));
5238 if (baseClass) // really added (not alias)
5239 {
5240 if (isArtificial) baseClass->setArtificial(true);
5241 baseClass->setLanguage(root->lang);
5242 }
5243 }
5244 }
5245 else
5246 {
5247 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5248 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5249 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5250 if (baseClass==nullptr) // not found (or alias)
5251 {
5252 baseClass = toClassDefMutable(
5253 Doxygen::classLinkedMap->add(baseClassName,
5254 createClassDef(root->fileName,root->startLine,root->startColumn,
5255 baseClassName,
5256 ClassDef::Class)));
5257 if (baseClass) // really added (not alias)
5258 {
5259 if (isArtificial) baseClass->setArtificial(true);
5260 baseClass->setLanguage(root->lang);
5261 si = baseClassName.rfind("::");
5262 if (si!=DString::npos) // class is nested
5263 {
5264 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5265 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5266 {
5267 baseClass->setArtificial(true); // see bug678139
5268 }
5269 }
5270 }
5271 }
5272 }
5273 if (baseClass)
5274 {
5275 if (biName.endsWith("-p"))
5276 {
5277 biName="<"+biName.left(biName.length()-2)+">";
5278 }
5279 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5280 {
5281 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5282 // add base class to this class
5283 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5284 // add this class as super class to the base class
5285 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5286 }
5287 // the undocumented base was found in this file
5288 baseClass->insertUsedFile(root->fileDef());
5289
5290 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5291 if (scope!=baseClass)
5292 {
5293 baseClass->setOuterScope(scope);
5294 }
5295
5296 if (baseClassName.endsWith("-p"))
5297 {
5299 }
5300 return true;
5301 }
5302 else
5303 {
5304 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5305 }
5306 }
5307 else
5308 {
5309 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5310 }
5311 }
5312 else
5313 {
5314 if (mode!=TemplateInstances)
5315 {
5316 warn(root->fileName,root->startLine,
5317 "Detected potential recursive class relation "
5318 "between class {} and base class {}!",
5319 root->name,baseClassName
5320 );
5321 }
5322 // for mode==TemplateInstance this case is quite common and
5323 // indicates a relation between a template class and a template
5324 // instance with the same name.
5325 }
5326 if (scopeOffset==0)
5327 {
5328 scopeOffset=-1;
5329 }
5330 else
5331 {
5332 size_t o = scopeName.rfind("::",scopeOffset-1);
5333 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5334 }
5335 //printf("new scopeOffset='%d'",scopeOffset);
5336 } while (scopeOffset>=0);
5337
5338 if (parentNode==nullptr)
5339 {
5340 lastParent=true;
5341 }
5342 else
5343 {
5344 parentNode=parentNode->parent();
5345 }
5346 } while (lastParent);
5347
5348 return false;
5349}
5350
5351//----------------------------------------------------------------------
5352// Computes the base and super classes for each class in the tree
5353
5354static bool isClassSection(const Entry *root)
5355{
5356 if ( !root->name.empty() )
5357 {
5358 if (root->section.isCompound())
5359 // is it a compound (class, struct, union, interface ...)
5360 {
5361 return true;
5362 }
5363 else if (root->section.isCompoundDoc())
5364 // is it a documentation block with inheritance info.
5365 {
5366 bool hasExtends = !root->extends.empty();
5367 if (hasExtends) return true;
5368 }
5369 }
5370 return false;
5371}
5372
5373
5374/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5375 */
5376static void findClassEntries(const Entry *root)
5377{
5378 if (isClassSection(root))
5379 {
5380 g_classEntries.emplace(root->name.str(),root);
5381 }
5382 for (const auto &e : root->children()) findClassEntries(e.get());
5383}
5384
5385static DString extractClassName(const Entry *root)
5386{
5387 // strip any anonymous scopes first
5390 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5391 {
5392 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5393 // template part before looking for matches
5394 if (root->lang==SrcLangExt::CSharp)
5395 {
5396 bName = mangleCSharpGenericName(root->name);
5397 }
5398 else
5399 {
5400 bName = bName.left(i);
5401 }
5402 }
5403 return bName;
5404}
5405
5406/*! Using the dictionary build by findClassEntries(), this
5407 * function will look for additional template specialization that
5408 * exists as inheritance relations only. These instances will be
5409 * added to the template they are derived from.
5410 */
5412{
5413 AUTO_TRACE();
5414 ClassDefSet visitedClasses;
5415 for (const auto &[name,root] : g_classEntries)
5416 {
5417 DString bName = extractClassName(root);
5418 ClassDefMutable *cdm = getClassMutable(bName);
5419 if (cdm)
5420 {
5421 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5422 }
5423 }
5424}
5425
5427{
5428 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5429 size_t i = root->name.find('<');
5430 size_t j = root->name.rfind('>');
5431 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5432 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5433 {
5434 ClassDefMutable *master = getClassMutable(root->name.left(i));
5435 if (master && master!=cd && !cd->templateMaster())
5436 {
5437 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5438 cd->setTemplateMaster(master);
5439 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5440 }
5441 }
5442}
5443
5445{
5446 AUTO_TRACE();
5447 for (const auto &[name,root] : g_classEntries)
5448 {
5449 DString bName = extractClassName(root);
5450 ClassDefMutable *cdm = getClassMutable(bName);
5451 if (cdm)
5452 {
5453 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5455 cdm->addTypeConstraints();
5456 }
5457 }
5458}
5459
5461{
5462 AUTO_TRACE();
5463 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5464 {
5465 if (!nd->hasDocumentation())
5466 {
5467 if ((guessSection(nd->getDefFileName()).isHeader() ||
5468 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5469 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5470 )
5471 {
5472 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5473 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5474 nd->name());
5475 }
5476 }
5477 }
5478}
5479
5481{
5482 AUTO_TRACE();
5483 for (const auto &[name,root] : g_classEntries)
5484 {
5485 DString bName = extractClassName(root);
5486 ClassDefMutable *cd = getClassMutable(bName);
5487 if (cd)
5488 {
5489 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5490 }
5491 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5492 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5493 {
5494 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5495 (guessSection(root->fileName).isHeader() ||
5496 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5497 protectionLevelVisible(root->protection) && // hidden by protection
5498 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5499 )
5500 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5501 }
5502 }
5503}
5504
5506{
5507 AUTO_TRACE();
5508 for (const auto &[name,root] : g_classEntries)
5509 {
5513 // strip any anonymous scopes first
5514 if (cd && !cd->getTemplateInstances().empty())
5515 {
5516 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5517 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5518 {
5519 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5520 if (tcd)
5521 {
5522 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5523 DString templSpec = ti.templSpec;
5524 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5525 for (const BaseInfo &bi : root->extends)
5526 {
5527 // check if the base class is a template argument
5528 BaseInfo tbi = bi;
5529 const ArgumentList &tl = cd->templateArguments();
5530 if (!tl.empty())
5531 {
5532 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5533 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5534 // for each template name that we inherit from we need to
5535 // substitute the formal with the actual arguments
5536 TemplateNameMap actualTemplateNames;
5537 for (const auto &tn_kv : templateNames)
5538 {
5539 size_t templIndex = tn_kv.second;
5540 Argument actArg;
5541 bool hasActArg=false;
5542 if (templIndex<templArgs->size())
5543 {
5544 actArg=templArgs->at(templIndex);
5545 hasActArg=true;
5546 }
5547 if (hasActArg &&
5548 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5549 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5550 )
5551 {
5552 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5553 }
5554 }
5555
5556 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5557 // find a documented base class in the correct scope
5558 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5559 {
5560 // no documented base class -> try to find an undocumented one
5561 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5562 }
5563 }
5564 }
5565 }
5566 }
5567 }
5568 }
5569}
5570
5571//-----------------------------------------------------------------------
5572// compute the references (anchors in HTML) for each function in the file
5573
5575{
5576 AUTO_TRACE();
5577 for (const auto &cd : *Doxygen::classLinkedMap)
5578 {
5579 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5580 if (cdm)
5581 {
5582 cdm->computeAnchors();
5583 }
5584 }
5585 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5586 {
5587 for (const auto &fd : *fn)
5588 {
5589 fd->computeAnchors();
5590 }
5591 }
5592 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5593 {
5595 if (ndm)
5596 {
5597 ndm->computeAnchors();
5598 }
5599 }
5600 for (const auto &gd : *Doxygen::groupLinkedMap)
5601 {
5602 gd->computeAnchors();
5603 }
5604}
5605
5606//----------------------------------------------------------------------
5607
5608
5609template<typename Func>
5610static void applyToAllDefinitions(Func func)
5611{
5612 for (const auto &cd : *Doxygen::classLinkedMap)
5613 {
5614 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5615 if (cdm)
5616 {
5617 func(cdm);
5618 }
5619 }
5620
5621 for (const auto &cd : *Doxygen::conceptLinkedMap)
5622 {
5623 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5624 if (cdm)
5625 {
5626 func(cdm);
5627 }
5628 }
5629
5630 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5631 {
5632 for (const auto &fd : *fn)
5633 {
5634 func(fd.get());
5635 }
5636 }
5637
5638 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5639 {
5641 if (ndm)
5642 {
5643 func(ndm);
5644 }
5645 }
5646
5647 for (const auto &gd : *Doxygen::groupLinkedMap)
5648 {
5649 func(gd.get());
5650 }
5651
5652 for (const auto &pd : *Doxygen::pageLinkedMap)
5653 {
5654 func(pd.get());
5655 }
5656
5657 for (const auto &dd : *Doxygen::dirLinkedMap)
5658 {
5659 func(dd.get());
5660 }
5661
5662 func(&ModuleManager::instance());
5663}
5664
5665//----------------------------------------------------------------------
5666
5668{
5669 AUTO_TRACE();
5670 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5671}
5672
5673//----------------------------------------------------------------------
5674
5676{
5677 AUTO_TRACE();
5678 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5679}
5680
5681
5682//----------------------------------------------------------------------
5683
5685{
5686 AUTO_TRACE();
5688 {
5689 rl->generatePage();
5690 }
5691}
5692
5693//----------------------------------------------------------------------
5694// Copy the documentation in entry 'root' to member definition 'md' and
5695// set the function declaration of the member to 'funcDecl'. If the boolean
5696// over_load is set the standard overload text is added.
5697
5698static void addMemberDocs(const Entry *root,
5699 MemberDefMutable *md, const DString &funcDecl,
5700 const ArgumentList *al,
5701 bool over_load,
5702 TypeSpecifier spec
5703 )
5704{
5705 if (md==nullptr) return;
5706 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5707 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5708 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5709 {
5710 DString fDecl=funcDecl;
5711 // strip extern specifier
5712 fDecl.stripPrefix("extern ");
5713 md->setDefinition(fDecl);
5714 }
5716 md->addQualifiers(root->qualifiers);
5718 const NamespaceDef *nd=md->getNamespaceDef();
5719 DString fullName;
5720 if (cd)
5721 fullName = cd->name();
5722 else if (nd)
5723 fullName = nd->name();
5724
5725 if (!fullName.empty()) fullName+="::";
5726 fullName+=md->name();
5727 FileDef *rfd=root->fileDef();
5728
5729 // TODO determine scope based on root not md
5730 Definition *rscope = md->getOuterScope();
5731
5732 const ArgumentList &mdAl = md->argumentList();
5733 if (al)
5734 {
5735 ArgumentList mergedAl = *al;
5736 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5737 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5738 }
5739 else
5740 {
5741 if (
5742 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5743 rscope,rfd,root->type,&root->argList,
5744 true, root->lang
5745 )
5746 )
5747 {
5748 //printf("merging arguments (2)\n");
5749 ArgumentList mergedArgList = root->argList;
5750 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5751 }
5752 }
5753 if (over_load) // the \overload keyword was used
5754 {
5756 if (!root->doc.empty())
5757 {
5758 doc+="<p>";
5759 doc+=root->doc;
5760 }
5761 md->setDocumentation(doc,root->docFile,root->docLine);
5763 md->setDocsForDefinition(!root->proto);
5764 }
5765 else
5766 {
5767 //printf("overwrite!\n");
5768 md->setDocumentation(root->doc,root->docFile,root->docLine);
5769 md->setDocsForDefinition(!root->proto);
5770
5771 //printf("overwrite!\n");
5772 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5773
5774 if (
5775 (md->inbodyDocumentation().empty() ||
5776 !root->parent()->name.empty()
5777 ) && !root->inbodyDocs.empty()
5778 )
5779 {
5781 }
5782 }
5783
5784 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5785 // qPrint(md->initializer()),md->initializer().empty(),
5786 // qPrint(root->initializer),root->initializer.empty()
5787 // );
5788 std::string rootInit = root->initializer.str();
5789 if (md->initializer().empty() && !rootInit.empty())
5790 {
5791 //printf("setInitializer\n");
5792 md->setInitializer(rootInit);
5793 }
5794 if (md->requiresClause().empty() && !root->req.empty())
5795 {
5796 md->setRequiresClause(root->req);
5797 }
5798
5799 md->setMaxInitLines(root->initLines);
5800
5801 if (rfd)
5802 {
5803 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5804 )
5805 {
5806 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5807 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5808 md->setBodyDef(rfd);
5809 }
5810
5811 md->setRefItems(root->sli);
5812 md->setRequirementReferences(root->rqli);
5813 }
5814
5816 md->addQualifiers(root->qualifiers);
5817
5818 md->mergeMemberSpecifiers(spec);
5820 addMemberToGroups(root,md);
5822 if (cd) cd->insertUsedFile(rfd);
5823 //printf("root->mGrpId=%d\n",root->mGrpId);
5824 if (root->mGrpId!=-1)
5825 {
5826 if (md->getMemberGroupId()!=-1)
5827 {
5828 if (md->getMemberGroupId()!=root->mGrpId)
5829 {
5830 warn(root->fileName,root->startLine,
5831 "member {} belongs to two different groups. The second one found here will be ignored.",
5832 md->name()
5833 );
5834 }
5835 }
5836 else // set group id
5837 {
5838 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5839 md->setMemberGroupId(root->mGrpId);
5840 }
5841 }
5842 md->addQualifiers(root->qualifiers);
5843}
5844
5845//----------------------------------------------------------------------
5846// find a class definition given the scope name and (optionally) a
5847// template list specifier
5848
5850 const DString &scopeName)
5851{
5852 SymbolResolver resolver(fd);
5853 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5854 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5855 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5856 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5857 return tcd;
5858}
5859
5860//----------------------------------------------------------------------------
5861// Returns true, if the entry belongs to the group of the member definition,
5862// otherwise false.
5863
5864static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5865{
5866 const GroupDef *gd = md->getGroupDef();
5867 if (!gd)
5868 {
5869 return allowNoGroup;
5870 }
5871
5872 for (const auto &g : root->groups)
5873 {
5874 if (g.groupname == gd->name())
5875 {
5876 return true; // matching group
5877 }
5878 }
5879
5880 return false;
5881}
5882
5883//----------------------------------------------------------------------
5884// Adds the documentation contained in 'root' to a global function
5885// with name 'name' and argument list 'args' (for overloading) and
5886// function declaration 'decl' to the corresponding member definition.
5887
5888static bool findGlobalMember(const Entry *root,
5889 const DString &namespaceName,
5890 const DString &type,
5891 const DString &name,
5892 const DString &tempArg,
5893 const DString &,
5894 const DString &decl,
5895 TypeSpecifier /* spec */)
5896{
5897 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5898 DString n=name;
5899 if (n.empty()) return false;
5900 if (n.find("::")!=DString::npos) return false; // skip undefined class members
5901 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5902 if (mn==nullptr)
5903 {
5904 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5905 }
5906 if (mn) // function name defined
5907 {
5908 AUTO_TRACE_ADD("Found symbol name");
5909 //int count=0;
5910 bool found=false;
5911 for (const auto &md : *mn)
5912 {
5913 // If the entry has groups, then restrict the search to members which are
5914 // in one of the groups of the entry. If md is not associated with a group yet,
5915 // allow this documentation entry to add the group info.
5916 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5917 {
5918 continue;
5919 }
5920
5921 const NamespaceDef *nd=nullptr;
5922 if (md->isAlias() && md->getOuterScope() &&
5923 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5924 {
5925 nd = toNamespaceDef(md->getOuterScope());
5926 }
5927 else
5928 {
5929 nd = md->getNamespaceDef();
5930 }
5931
5932 // special case for strong enums
5933 size_t enumNamePos=0;
5934 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
5935 { // md part of a strong enum in a namespace?
5936 DString enumName = namespaceName.mid(enumNamePos+2);
5937 if (namespaceName.left(enumNamePos)==nd->name())
5938 {
5940 if (enumMn)
5941 {
5942 for (const auto &emd : *enumMn)
5943 {
5944 found = emd->isStrong() && md->getEnumScope()==emd.get();
5945 if (found)
5946 {
5947 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5948 break;
5949 }
5950 }
5951 }
5952 }
5953 if (found)
5954 {
5955 break;
5956 }
5957 }
5958 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5959 {
5960 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5961 if (enumMn)
5962 {
5963 for (const auto &emd : *enumMn)
5964 {
5965 found = emd->isStrong() && md->getEnumScope()==emd.get();
5966 if (found)
5967 {
5968 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5969 break;
5970 }
5971 }
5972 }
5973 }
5974
5975 const FileDef *fd=root->fileDef();
5976 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5978 if (fd)
5979 {
5980 nl = fd->getUsedNamespaces();
5981 }
5982 //printf("NamespaceList %p\n",nl);
5983
5984 // search in the list of namespaces that are imported via a
5985 // using declaration
5986 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5987
5988 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
5989 (nd && nd->name()==namespaceName) || // or in the same namespace
5990 viaUsingDirective // member in 'using' namespace
5991 )
5992 {
5993 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5994
5995 NamespaceDef *rnd = nullptr;
5996 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5997
5998 const ArgumentList &mdAl = md.get()->argumentList();
5999 bool matching=
6000 (mdAl.empty() && root->argList.empty()) ||
6001 md->isVariable() || md->isTypedef() || /* in case of function pointers */
6002 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
6003 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
6004 false,root->lang);
6005
6006 // for template members we need to check if the number of
6007 // template arguments is the same, otherwise we are dealing with
6008 // different functions.
6009 if (matching && !root->tArgLists.empty())
6010 {
6011 const ArgumentList &mdTempl = md->templateArguments();
6012 if (root->tArgLists.back().size()!=mdTempl.size())
6013 {
6014 matching=false;
6015 }
6016 }
6017
6018 //printf("%s<->%s\n",
6019 // qPrint(argListToString(md->argumentList())),
6020 // qPrint(argListToString(root->argList)));
6021
6022 // For static members we also check if the comment block was found in
6023 // the same file. This is needed because static members with the same
6024 // name can be in different files. Thus it would be wrong to just
6025 // put the comment block at the first syntactically matching member. If
6026 // the comment block belongs to a group of the static member, then add
6027 // the documentation even if it is in a different file.
6028 if (matching && md->isStatic() &&
6029 md->getDefFileName()!=root->fileName &&
6030 mn->size()>1 &&
6031 !isEntryInGroupOfMember(root,md.get()))
6032 {
6033 matching = false;
6034 }
6035
6036 // for template member we also need to check the return type and requires
6037 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6038 {
6039 //printf("Comparing return types '%s'<->'%s'\n",
6040 // md->typeString(),type);
6041 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6042 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6043 md->typeString()!=type ||
6044 md->requiresClause()!=root->req)
6045 {
6046 //printf(" ---> no matching\n");
6047 matching = false;
6048 }
6049 }
6050
6051 if (matching) // add docs to the member
6052 {
6053 AUTO_TRACE_ADD("Match found");
6054 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6055 found=true;
6056 break;
6057 }
6058 }
6059 }
6060 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6061 {
6062 DString fullFuncDecl=decl;
6063 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6064 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6065 if (mn->size()>0)
6066 {
6067 warnMsg+="\nPossible candidates:";
6068 for (const auto &md : *mn)
6069 {
6070 warnMsg+="\n '";
6071 warnMsg+=replaceAnonymousScopes(md->declaration());
6072 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6073 }
6074 }
6075 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6076 }
6077 }
6078 else // got docs for an undefined member!
6079 {
6080 if (root->type!="friend class" &&
6081 root->type!="friend struct" &&
6082 root->type!="friend union" &&
6083 root->type!="friend" &&
6084 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6085 root->type.find("typedef ")==DString::npos)
6086 )
6087 {
6088 warn(root->fileName,root->startLine,
6089 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6090 );
6091 }
6092 }
6093 return true;
6094}
6095
6097 const ArgumentLists &srcTempArgLists,
6098 const ArgumentLists &dstTempArgLists
6099 )
6100{
6101 auto srcIt = srcTempArgLists.begin();
6102 auto dstIt = dstTempArgLists.begin();
6103 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6104 {
6105 if ((*srcIt).size()!=(*dstIt).size()) return true;
6106 ++srcIt;
6107 ++dstIt;
6108 }
6109 return false;
6110}
6111
6112static bool scopeIsTemplate(const Definition *d)
6113{
6114 bool result=false;
6115 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6117 {
6118 auto cd = toClassDef(d);
6119 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6121 }
6122 //printf("< scopeIsTemplate=%d\n",result);
6123 return result;
6124}
6125
6127 const ArgumentLists &srcTempArgLists,
6128 const ArgumentLists &dstTempArgLists,
6129 const std::string &src
6130 )
6131{
6132 std::string dst;
6133 static const reg::Ex re(R"(\a\w*)");
6134 reg::Iterator it(src,re);
6136 //printf("type=%s\n",qPrint(sa->type));
6137 size_t p=0;
6138 for (; it!=end ; ++it) // for each word in srcType
6139 {
6140 const auto &match = *it;
6141 size_t i = match.position();
6142 size_t l = match.length();
6143 bool found=false;
6144 dst+=src.substr(p,i-p);
6145 std::string name=match.str();
6146
6147 auto srcIt = srcTempArgLists.begin();
6148 auto dstIt = dstTempArgLists.begin();
6149 while (srcIt!=srcTempArgLists.end() && !found)
6150 {
6151 const ArgumentList *tdAli = nullptr;
6152 std::vector<Argument>::const_iterator tdaIt;
6153 if (dstIt!=dstTempArgLists.end())
6154 {
6155 tdAli = &(*dstIt);
6156 tdaIt = tdAli->begin();
6157 ++dstIt;
6158 }
6159
6160 const ArgumentList &tsaLi = *srcIt;
6161 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6162 {
6163 Argument tsa = *tsaIt;
6164 const Argument *tda = nullptr;
6165 if (tdAli && tdaIt!=tdAli->end())
6166 {
6167 tda = &(*tdaIt);
6168 ++tdaIt;
6169 }
6170 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6171 // qPrint(tsa.type),qPrint(tsa.name),
6172 // qPrint(tda->type),qPrint(tda->name));
6173 if (name==tsa.name.str())
6174 {
6175 if (tda && tda->name.empty())
6176 {
6177 DString tdaName = tda->name;
6178 DString tdaType = tda->type;
6179 int vc=0;
6180 if (tdaType.startsWith("class ")) vc=6;
6181 else if (tdaType.startsWith("typename ")) vc=9;
6182 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6183 {
6184 tdaName = tdaType.mid(vc);
6185 }
6186 if (!tdaName.empty())
6187 {
6188 name=tdaName.str(); // substitute
6189 found=true;
6190 }
6191 }
6192 }
6193 }
6194
6195 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6196 // qPrint(argListToString(srclali.current())),
6197 // qPrint(argListToString(dstlali.current())),
6198 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6199 ++srcIt;
6200 }
6201 dst+=name;
6202 p=i+l;
6203 }
6204 dst+=src.substr(p);
6205 //printf(" substituteTemplatesInString(%s)=%s\n",
6206 // qPrint(src),qPrint(dst));
6207 return dst;
6208}
6209
6211 const ArgumentLists &srcTempArgLists,
6212 const ArgumentLists &dstTempArgLists,
6213 const ArgumentList &src,
6214 ArgumentList &dst
6215 )
6216{
6217 auto dstIt = dst.begin();
6218 for (const Argument &sa : src)
6219 {
6220 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6221 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6222 if (dstIt == dst.end())
6223 {
6224 Argument da = sa;
6225 da.type = dstType;
6226 da.array = dstArray;
6227 dst.push_back(da);
6228 dstIt = dst.end();
6229 }
6230 else
6231 {
6232 Argument da = *dstIt;
6233 da.type = dstType;
6234 da.array = dstArray;
6235 ++dstIt;
6236 }
6237 }
6242 srcTempArgLists,dstTempArgLists,
6243 src.trailingReturnType().str()));
6244 dst.setIsDeleted(src.isDeleted());
6245 dst.setRefQualifier(src.refQualifier());
6246 dst.setNoParameters(src.noParameters());
6247 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6248 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6249 // );
6250}
6251
6252//-------------------------------------------------------------------------------------------
6253
6254static void addLocalObjCMethod(const Entry *root,
6255 const DString &scopeName,
6256 const DString &funcType,const DString &funcName,const DString &funcArgs,
6257 const DString &exceptions,const DString &funcDecl,
6258 TypeSpecifier spec)
6259{
6260 AUTO_TRACE();
6261 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6262 ClassDefMutable *cd=nullptr;
6263 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6264 {
6265 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6266 auto md = createMemberDef(
6267 root->fileName,root->startLine,root->startColumn,
6268 funcType,funcName,funcArgs,exceptions,
6269 root->protection,root->virt,root->isStatic,Relationship::Member,
6270 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6271 auto mmd = toMemberDefMutable(md.get());
6272 mmd->setTagInfo(root->tagInfo());
6273 mmd->setLanguage(root->lang);
6274 mmd->setId(root->id);
6275 mmd->makeImplementationDetail();
6276 mmd->setMemberClass(cd);
6277 mmd->setDefinition(funcDecl);
6279 mmd->addQualifiers(root->qualifiers);
6280 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6281 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6282 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6283 mmd->setDocsForDefinition(!root->proto);
6284 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6285 mmd->addSectionsToDefinition(root->anchors);
6286 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6287 FileDef *fd=root->fileDef();
6288 mmd->setBodyDef(fd);
6289 mmd->setMemberSpecifiers(spec);
6290 mmd->setVhdlSpecifiers(root->vhdlSpec);
6291 mmd->setMemberGroupId(root->mGrpId);
6292 cd->insertMember(md.get());
6293 cd->insertUsedFile(fd);
6294 mmd->setRefItems(root->sli);
6295 mmd->setRequirementReferences(root->rqli);
6296
6298 mn->push_back(std::move(md));
6299 }
6300 else
6301 {
6302 // local objective C method found for class without interface
6303 }
6304}
6305
6306//-------------------------------------------------------------------------------------------
6307
6308static void addMemberFunction(const Entry *root,
6309 MemberName *mn,
6310 const DString &scopeName,
6311 const DString &namespaceName,
6312 const DString &className,
6313 const DString &funcTyp,
6314 const DString &funcName,
6315 const DString &funcArgs,
6316 const DString &funcTempList,
6317 const DString &exceptions,
6318 const DString &type,
6319 const DString &args,
6320 bool isFriend,
6321 TypeSpecifier spec,
6322 const DString &relates,
6323 const DString &funcDecl,
6324 bool overloaded,
6325 bool isFunc)
6326{
6327 AUTO_TRACE();
6328 DString funcType = funcTyp;
6329 int count=0;
6330 int noMatchCount=0;
6331 bool memFound=false;
6332 for (const auto &imd : *mn)
6333 {
6334 MemberDefMutable *md = toMemberDefMutable(imd.get());
6335 if (md==nullptr) continue;
6337 if (cd==nullptr) continue;
6338 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6339 // scopeName, cd->name(), md->argsString(), root->fileName);
6340 FileDef *fd=root->fileDef();
6341 NamespaceDef *nd=nullptr;
6342 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6343
6344 //printf("scopeName %s->%s\n",qPrint(scopeName),
6345 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6346
6347 // if the member we are searching for is an enum value that is part of
6348 // a "strong" enum, we need to look into the fields of the enum for a match
6349 size_t enumNamePos=0;
6350 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6351 {
6352 DString enumName = className.mid(enumNamePos+2);
6353 DString fullScope = className.left(enumNamePos);
6354 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6355 if (fullScope==cd->name())
6356 {
6357 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6358 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6359 if (enumMn)
6360 {
6361 for (const auto &emd : *enumMn)
6362 {
6363 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6364 if (memFound)
6365 {
6366 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6367 count++;
6368 }
6369 if (memFound) break;
6370 }
6371 }
6372 }
6373 }
6374 if (memFound) break;
6375
6376 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6377 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6378 {
6379 // don't be fooled by anonymous scopes
6380 tcd=cd;
6381 }
6382 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6383 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6384
6385 if (cd && tcd==cd) // member's classes match
6386 {
6387 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6388
6389 // get the template parameter lists found at the member declaration
6390 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6391 const ArgumentList &templAl = md->templateArguments();
6392 if (!templAl.empty())
6393 {
6394 declTemplArgs.push_back(templAl);
6395 }
6396
6397 // get the template parameter lists found at the member definition
6398 const ArgumentLists &defTemplArgs = root->tArgLists;
6399 //printf("defTemplArgs=%p\n",defTemplArgs);
6400
6401 // do we replace the decl argument lists with the def argument lists?
6402 bool substDone=false;
6403 ArgumentList argList;
6404
6405 /* substitute the occurrences of class template names in the
6406 * argument list before matching
6407 */
6408 const ArgumentList &mdAl = md->argumentList();
6409 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6410 {
6411 /* the function definition has template arguments
6412 * and the class definition also has template arguments, so
6413 * we must substitute the template names of the class by that
6414 * of the function definition before matching.
6415 */
6416 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6417
6418 substDone=true;
6419 }
6420 else /* no template arguments, compare argument lists directly */
6421 {
6422 argList = mdAl;
6423 }
6424
6425 bool matching=
6426 md->isVariable() || md->isTypedef() || // needed for function pointers
6428 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6429 cd,fd,root->type,&root->argList,
6430 true,root->lang);
6431
6432 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6433 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6434
6435 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6436 {
6437 matching = false; // don't match methods and attributes with the same name
6438 }
6439
6440 // for template member we also need to check the return type
6441 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6442 {
6443 DString memType = md->typeString();
6444 memType.stripPrefix("static "); // see bug700696
6445 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6446 className+"::",""); // see bug700693 & bug732594
6447 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6448 className+"::",""); // see bug758900
6449 if (memType=="auto" && !argList.trailingReturnType().empty())
6450 {
6451 memType = argList.trailingReturnType();
6452 memType.stripPrefix(" -> ");
6453 }
6454 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6455 {
6456 funcType = root->argList.trailingReturnType();
6457 funcType.stripPrefix(" -> ");
6459 substDone=true;
6460 }
6461 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6462 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6463 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6464 {
6465 //printf(" ---> no matching\n");
6466 matching = false;
6467 }
6468 }
6469 else if (defTemplArgs.size()>declTemplArgs.size())
6470 {
6471 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6472 // avoid matching a non-template function in a template class against a
6473 // template function with the same name and parameters, see issue #10184
6474 substDone = false;
6475 matching = false;
6476 }
6477 bool rootIsUserDoc = root->section.isMemberDoc();
6478 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6479 bool mdIsTemplate = md->templateArguments().hasParameters();
6480 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6481 bool rootIsTemplate = !root->tArgLists.empty();
6482 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6483 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6484 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6485 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6486 )
6487 {
6488 // Method with template return type does not match method without return type
6489 // even if the parameters are the same. See also bug709052
6490 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6491 matching = false;
6492 }
6493
6494 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6495
6496 if (substDone) // found a new argument list
6497 {
6498 if (matching) // replace member's argument list
6499 {
6501 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6502 }
6503 else // no match
6504 {
6505 if (!funcTempList.empty() &&
6506 isSpecialization(declTemplArgs,defTemplArgs))
6507 {
6508 // check if we are dealing with a partial template
6509 // specialization. In this case we add it to the class
6510 // even though the member arguments do not match.
6511
6512 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6513 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6514 return;
6515 }
6516 }
6517 }
6518 if (matching)
6519 {
6520 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6521 count++;
6522 memFound=true;
6523 }
6524 }
6525 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6526 // but in a different namespace
6527 {
6528 noMatchCount++;
6529 }
6530
6531 if (memFound) break;
6532 }
6533 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6534 {
6535 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6536 return;
6537 }
6538 if (count==0 && !(isFriend && funcType=="class"))
6539 {
6540 int candidates=0;
6541 const ClassDef *ecd = nullptr, *ucd = nullptr;
6542 MemberDef *emd = nullptr, *umd = nullptr;
6543 //printf("Assume template class\n");
6544 for (const auto &md : *mn)
6545 {
6546 MemberDef *cmd=md.get();
6548 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6549 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6550 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6551 {
6552 const ArgumentList &templAl = md->templateArguments();
6553 if (!root->tArgLists.empty() && !templAl.empty() &&
6554 root->tArgLists.back().size()<=templAl.size())
6555 {
6556 AUTO_TRACE_ADD("add template specialization");
6557 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6558 root->protection,root->isStatic,root->virt,spec,relates);
6559 return;
6560 }
6561 if (argListToString(md->argumentList(),false,false) ==
6562 argListToString(root->argList,false,false))
6563 { // exact argument list match -> remember
6564 ucd = ecd = ccd;
6565 umd = emd = cmd;
6566 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6567 className,ccd->name(),md->argsString());
6568 }
6569 else // arguments do not match, but member name and scope do -> remember
6570 {
6571 ucd = ccd;
6572 umd = cmd;
6573 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6574 className,ccd->name(),md->argsString());
6575 }
6576 candidates++;
6577 }
6578 }
6579 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6580 if (!strictProtoMatching)
6581 {
6582 if (candidates==1 && ucd && umd)
6583 {
6584 // we didn't find an actual match on argument lists, but there is only 1 member with this
6585 // name in the same scope, so that has to be the one.
6586 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6587 return;
6588 }
6589 else if (candidates>1 && ecd && emd)
6590 {
6591 // we didn't find a unique match using type resolution,
6592 // but one of the matches has the exact same signature so
6593 // we take that one.
6594 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6595 return;
6596 }
6597 }
6598
6599 DString warnMsg = "no ";
6600 if (noMatchCount>1) warnMsg+="uniquely ";
6601 warnMsg+="matching class member found for \n";
6602
6603 for (const ArgumentList &al : root->tArgLists)
6604 {
6605 warnMsg+=" template ";
6606 warnMsg+=tempArgListToString(al,root->lang);
6607 warnMsg+='\n';
6608 }
6609
6610 DString fullFuncDecl=funcDecl;
6611 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6612
6613 warnMsg+=" ";
6614 warnMsg+=fullFuncDecl;
6615
6616 if (candidates>0 || noMatchCount>=1)
6617 {
6618 warnMsg+="\nPossible candidates:";
6619
6620 NamespaceDef *nd=nullptr;
6621 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6622 FileDef *fd=root->fileDef();
6623
6624 for (const auto &md : *mn)
6625 {
6626 const ClassDef *cd=md->getClassDef();
6627 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6628 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6629 {
6630 // don't be fooled by anonymous scopes
6631 tcd=cd;
6632 }
6633 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6634 {
6635 warnMsg+='\n';
6636 const ArgumentList &templAl = md->templateArguments();
6637 warnMsg+=" '";
6638 if (templAl.hasParameters())
6639 {
6640 warnMsg+="template ";
6641 warnMsg+=tempArgListToString(templAl,root->lang);
6642 warnMsg+='\n';
6643 warnMsg+=" ";
6644 }
6645 if (!md->typeString().empty())
6646 {
6647 warnMsg+=md->typeString();
6648 warnMsg+=' ';
6649 }
6651 if (!qScope.empty())
6652 warnMsg+=qScope+"::"+md->name();
6653 warnMsg+=md->argsString();
6654 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6655 }
6656 }
6657 }
6658 warn(root->fileName,root->startLine,"{}",warnMsg);
6659 }
6660}
6661
6662//-------------------------------------------------------------------------------------------
6663
6664static void addMemberSpecialization(const Entry *root,
6665 MemberName *mn,
6666 ClassDefMutable *cd,
6667 const DString &funcType,
6668 const DString &funcName,
6669 const DString &funcArgs,
6670 const DString &funcDecl,
6671 const DString &exceptions,
6672 TypeSpecifier spec
6673 )
6674{
6675 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6676 MemberDef *declMd=nullptr;
6677 for (const auto &md : *mn)
6678 {
6679 if (md->getClassDef()==cd)
6680 {
6681 // TODO: we should probably also check for matching arguments
6682 declMd = md.get();
6683 break;
6684 }
6685 }
6686 MemberType mtype=MemberType::Function;
6687 ArgumentList tArgList;
6688 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6689 auto md = createMemberDef(
6690 root->fileName,root->startLine,root->startColumn,
6691 funcType,funcName,funcArgs,exceptions,
6692 declMd ? declMd->protection() : root->protection,
6693 root->virt,root->isStatic,Relationship::Member,
6694 mtype,tArgList,root->argList,root->metaData);
6695 auto mmd = toMemberDefMutable(md.get());
6696 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6697 mmd->setTagInfo(root->tagInfo());
6698 mmd->setLanguage(root->lang);
6699 mmd->setId(root->id);
6700 mmd->setMemberClass(cd);
6701 mmd->setTemplateSpecialization(true);
6702 mmd->setTypeConstraints(root->typeConstr);
6703 mmd->setDefinition(funcDecl);
6705 mmd->addQualifiers(root->qualifiers);
6706 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6707 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6708 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6709 mmd->setDocsForDefinition(!root->proto);
6710 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6711 mmd->addSectionsToDefinition(root->anchors);
6712 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6713 FileDef *fd=root->fileDef();
6714 mmd->setBodyDef(fd);
6715 mmd->setMemberSpecifiers(spec);
6716 mmd->setVhdlSpecifiers(root->vhdlSpec);
6717 mmd->setMemberGroupId(root->mGrpId);
6718 cd->insertMember(md.get());
6719 mmd->setRefItems(root->sli);
6720 mmd->setRequirementReferences(root->rqli);
6721
6722 mn->push_back(std::move(md));
6723}
6724
6725//-------------------------------------------------------------------------------------------
6726
6727static void addOverloaded(const Entry *root,MemberName *mn,
6728 const DString &funcType,const DString &funcName,const DString &funcArgs,
6729 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6730{
6731 // for unique overloaded member we allow the class to be
6732 // omitted, this is to be Qt compatible. Using this should
6733 // however be avoided, because it is error prone
6734 bool sameClass=false;
6735 if (mn->size()>0)
6736 {
6737 // check if all members with the same name are also in the same class
6738 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6739 [](const auto &md1,const auto &md2)
6740 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6741 }
6742 if (sameClass)
6743 {
6744 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6745 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6746 if (cd==nullptr) return;
6747
6748 MemberType mtype = MemberType::Function;
6749 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6750 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6751 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6752
6753 // new overloaded member function
6754 std::unique_ptr<ArgumentList> tArgList =
6755 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6756 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6757 auto md = createMemberDef(
6758 root->fileName,root->startLine,root->startColumn,
6759 funcType,funcName,funcArgs,exceptions,
6760 root->protection,root->virt,root->isStatic,Relationship::Related,
6761 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6762 auto mmd = toMemberDefMutable(md.get());
6763 mmd->setTagInfo(root->tagInfo());
6764 mmd->setLanguage(root->lang);
6765 mmd->setId(root->id);
6766 mmd->setTypeConstraints(root->typeConstr);
6767 mmd->setMemberClass(cd);
6768 mmd->setDefinition(funcDecl);
6770 mmd->addQualifiers(root->qualifiers);
6772 doc+="<p>";
6773 doc+=root->doc;
6774 mmd->setDocumentation(doc,root->docFile,root->docLine);
6775 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6776 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6777 mmd->setDocsForDefinition(!root->proto);
6778 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6779 mmd->addSectionsToDefinition(root->anchors);
6780 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6781 FileDef *fd=root->fileDef();
6782 mmd->setBodyDef(fd);
6783 mmd->setMemberSpecifiers(spec);
6784 mmd->setVhdlSpecifiers(root->vhdlSpec);
6785 mmd->setMemberGroupId(root->mGrpId);
6786 cd->insertMember(md.get());
6787 cd->insertUsedFile(fd);
6788 mmd->setRefItems(root->sli);
6789 mmd->setRequirementReferences(root->rqli);
6790
6791 mn->push_back(std::move(md));
6792 }
6793}
6794
6795static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6796{
6797 if (outerScope && outerScope!=Doxygen::globalScope)
6798 {
6799 auto aliasMd = createMemberDefAlias(outerScope,md);
6800 if (outerScope->definitionType()==Definition::TypeClass)
6801 {
6802 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6803 if (cdm)
6804 {
6805 cdm->insertMember(aliasMd.get());
6806 }
6807 }
6808 else if (outerScope->definitionType()==Definition::TypeNamespace)
6809 {
6810 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6811 if (ndm)
6812 {
6813 ndm->insertMember(aliasMd.get());
6814 }
6815 }
6816 else if (outerScope->definitionType()==Definition::TypeFile)
6817 {
6818 toFileDef(outerScope)->insertMember(aliasMd.get());
6819 }
6820 if (aliasMd)
6821 {
6822 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6823 }
6824 }
6825}
6826
6827//-------------------------------------------------------------------------------------------
6828
6829/*! This function tries to find a member (in a documented class/file/namespace)
6830 * that corresponds to the function/variable declaration given in \a funcDecl.
6831 *
6832 * The boolean \a overloaded is used to specify whether or not a standard
6833 * overload documentation line should be generated.
6834 *
6835 * The boolean \a isFunc is a hint that indicates that this is a function
6836 * instead of a variable or typedef.
6837 */
6838static void findMember(const Entry *root,
6839 const DString &relates,
6840 const DString &type,
6841 const DString &args,
6842 DString funcDecl,
6843 bool overloaded,
6844 bool isFunc
6845 )
6846{
6847 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6848 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6849 root->spec, root->lang);
6850
6851 DString scopeName;
6852 DString className;
6853 DString namespaceName;
6854 DString funcType;
6855 DString funcName;
6856 DString funcArgs;
6857 DString funcTempList;
6858 DString exceptions;
6859 DString funcSpec;
6860 bool isRelated=false;
6861 bool isMemberOf=false;
6862 bool isFriend=false;
6863 bool done=false;
6864 TypeSpecifier spec = root->spec;
6865 while (!done)
6866 {
6867 done=true;
6868 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6869 {
6870 isFriend=true;
6871 done=false;
6872 }
6873 if (funcDecl.stripPrefix("inline "))
6874 {
6875 spec.setInline(true);
6876 done=false;
6877 }
6878 if (funcDecl.stripPrefix("explicit "))
6879 {
6880 spec.setExplicit(true);
6881 done=false;
6882 }
6883 if (funcDecl.stripPrefix("mutable "))
6884 {
6885 spec.setMutable(true);
6886 done=false;
6887 }
6888 if (funcDecl.stripPrefix("thread_local "))
6889 {
6890 spec.setThreadLocal(true);
6891 done=false;
6892 }
6893 if (funcDecl.stripPrefix("virtual "))
6894 {
6895 done=false;
6896 }
6897 }
6898
6899 // delete any ; from the function declaration
6900 size_t sep=0;
6901 while ((sep=funcDecl.find(';'))!=DString::npos)
6902 {
6903 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
6904 }
6905
6906 // make sure the first character is a space to simplify searching.
6907 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6908
6909 // remove some superfluous spaces
6910 funcDecl= substitute(
6911 substitute(
6912 substitute(funcDecl,"~ ","~"),
6913 ":: ","::"
6914 ),
6915 " ::","::"
6916 ).stripWhiteSpace();
6917
6918 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6919 if (isFriend && funcDecl.startsWith("class "))
6920 {
6921 //printf("friend class\n");
6922 funcDecl=funcDecl.mid(6);
6923 funcName = funcDecl;
6924 }
6925 else if (isFriend && funcDecl.startsWith("struct "))
6926 {
6927 funcDecl=funcDecl.mid(7);
6928 funcName = funcDecl;
6929 }
6930 else
6931 {
6932 // extract information from the declarations
6933 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6934 funcArgs,funcTempList,exceptions
6935 );
6936 }
6937
6938 // the class name can also be a namespace name, we decide this later.
6939 // if a related class name is specified and the class name could
6940 // not be derived from the function declaration, then use the
6941 // related field.
6942 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6943 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6944 if (!relates.empty())
6945 { // related member, prefix user specified scope
6946 isRelated=true;
6947 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6948 if (getClass(relates)==nullptr && !scopeName.empty())
6949 {
6950 scopeName= mergeScopes(scopeName,relates);
6951 }
6952 else
6953 {
6954 scopeName = relates;
6955 }
6956 }
6957
6958 if (relates.empty() && root->parent() &&
6959 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6960 !root->parent()->name.empty()) // see if we can combine scopeName
6961 // with the scope in which it was found
6962 {
6963 DString joinedName = root->parent()->name+"::"+scopeName;
6964 if (!scopeName.empty() &&
6965 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6966 {
6967 scopeName = joinedName;
6968 }
6969 else
6970 {
6971 scopeName = mergeScopes(root->parent()->name,scopeName);
6972 }
6973 }
6974 else // see if we can prefix a namespace or class that is used from the file
6975 {
6976 FileDef *fd=root->fileDef();
6977 if (fd)
6978 {
6979 for (const auto &fnd : fd->getUsedNamespaces())
6980 {
6981 DString joinedName = fnd->name()+"::"+scopeName;
6982 if (Doxygen::namespaceLinkedMap->find(joinedName))
6983 {
6984 scopeName=joinedName;
6985 break;
6986 }
6987 }
6988 }
6989 }
6991 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
6992
6993 // funcSpec contains the last template specifiers of the given scope.
6994 // If this method does not have any template arguments or they are
6995 // empty while funcSpec is not empty we assume this is a
6996 // specialization of a method. If not, we clear the funcSpec and treat
6997 // this as a normal method of a template class.
6998 if (!(root->tArgLists.size()>0 &&
6999 root->tArgLists.front().size()==0
7000 )
7001 )
7002 {
7003 funcSpec.clear();
7004 }
7005
7006 //namespaceName=removeAnonymousScopes(namespaceName);
7007 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
7008
7009 // split scope into a namespace and a class part
7010 extractNamespaceName(scopeName,className,namespaceName,true);
7011 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
7012
7013 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
7014 // merge class and namespace scopes again
7015 scopeName.clear();
7016 if (!namespaceName.empty())
7017 {
7018 if (className.empty())
7019 {
7020 scopeName=namespaceName;
7021 }
7022 else if (!relates.empty() || // relates command with explicit scope
7023 !getClass(className)) // class name only exists in a namespace
7024 {
7025 scopeName=namespaceName+"::"+className;
7026 }
7027 else
7028 {
7029 scopeName=className;
7030 }
7031 }
7032 else if (!className.empty())
7033 {
7034 scopeName=className;
7035 }
7036 //printf("new scope='%s'\n",qPrint(scopeName));
7037
7038 DString tempScopeName=scopeName;
7039 ClassDefMutable *cd=getClassMutable(scopeName);
7040 if (cd)
7041 {
7042 if (funcSpec.empty())
7043 {
7044 uint32_t argListIndex=0;
7045 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7046 }
7047 else
7048 {
7049 tempScopeName=scopeName+funcSpec;
7050 }
7051 }
7052 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7053 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7054
7055 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7056 // rebuild the function declaration (needed to get the scope right).
7057 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7058 {
7059 if (!funcType.empty())
7060 {
7061 if (isFunc) // a function -> we use argList for the arguments
7062 {
7063 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7064 }
7065 else
7066 {
7067 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7068 }
7069 }
7070 else
7071 {
7072 if (isFunc) // a function => we use argList for the arguments
7073 {
7074 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7075 }
7076 else // variable => add 'argument' list
7077 {
7078 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7079 }
7080 }
7081 }
7082 else // build declaration without scope
7083 {
7084 if (!funcType.empty()) // but with a type
7085 {
7086 if (isFunc) // function => omit argument list
7087 {
7088 funcDecl=funcType+" "+funcName+funcTempList;
7089 }
7090 else // variable => add 'argument' list
7091 {
7092 funcDecl=funcType+" "+funcName+funcArgs;
7093 }
7094 }
7095 else // no type
7096 {
7097 if (isFunc)
7098 {
7099 funcDecl=funcName+funcTempList;
7100 }
7101 else
7102 {
7103 funcDecl=funcName+funcArgs;
7104 }
7105 }
7106 }
7107
7108 if (funcType=="template class" && !funcTempList.empty())
7109 return; // ignore explicit template instantiations
7110
7111 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7112 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7113 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7114 namespaceName, className, funcType, funcSpec,
7115 funcName, funcArgs, funcTempList, funcDecl, relates,
7116 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7117
7118 if (!funcName.empty()) // function name is valid
7119 {
7120 // check if 'className' is actually a scoped enum, in which case we need to
7121 // process it as a global, see issue #6471
7122 bool strongEnum = false;
7123 MemberName *mn=nullptr;
7124 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7125 {
7126 for (const auto &imd : *mn)
7127 {
7128 MemberDefMutable *md = toMemberDefMutable(imd.get());
7129 Definition *mdScope = nullptr;
7130 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7131 // need filter for the correct scope, see issue #9668
7132 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7133 {
7134 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7135 strongEnum = true;
7136 // pass the scope name name as a 'namespace' to the findGlobalMember function
7137 if (!namespaceName.empty())
7138 {
7139 namespaceName+="::"+className;
7140 }
7141 else
7142 {
7143 namespaceName=className;
7144 }
7145 }
7146 }
7147 }
7148
7149 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7150 {
7151 funcName = substitute(funcName,className+"::","");
7152 }
7153 mn = nullptr;
7154 if (!funcTempList.empty()) // try with member specialization
7155 {
7156 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7157 }
7158 if (mn==nullptr) // try without specialization
7159 {
7160 mn=Doxygen::memberNameLinkedMap->find(funcName);
7161 }
7162 if (!isRelated && !strongEnum && mn) // function name already found
7163 {
7164 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7165 if (!className.empty()) // class name is valid
7166 {
7167 if (funcSpec.empty()) // not a member specialization
7168 {
7169 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7170 funcArgs,funcTempList,exceptions,
7171 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7172 }
7173 else if (cd) // member specialization
7174 {
7175 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7176 }
7177 else
7178 {
7179 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7180 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7181 }
7182 }
7183 else if (overloaded) // check if the function belongs to only one class
7184 {
7185 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7186 }
7187 else // unrelated function with the same name as a member
7188 {
7189 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7190 {
7191 DString fullFuncDecl=funcDecl;
7192 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7193 warn(root->fileName,root->startLine,
7194 "Cannot determine class for function\n{}",
7195 fullFuncDecl
7196 );
7197 }
7198 }
7199 }
7200 else if (isRelated && !relates.empty())
7201 {
7202 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7203 if (className.empty()) className=relates;
7204 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7205 if ((cd=getClassMutable(scopeName)))
7206 {
7207 bool newMember=true; // assume we have a new member
7208 MemberDefMutable *mdDefine=nullptr;
7209 {
7210 mn = Doxygen::functionNameLinkedMap->find(funcName);
7211 if (mn)
7212 {
7213 for (const auto &imd : *mn)
7214 {
7215 MemberDefMutable *md = toMemberDefMutable(imd.get());
7216 if (md && md->isDefine())
7217 {
7218 mdDefine = md;
7219 break;
7220 }
7221 }
7222 }
7223 }
7224
7225 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7226 {
7227 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7228
7229 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7230 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7231 // insert it as a class member
7232 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7233 {
7234 mn=Doxygen::memberNameLinkedMap->add(funcName);
7235 }
7236
7237 if (mdDefine->getFileDef())
7238 {
7239 mdDefine->getFileDef()->removeMember(mdDefine);
7240 }
7241 mdDefine->makeRelated();
7242 mdDefine->setMemberClass(cd);
7243 mdDefine->moveTo(cd);
7244 cd->insertMember(mdDefine);
7245 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7246 insertMemberAlias(cd->getOuterScope(),mdDefine);
7247 mn->push_back(std::move(mdDefineTaken));
7248 }
7249 else // normal member, needs to be created and added to the class
7250 {
7251 FileDef *fd=root->fileDef();
7252
7253 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7254 {
7255 mn=Doxygen::memberNameLinkedMap->add(funcName);
7256 }
7257 else
7258 {
7259 // see if we got another member with matching arguments
7260 MemberDefMutable *rmd_found = nullptr;
7261 for (const auto &irmd : *mn)
7262 {
7263 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7264 if (rmd)
7265 {
7266 const ArgumentList &rmdAl = rmd->argumentList();
7267
7268 newMember=
7269 className!=rmd->getOuterScope()->name() ||
7270 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7271 cd,fd,root->type,&root->argList,
7272 true,root->lang);
7273 if (!newMember)
7274 {
7275 rmd_found = rmd;
7276 }
7277 }
7278 }
7279 if (rmd_found) // member already exists as rmd -> add docs
7280 {
7281 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7282 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7283 newMember=false;
7284 }
7285 }
7286
7287 if (newMember) // need to create a new member
7288 {
7289 MemberType mtype = MemberType::Function;
7290 switch (root->mtype)
7291 {
7292 case MethodTypes::Method: mtype = MemberType::Function; break;
7293 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7294 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7295 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7296 case MethodTypes::Property: mtype = MemberType::Property; break;
7297 case MethodTypes::Event: mtype = MemberType::Event; break;
7298 }
7299
7300 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7301 // root->argList ? (int)root->argList->count() : -1);
7302
7303 // first note that we pass:
7304 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7305 // for the template arguments for the new "member."
7306 // this accurately reflects the template arguments of
7307 // the related function, which don't have to do with
7308 // those of the related class.
7309 auto md = createMemberDef(
7310 root->fileName,root->startLine,root->startColumn,
7311 funcType,funcName,funcArgs,exceptions,
7312 root->protection,root->virt,
7313 root->isStatic,
7314 isMemberOf ? Relationship::Foreign : Relationship::Related,
7315 mtype,
7316 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7317 funcArgs.empty() ? ArgumentList() : root->argList,
7318 root->metaData);
7319 auto mmd = toMemberDefMutable(md.get());
7320
7321 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7322 insertMemberAlias(cd->getOuterScope(),md.get());
7323
7324 // we still have the problem that
7325 // MemberDef::writeDocumentation() in memberdef.cpp
7326 // writes the template argument list for the class,
7327 // as if this member is a member of the class.
7328 // fortunately, MemberDef::writeDocumentation() has
7329 // a special mechanism that allows us to totally
7330 // override the set of template argument lists that
7331 // are printed. We use that and set it to the
7332 // template argument lists of the related function.
7333 //
7334 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7335
7336 mmd->setTagInfo(root->tagInfo());
7337
7338 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7339 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7340
7341 // try to find the matching line number of the body from the
7342 // global function list
7343 bool found=false;
7344 if (root->bodyLine==-1)
7345 {
7347 if (rmn)
7348 {
7349 const MemberDefMutable *rmd_found=nullptr;
7350 for (const auto &irmd : *rmn)
7351 {
7352 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7353 if (rmd)
7354 {
7355 const ArgumentList &rmdAl = rmd->argumentList();
7356 // check for matching argument lists
7357 if (
7358 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7359 cd,fd,root->type,&root->argList,
7360 true,root->lang)
7361 )
7362 {
7363 found=true;
7364 rmd_found = rmd;
7365 break;
7366 }
7367 }
7368 }
7369 if (rmd_found) // member found -> copy line number info
7370 {
7371 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7372 mmd->setBodyDef(rmd_found->getBodyDef());
7373 //md->setBodyMember(rmd);
7374 }
7375 }
7376 }
7377 if (!found) // line number could not be found or is available in this
7378 // entry
7379 {
7380 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7381 mmd->setBodyDef(fd);
7382 }
7383
7384 //if (root->mGrpId!=-1)
7385 //{
7386 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7387 //}
7388 mmd->setMemberClass(cd);
7389 mmd->setMemberSpecifiers(spec);
7390 mmd->setVhdlSpecifiers(root->vhdlSpec);
7391 mmd->setDefinition(funcDecl);
7393 mmd->addQualifiers(root->qualifiers);
7394 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7395 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7396 mmd->setDocsForDefinition(!root->proto);
7397 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7398 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7399 mmd->addSectionsToDefinition(root->anchors);
7400 mmd->setMemberGroupId(root->mGrpId);
7401 mmd->setLanguage(root->lang);
7402 mmd->setId(root->id);
7403 //md->setMemberDefTemplateArguments(root->mtArgList);
7404 cd->insertMember(md.get());
7405 cd->insertUsedFile(fd);
7406 mmd->setRefItems(root->sli);
7407 mmd->setRequirementReferences(root->rqli);
7408 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7409 addMemberToGroups(root,md.get());
7411 //printf("Adding member=%s\n",qPrint(md->name()));
7412 mn->push_back(std::move(md));
7413 }
7414 if (root->relatesType==RelatesType::Duplicate)
7415 {
7416 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7417 {
7418 DString fullFuncDecl=funcDecl;
7419 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7420 warn(root->fileName,root->startLine,
7421 "Cannot determine file/namespace for relatedalso function\n{}",
7422 fullFuncDecl
7423 );
7424 }
7425 }
7426 }
7427 }
7428 else
7429 {
7430 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7431 }
7432 }
7433 else if (root->parent() && root->parent()->section.isObjcImpl())
7434 {
7435 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7436 }
7437 else // unrelated not overloaded member found
7438 {
7439 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7440 if (className.empty() && !globMem)
7441 {
7442 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7443 }
7444 else if (!className.empty() && !globMem)
7445 {
7446 warn(root->fileName,root->startLine,
7447 "member '{}' of class '{}' cannot be found",
7448 funcName,className);
7449 }
7450 }
7451 }
7452 else
7453 {
7454 // this should not be called
7455 warn(root->fileName,root->startLine,"member with no name found.");
7456 }
7457 return;
7458}
7459
7460//----------------------------------------------------------------------
7461// find the members corresponding to the different documentation blocks
7462// that are extracted from the sources.
7463
7464static void filterMemberDocumentation(const Entry *root,const DString &relates)
7465{
7466 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7467 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7468 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7469 bool isFunc=true;
7470
7471 DString type = root->type;
7472 DString args = root->args;
7473 int i=-1, l=0;
7474 if ( // detect func variable/typedef to func ptr
7475 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7476 )
7477 {
7478 //printf("Fixing function pointer!\n");
7479 // fix type and argument
7480 args.prepend(type.mid(i+l));
7481 type=type.left(i+l);
7482 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7483 isFunc=false;
7484 }
7485 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7486 // detect function types marked as functions
7487 {
7488 isFunc=false;
7489 }
7490
7491 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7492 if (root->section.isMemberDoc())
7493 {
7494 //printf("Documentation for inline member '%s' found args='%s'\n",
7495 // qPrint(root->name),qPrint(args));
7496 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7497 if (type.empty())
7498 {
7499 findMember(root,
7500 relates,
7501 type,
7502 args,
7503 root->name + args + root->exception,
7504 false,
7505 isFunc);
7506 }
7507 else
7508 {
7509 findMember(root,
7510 relates,
7511 type,
7512 args,
7513 type + " " + root->name + args + root->exception,
7514 false,
7515 isFunc);
7516 }
7517 }
7518 else if (root->section.isOverloadDoc())
7519 {
7520 //printf("Overloaded member %s found\n",qPrint(root->name));
7521 findMember(root,
7522 relates,
7523 type,
7524 args,
7525 root->name,
7526 true,
7527 isFunc);
7528 }
7529 else if
7530 ((root->section.isFunction() // function
7531 ||
7532 (root->section.isVariable() && // variable
7533 !type.empty() && // with a type
7534 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7535 // (to skip forward declaration of class etc.)
7536 )
7537 )
7538 )
7539 {
7540 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7541 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7542 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7543 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7544 if (type=="friend class" || type=="friend struct" ||
7545 type=="friend union")
7546 {
7547 findMember(root,
7548 relates,
7549 type,
7550 args,
7551 type+" "+root->name,
7552 false,false);
7553
7554 }
7555 else if (!type.empty())
7556 {
7557 findMember(root,
7558 relates,
7559 type,
7560 args,
7561 type+" "+ root->inside + root->name + args + root->exception,
7562 false,isFunc);
7563 }
7564 else
7565 {
7566 findMember(root,
7567 relates,
7568 type,
7569 args,
7570 root->inside + root->name + args + root->exception,
7571 false,isFunc);
7572 }
7573 }
7574 else if (root->section.isDefine() && !relates.empty())
7575 {
7576 findMember(root,
7577 relates,
7578 type,
7579 args,
7580 root->name + args,
7581 false,
7582 !args.empty());
7583 }
7584 else if (root->section.isVariableDoc())
7585 {
7586 //printf("Documentation for variable %s found\n",qPrint(root->name));
7587 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7588 findMember(root,
7589 relates,
7590 type,
7591 args,
7592 root->name,
7593 false,
7594 false);
7595 }
7596 else if (root->section.isExportedInterface() ||
7597 root->section.isIncludedService())
7598 {
7599 findMember(root,
7600 relates,
7601 type,
7602 args,
7603 type + " " + root->name,
7604 false,
7605 false);
7606 }
7607 else
7608 {
7609 // skip section
7610 //printf("skip section\n");
7611 }
7612}
7613
7614static void findMemberDocumentation(const Entry *root)
7615{
7616 if (root->section.isMemberDoc() ||
7617 root->section.isOverloadDoc() ||
7618 root->section.isFunction() ||
7619 root->section.isVariable() ||
7620 root->section.isVariableDoc() ||
7621 root->section.isDefine() ||
7622 root->section.isIncludedService() ||
7623 root->section.isExportedInterface()
7624 )
7625 {
7626 AUTO_TRACE();
7627 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7628 {
7630 }
7632 }
7633 for (const auto &e : root->children())
7634 {
7635 if (!e->section.isEnum())
7636 {
7637 findMemberDocumentation(e.get());
7638 }
7639 }
7640}
7641
7642//----------------------------------------------------------------------
7643
7644static void findObjCMethodDefinitions(const Entry *root)
7645{
7646 AUTO_TRACE();
7647 for (const auto &objCImpl : root->children())
7648 {
7649 if (objCImpl->section.isObjcImpl())
7650 {
7651 for (const auto &objCMethod : objCImpl->children())
7652 {
7653 if (objCMethod->section.isFunction())
7654 {
7655 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7656 findMember(objCMethod.get(),
7657 objCMethod->relates,
7658 objCMethod->type,
7659 objCMethod->args,
7660 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7661 false,true);
7662 objCMethod->section=EntryType::makeEmpty();
7663 }
7664 }
7665 }
7666 }
7667}
7668
7669//----------------------------------------------------------------------
7670// find and add the enumeration to their classes, namespaces or files
7671
7672static void findEnums(const Entry *root)
7673{
7674 if (root->section.isEnum())
7675 {
7676 AUTO_TRACE("name={}",root->name);
7677 ClassDefMutable *cd = nullptr;
7678 FileDef *fd = nullptr;
7679 NamespaceDefMutable *nd = nullptr;
7680 MemberNameLinkedMap *mnsd = nullptr;
7681 bool isGlobal = false;
7682 bool isRelated = false;
7683 bool isMemberOf = false;
7684 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7685
7686 DString name;
7687 DString scope;
7688
7689 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7690 {
7691 scope=root->name.left(i); // extract scope
7692 if (root->lang==SrcLangExt::CSharp)
7693 {
7694 scope = mangleCSharpGenericName(scope);
7695 }
7696 name=root->name.right(root->name.length()-i-2); // extract name
7697 if ((cd=getClassMutable(scope))==nullptr)
7698 {
7700 }
7701 }
7702 else // no scope, check the scope in which the docs where found
7703 {
7704 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7705 {
7706 scope=root->parent()->name;
7707 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7708 }
7709 name=root->name;
7710 }
7711
7712 if (!root->relates.empty())
7713 { // related member, prefix user specified scope
7714 isRelated=true;
7715 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7716 if (getClass(root->relates)==nullptr && !scope.empty())
7717 scope=mergeScopes(scope,root->relates);
7718 else
7719 scope=root->relates;
7720 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7721 }
7722
7723 if (cd && !name.empty()) // found a enum inside a compound
7724 {
7725 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7726 fd=nullptr;
7728 isGlobal=false;
7729 }
7730 else if (nd) // found enum inside namespace
7731 {
7733 isGlobal=true;
7734 }
7735 else // found a global enum
7736 {
7737 fd=root->fileDef();
7739 isGlobal=true;
7740 }
7741
7742 if (!name.empty())
7743 {
7744 // new enum type
7745 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7746 auto md = createMemberDef(
7747 root->fileName,root->startLine,root->startColumn,
7748 DString(),name,DString(),DString(),
7749 root->protection,Specifier::Normal,false,
7750 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7751 MemberType::Enumeration,
7753 auto mmd = toMemberDefMutable(md.get());
7754 mmd->setTagInfo(root->tagInfo());
7755 mmd->setLanguage(root->lang);
7756 mmd->setId(root->id);
7757 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7758 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7759 mmd->setBodyDef(root->fileDef());
7760 mmd->setMemberSpecifiers(root->spec);
7761 mmd->setVhdlSpecifiers(root->vhdlSpec);
7762 mmd->setEnumBaseType(root->args);
7763 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7764 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7765 mmd->addSectionsToDefinition(root->anchors);
7766 mmd->setMemberGroupId(root->mGrpId);
7768 mmd->addQualifiers(root->qualifiers);
7769 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7770 mmd->setRefItems(root->sli);
7771 mmd->setRequirementReferences(root->rqli);
7772 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7773 bool defSet=false;
7774
7775 DString baseType = root->args;
7776 if (!baseType.empty())
7777 {
7778 baseType.prepend(" : ");
7779 }
7780
7781 if (nd)
7782 {
7783 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7784 {
7785 mmd->setDefinition(name+baseType);
7786 }
7787 else
7788 {
7789 mmd->setDefinition(nd->name()+"::"+name+baseType);
7790 }
7791 //printf("definition=%s\n",md->definition());
7792 defSet=true;
7793 mmd->setNamespace(nd);
7794 nd->insertMember(md.get());
7795 }
7796
7797 // even if we have already added the enum to a namespace, we still
7798 // also want to add it to other appropriate places such as file
7799 // or class.
7800 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7801 {
7802 if (!defSet) mmd->setDefinition(name+baseType);
7803 if (fd==nullptr && root->parent())
7804 {
7805 fd=root->parent()->fileDef();
7806 }
7807 if (fd)
7808 {
7809 mmd->setFileDef(fd);
7810 fd->insertMember(md.get());
7811 }
7812 }
7813 else if (cd)
7814 {
7815 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7816 {
7817 mmd->setDefinition(name+baseType);
7818 }
7819 else
7820 {
7821 mmd->setDefinition(cd->name()+"::"+name+baseType);
7822 }
7823 cd->insertMember(md.get());
7824 cd->insertUsedFile(fd);
7825 }
7826 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7827 mmd->setDocsForDefinition(!root->proto);
7828 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7829 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7830
7831 //printf("Adding member=%s\n",qPrint(md->name()));
7832 addMemberToGroups(root,md.get());
7834
7835 MemberName *mn = mnsd->add(name);
7836 mn->push_back(std::move(md));
7837 }
7838 }
7839 else
7840 {
7841 for (const auto &e : root->children()) findEnums(e.get());
7842 }
7843}
7844
7845//----------------------------------------------------------------------
7846
7847static void addEnumValuesToEnums(const Entry *root)
7848{
7849 if (root->section.isEnum())
7850 // non anonymous enumeration
7851 {
7852 AUTO_TRACE("name={}",root->name);
7853 ClassDefMutable *cd = nullptr;
7854 FileDef *fd = nullptr;
7855 NamespaceDefMutable *nd = nullptr;
7856 MemberNameLinkedMap *mnsd = nullptr;
7857 bool isGlobal = false;
7858 bool isRelated = false;
7859 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7860
7861 DString name;
7862 DString scope;
7863
7864 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7865 {
7866 scope=root->name.left(i); // extract scope
7867 if (root->lang==SrcLangExt::CSharp)
7868 {
7869 scope = mangleCSharpGenericName(scope);
7870 }
7871 name=root->name.right(root->name.length()-i-2); // extract name
7872 if ((cd=getClassMutable(scope))==nullptr)
7873 {
7875 }
7876 }
7877 else // no scope, check the scope in which the docs where found
7878 {
7879 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7880 {
7881 scope=root->parent()->name;
7882 if (root->lang==SrcLangExt::CSharp)
7883 {
7884 scope = mangleCSharpGenericName(scope);
7885 }
7886 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7887 }
7888 name=root->name;
7889 }
7890
7891 if (!root->relates.empty())
7892 { // related member, prefix user specified scope
7893 isRelated=true;
7894 if (getClassMutable(root->relates)==nullptr && !scope.empty())
7895 scope=mergeScopes(scope,root->relates);
7896 else
7897 scope=root->relates;
7898 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7899 }
7900
7901 if (cd && !name.empty()) // found a enum inside a compound
7902 {
7903 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7904 fd=nullptr;
7906 isGlobal=false;
7907 }
7908 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7909 {
7910 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7912 isGlobal=true;
7913 }
7914 else // found a global enum
7915 {
7916 fd=root->fileDef();
7917 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7919 isGlobal=true;
7920 }
7921
7922 if (!name.empty())
7923 {
7924 //printf("** name=%s\n",qPrint(name));
7925 MemberName *mn = mnsd->find(name); // for all members with this name
7926 if (mn)
7927 {
7928 struct EnumValueInfo
7929 {
7930 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
7931 name(n), member(std::move(md)) {}
7932 DString name;
7933 std::unique_ptr<MemberDef> member;
7934 };
7935 std::vector< EnumValueInfo > extraMembers;
7936 // for each enum in this list
7937 for (const auto &imd : *mn)
7938 {
7939 MemberDefMutable *md = toMemberDefMutable(imd.get());
7940 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7941 if (md && md->isEnumerate() && !root->children().empty())
7942 {
7943 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7944 for (const auto &e : root->children())
7945 {
7946 SrcLangExt sle = root->lang;
7947 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7948 if ( isJavaLike || root->spec.isStrong())
7949 {
7950 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7951 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7952 // values are only visible inside the enum scope, so we must create
7953 // them here and only add them to the enum
7954 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7955 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7956 DString qualifiedName = root->name;
7957 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
7958 {
7959 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7960 }
7961 if (isJavaLike)
7962 {
7963 qualifiedName=substitute(qualifiedName,"::",".");
7964 }
7965 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7966 {
7967 DString fileName = e->fileName;
7968 if (fileName.empty() && e->tagInfo())
7969 {
7970 fileName = e->tagInfo()->tagName;
7971 }
7972 AUTO_TRACE_ADD("strong enum value {}",e->name);
7973 auto fmd = createMemberDef(
7974 fileName,e->startLine,e->startColumn,
7975 e->type,e->name,e->args,DString(),
7976 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7977 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7978 auto fmmd = toMemberDefMutable(fmd.get());
7979 NamespaceDef *mnd = md->getNamespaceDef();
7980 if (md->getClassDef())
7981 fmmd->setMemberClass(md->getClassDef());
7982 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7983 fmmd->setNamespace(mnd);
7984 else if (md->getFileDef())
7985 fmmd->setFileDef(md->getFileDef());
7986 fmmd->setOuterScope(md->getOuterScope());
7987 fmmd->setTagInfo(e->tagInfo());
7988 fmmd->setLanguage(e->lang);
7989 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7990 fmmd->setBodyDef(e->fileDef());
7991 fmmd->setId(e->id);
7992 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7993 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7994 fmmd->addSectionsToDefinition(e->anchors);
7995 fmmd->setInitializer(e->initializer.str());
7996 fmmd->setMaxInitLines(e->initLines);
7997 fmmd->setMemberGroupId(e->mGrpId);
7998 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7999 fmmd->setRefItems(e->sli);
8000 fmmd->setRequirementReferences(e->rqli);
8001 fmmd->setAnchor();
8002 md->insertEnumField(fmd.get());
8003 fmmd->setEnumScope(md,true);
8004 extraMembers.emplace_back(e->name,std::move(fmd));
8005 }
8006 }
8007 else
8008 {
8009 AUTO_TRACE_ADD("enum value {}",e->name);
8010 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
8011 MemberName *fmn=nullptr;
8012 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
8013 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
8014 // get list of members with the same name as the field
8015 {
8016 for (const auto &ifmd : *fmn)
8017 {
8018 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8019 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
8020 {
8021 //printf("found enum value with same name %s in scope %s\n",
8022 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
8023 if (nd && !nd->isAnonymous())
8024 {
8025 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8026 {
8027 const NamespaceDef *fnd=fmd->getNamespaceDef();
8028 if (fnd==nd) // enum value is inside a namespace
8029 {
8030 md->insertEnumField(fmd);
8031 fmd->setEnumScope(md);
8032 }
8033 }
8034 }
8035 else if (isGlobal)
8036 {
8037 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8038 {
8039 const FileDef *ffd=fmd->getFileDef();
8040 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8041 {
8042 md->insertEnumField(fmd);
8043 fmd->setEnumScope(md);
8044 }
8045 }
8046 }
8047 else if (isRelated && cd) // reparent enum value to
8048 // match the enum's scope
8049 {
8050 md->insertEnumField(fmd); // add field def to list
8051 fmd->setEnumScope(md); // cross ref with enum name
8052 fmd->setEnumClassScope(cd); // cross ref with enum name
8053 fmd->setOuterScope(cd);
8054 fmd->makeRelated();
8055 cd->insertMember(fmd);
8056 }
8057 else
8058 {
8059 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8060 {
8061 const ClassDef *fcd=fmd->getClassDef();
8062 if (fcd==cd) // enum value is inside a class
8063 {
8064 //printf("Inserting enum field %s in enum scope %s\n",
8065 // qPrint(fmd->name()),qPrint(md->name()));
8066 md->insertEnumField(fmd); // add field def to list
8067 fmd->setEnumScope(md); // cross ref with enum name
8068 }
8069 }
8070 }
8071 }
8072 }
8073 }
8074 }
8075 }
8076 }
8077 }
8078 // move the newly added members into mn
8079 for (auto &e : extraMembers)
8080 {
8081 MemberName *emn=mnsd->add(e.name);
8082 emn->push_back(std::move(e.member));
8083 }
8084 }
8085 }
8086 }
8087 else
8088 {
8089 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8090 }
8091}
8092
8093//----------------------------------------------------------------------
8094
8095static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8096{
8097 AUTO_TRACE();
8098 // documentation outside a compound overrides the documentation inside it
8099 {
8100 md->setDocumentation(root->doc,root->docFile,root->docLine);
8101 md->setDocsForDefinition(!root->proto);
8102 }
8103
8104 // brief descriptions inside a compound override the documentation
8105 // outside it
8106 {
8107 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8108 }
8109
8110 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8111 {
8113 }
8114
8115 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8116 {
8117 md->setMemberGroupId(root->mGrpId);
8118 }
8119
8121 md->setRefItems(root->sli);
8122 md->setRequirementReferences(root->rqli);
8123
8124 const GroupDef *gd=md->getGroupDef();
8125 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8126 {
8127 addMemberToGroups(root,md);
8128 }
8130}
8131
8132//----------------------------------------------------------------------
8133// Search for the name in the associated groups. If a matching member
8134// definition exists, then add the documentation to it and return true,
8135// otherwise false.
8136
8137static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8138{
8139 for (const auto &g : root->groups)
8140 {
8141 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8142 if (gd)
8143 {
8144 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8145 if (ml)
8146 {
8147 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8148 if (md)
8149 {
8150 addEnumDocs(root,md);
8151 return true;
8152 }
8153 }
8154 }
8155 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8156 {
8157 warn(root->fileName, root->startLine,
8158 "Found non-existing group '{}' for the command '{}', ignoring command",
8159 g.groupname, Grouping::getGroupPriName( g.pri )
8160 );
8161 }
8162 }
8163
8164 return false;
8165}
8166
8167//----------------------------------------------------------------------
8168// find the documentation blocks for the enumerations
8169
8170static void findEnumDocumentation(const Entry *root)
8171{
8172 if (root->section.isEnumDoc() &&
8173 !root->name.empty() &&
8174 root->name.at(0)!='@' // skip anonymous enums
8175 )
8176 {
8177 DString name;
8178 DString scope;
8179 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8180 {
8181 name=root->name.mid(i+2); // extract name
8182 scope=root->name.left(i); // extract scope
8183 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8184 }
8185 else // just the name
8186 {
8187 name=root->name;
8188 }
8189 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8190 {
8191 if (!scope.empty()) scope.prepend("::");
8192 scope.prepend(root->parent()->name);
8193 }
8194 const ClassDef *cd = getClass(scope);
8196 const FileDef *fd = root->fileDef();
8197 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8198 name,scope,root->parent()->name,
8199 cd ? cd->name() : DString("<none>"),
8200 nd ? nd->name() : DString("<none>"),
8201 fd ? fd->name() : DString("<none>"));
8202
8203 if (!name.empty())
8204 {
8205 bool found = tryAddEnumDocsToGroupMember(root, name);
8206 if (!found)
8207 {
8209 if (mn)
8210 {
8211 for (const auto &imd : *mn)
8212 {
8213 MemberDefMutable *md = toMemberDefMutable(imd.get());
8214 if (md && md->isEnumerate())
8215 {
8216 const ClassDef *mcd = md->getClassDef();
8217 const NamespaceDef *mnd = md->getNamespaceDef();
8218 const FileDef *mfd = md->getFileDef();
8219 if (cd && mcd==cd)
8220 {
8221 AUTO_TRACE_ADD("Match found for class scope");
8222 addEnumDocs(root,md);
8223 found = true;
8224 break;
8225 }
8226 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8227 {
8228 AUTO_TRACE_ADD("Match found for namespace scope");
8229 addEnumDocs(root,md);
8230 found = true;
8231 break;
8232 }
8233 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8234 {
8235 AUTO_TRACE_ADD("Match found for global scope");
8236 addEnumDocs(root,md);
8237 found = true;
8238 break;
8239 }
8240 }
8241 }
8242 }
8243 }
8244 if (!found)
8245 {
8246 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8247 }
8248 }
8249 }
8250 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8251}
8252
8253// search for each enum (member or function) in mnl if it has documented
8254// enum values.
8255static void findDEV(const MemberNameLinkedMap &mnsd)
8256{
8257 // for each member name
8258 for (const auto &mn : mnsd)
8259 {
8260 // for each member definition
8261 for (const auto &imd : *mn)
8262 {
8263 MemberDefMutable *md = toMemberDefMutable(imd.get());
8264 if (md && md->isEnumerate()) // member is an enum
8265 {
8266 int documentedEnumValues=0;
8267 // for each enum value
8268 for (const auto &fmd : md->enumFieldList())
8269 {
8270 if (fmd->isLinkableInProject()) documentedEnumValues++;
8271 }
8272 // at least one enum value is documented
8273 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8274 }
8275 }
8276 }
8277}
8278
8279// search for each enum (member or function) if it has documented enum
8280// values.
8286
8287//----------------------------------------------------------------------
8288
8290{
8291 auto &index = Index::instance();
8292 // for each class member name
8293 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8294 {
8295 // for each member definition
8296 for (const auto &md : *mn)
8297 {
8298 index.addClassMemberNameToIndex(md.get());
8299 if (md->getModuleDef())
8300 {
8301 index.addModuleMemberNameToIndex(md.get());
8302 }
8303 }
8304 }
8305 // for each file/namespace function name
8306 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8307 {
8308 // for each member definition
8309 for (const auto &md : *mn)
8310 {
8311 if (md->getNamespaceDef())
8312 {
8313 index.addNamespaceMemberNameToIndex(md.get());
8314 }
8315 else
8316 {
8317 index.addFileMemberNameToIndex(md.get());
8318 }
8319 if (md->getModuleDef())
8320 {
8321 index.addModuleMemberNameToIndex(md.get());
8322 }
8323 }
8324 }
8325
8326 index.sortMemberIndexLists();
8327}
8328
8329//----------------------------------------------------------------------
8330
8331static void addToIndices()
8332{
8333 for (const auto &cd : *Doxygen::classLinkedMap)
8334 {
8335 if (cd->isLinkableInProject())
8336 {
8337 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8338 if (Doxygen::searchIndex.enabled())
8339 {
8340 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8341 Doxygen::searchIndex.addWord(cd->localName(),true);
8342 }
8343 }
8344 }
8345
8346 for (const auto &cd : *Doxygen::conceptLinkedMap)
8347 {
8348 if (cd->isLinkableInProject())
8349 {
8350 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8351 if (Doxygen::searchIndex.enabled())
8352 {
8353 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8354 Doxygen::searchIndex.addWord(cd->localName(),true);
8355 }
8356 }
8357 }
8358
8359 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8360 {
8361 if (nd->isLinkableInProject())
8362 {
8363 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8364 if (Doxygen::searchIndex.enabled())
8365 {
8366 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8367 Doxygen::searchIndex.addWord(nd->localName(),true);
8368 }
8369 }
8370 }
8371
8372 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8373 {
8374 for (const auto &fd : *fn)
8375 {
8376 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8377 {
8378 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8379 Doxygen::searchIndex.addWord(fd->localName(),true);
8380 }
8381 }
8382 }
8383
8384 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8385 {
8387 if (Doxygen::searchIndex.enabled())
8388 {
8389 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8390 std::string s = title.str();
8391 static const reg::Ex re(R"(\a[\w-]*)");
8392 reg::Iterator it(s,re);
8394 for (; it!=end ; ++it)
8395 {
8396 const auto &match = *it;
8397 std::string matchStr = match.str();
8398 Doxygen::searchIndex.addWord(matchStr,true);
8399 }
8400 }
8401 };
8402
8403 for (const auto &gd : *Doxygen::groupLinkedMap)
8404 {
8405 if (gd->isLinkableInProject())
8406 {
8407 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8408 }
8409 }
8410
8411 for (const auto &pd : *Doxygen::pageLinkedMap)
8412 {
8413 if (pd->isLinkableInProject())
8414 {
8415 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8416 }
8417 }
8418
8420 {
8421 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8422 }
8423
8424 auto addMemberToSearchIndex = [](const MemberDef *md)
8425 {
8426 if (Doxygen::searchIndex.enabled())
8427 {
8428 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8429 DString ln=md->localName();
8430 DString qn=md->qualifiedName();
8432 if (ln!=qn)
8433 {
8435 if (md->getClassDef())
8436 {
8437 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8438 }
8439 if (md->getNamespaceDef())
8440 {
8441 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8442 }
8443 }
8444 }
8445 };
8446
8447 auto getScope = [](const MemberDef *md)
8448 {
8449 const Definition *scope = nullptr;
8450 if (md->getGroupDef()) scope = md->getGroupDef();
8451 else if (md->getClassDef()) scope = md->getClassDef();
8452 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8453 else if (md->getFileDef()) scope = md->getFileDef();
8454 return scope;
8455 };
8456
8457 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8458 {
8459 if (md->isLinkableInProject())
8460 {
8461 if (!(md->isEnumerate() && md->isAnonymous()))
8462 {
8463 Doxygen::indexList->addIndexItem(getScope(md),md);
8464 addMemberToSearchIndex(md);
8465 }
8466 if (md->isEnumerate())
8467 {
8468 for (const auto &fmd : md->enumFieldList())
8469 {
8470 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8471 addMemberToSearchIndex(fmd);
8472 }
8473 }
8474 }
8475 };
8476
8477 // for each class member name
8478 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8479 {
8480 // for each member definition
8481 for (const auto &md : *mn)
8482 {
8483 addMemberToIndices(md.get());
8484 }
8485 }
8486 // for each file/namespace function name
8487 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8488 {
8489 // for each member definition
8490 for (const auto &md : *mn)
8491 {
8492 addMemberToIndices(md.get());
8493 }
8494 }
8495}
8496
8497//----------------------------------------------------------------------
8498
8500{
8501 // for each member name
8502 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8503 {
8504 // for each member definition
8505 for (const auto &imd : *mn)
8506 {
8507 MemberDefMutable *md = toMemberDefMutable(imd.get());
8508 if (md)
8509 {
8511 }
8512 }
8513 }
8514 // for each member name
8515 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8516 {
8517 // for each member definition
8518 for (const auto &imd : *mn)
8519 {
8520 MemberDefMutable *md = toMemberDefMutable(imd.get());
8521 if (md)
8522 {
8524 }
8525 }
8526 }
8527}
8528
8529// recursive helper function looking for reimplements/implemented
8530// by relations between class cd and direct or indirect base class bcd
8532{
8533 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8534 {
8535 for (const auto &imd : *mn) // for each member with a given name
8536 {
8537 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8538 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8539 {
8540 ClassDef *mbcd = bcd->classDef;
8541 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8542 {
8543 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8544 const auto &bmni = bmn.find(mn->memberName());
8545 if (bmni) // there are base class members with the same name
8546 {
8547 for (const auto &ibmd : *bmni) // for base class member with that name
8548 {
8549 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8550 if (bmd) // not part of an inline namespace
8551 {
8552 auto lang = bmd->getLanguage();
8553 auto compType = mbcd->compoundType();
8554 if (bmd->virtualness()!=Specifier::Normal ||
8555 lang==SrcLangExt::Python ||
8556 lang==SrcLangExt::Java ||
8557 lang==SrcLangExt::PHP ||
8558 compType==ClassDef::Interface ||
8559 compType==ClassDef::Protocol)
8560 {
8561 const ArgumentList &bmdAl = bmd->argumentList();
8562 const ArgumentList &mdAl = md->argumentList();
8563 //printf(" Base argList='%s'\n Super argList='%s'\n",
8564 // qPrint(argListToString(bmdAl)),
8565 // qPrint(argListToString(mdAl))
8566 // );
8567 if (
8568 lang==SrcLangExt::Python ||
8569 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8570 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8571 true,lang
8572 )
8573 )
8574 {
8575 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8576 //printf("match!\n");
8577 const MemberDef *rmd = md->reimplements();
8578 if (rmd==nullptr) // not already assigned
8579 {
8580 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8581 md->setReimplements(bmd);
8582 }
8583 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8584 bmd->insertReimplementedBy(md);
8585 }
8586 else
8587 {
8588 //printf("no match!\n");
8589 }
8590 }
8591 }
8592 }
8593 }
8594 }
8595 }
8596 }
8597 }
8598
8599 // do also for indirect base classes
8600 for (const auto &bbcd : bcd->classDef->baseClasses())
8601 {
8603 }
8604}
8605
8606//----------------------------------------------------------------------
8607// computes the relation between all members. For each member 'm'
8608// the members that override the implementation of 'm' are searched and
8609// the member that 'm' overrides is searched.
8610
8612{
8613 for (const auto &cd : *Doxygen::classLinkedMap)
8614 {
8615 if (cd->isLinkable())
8616 {
8617 for (const auto &bcd : cd->baseClasses())
8618 {
8620 }
8621 }
8622 }
8623}
8624
8625//----------------------------------------------------------------------------
8626
8628{
8629 // for each class
8630 for (const auto &cd : *Doxygen::classLinkedMap)
8631 {
8632 // that is a template
8633 for (const auto &ti : cd->getTemplateInstances())
8634 {
8635 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8636 if (tcdm)
8637 {
8638 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8639 }
8640 }
8641 }
8642}
8643
8644//----------------------------------------------------------------------------
8645
8646static void mergeCategories()
8647{
8648 AUTO_TRACE();
8649 // merge members of categories into the class they extend
8650 for (const auto &cd : *Doxygen::classLinkedMap)
8651 {
8652 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8653 {
8654 DString baseName=cd->name().left(i);
8655 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8656 if (baseClass)
8657 {
8658 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8659 baseClass->mergeCategory(cd.get());
8660 }
8661 }
8662 }
8663}
8664
8665// builds the list of all members for each class
8666
8668{
8669 // merge the member list of base classes into the inherited classes.
8670 for (const auto &cd : *Doxygen::classLinkedMap)
8671 {
8672 if (// !cd->isReference() && // not an external class
8673 cd->subClasses().empty() && // is a root of the hierarchy
8674 !cd->baseClasses().empty()) // and has at least one base class
8675 {
8676 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8677 if (cdm)
8678 {
8679 //printf("*** merging members for %s\n",qPrint(cd->name()));
8680 cdm->mergeMembers();
8681 }
8682 }
8683 }
8684 // now sort the member list of all members for all classes.
8685 for (const auto &cd : *Doxygen::classLinkedMap)
8686 {
8687 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8688 if (cdm)
8689 {
8690 cdm->sortAllMembersList();
8691 }
8692 }
8693}
8694
8695//----------------------------------------------------------------------------
8696
8698{
8699 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8700 {
8701 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8702 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8703 if (showSources)
8704 {
8705 msg("Generating code for file {}...\n",fd->docName());
8706 fd->writeSourceHeader(ol);
8707 fd->writeSourceBody(ol,parser);
8708 fd->writeSourceFooter(ol);
8709 }
8710 else if (parseSources)
8711 {
8712 msg("Parsing code for file {}...\n",fd->docName());
8713 fd->parseSource(parser);
8714 }
8715 };
8716 if (!Doxygen::inputNameLinkedMap->empty())
8717 {
8718#if USE_LIBCLANG
8720 {
8721 StringUnorderedSet processedFiles;
8722
8723 // create a dictionary with files to process
8724 StringUnorderedSet filesToProcess;
8725
8726 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8727 {
8728 for (const auto &fd : *fn)
8729 {
8730 filesToProcess.insert(fd->absFilePath().str());
8731 }
8732 }
8733 // process source files (and their include dependencies)
8734 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8735 {
8736 for (const auto &fd : *fn)
8737 {
8738 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8739 (fd->generateSourceFile() ||
8741 )
8742 )
8743 {
8744 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8745 clangParser->parse();
8746 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8747
8748 for (auto incFile : clangParser->filesInSameTU())
8749 {
8750 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8751 fd->absFilePath()!=incFile && // not same file
8752 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8753 {
8754 StringVector moreFiles;
8755 bool ambig = false;
8757 if (ifd && !ifd->isReference())
8758 {
8759 processSourceFile(ifd,*g_outputList,clangParser.get());
8760 processedFiles.insert(incFile);
8761 }
8762 }
8763 }
8764 processedFiles.insert(fd->absFilePath().str());
8765 }
8766 }
8767 }
8768 // process remaining files
8769 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8770 {
8771 for (const auto &fd : *fn)
8772 {
8773 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8774 {
8775 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8776 {
8777 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8778 clangParser->parse();
8779 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8780 }
8781 else // non C/C++ file, use built-in parser
8782 {
8783 processSourceFile(fd.get(),*g_outputList,nullptr);
8784 }
8785 }
8786 }
8787 }
8788 }
8789 else
8790#endif
8791 {
8792 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8793 if (numThreads>1)
8794 {
8795 msg("Generating code files using {} threads.\n",numThreads);
8796 struct SourceContext
8797 {
8798 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8799 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8800 FileDef *fd;
8801 bool generateSourceFile;
8802 OutputList ol;
8803 };
8804 ThreadPool threadPool(numThreads);
8805 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8806 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8807 {
8808 for (const auto &fd : *fn)
8809 {
8810 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8811 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8812 auto processFile = [ctx]()
8813 {
8814 if (ctx->generateSourceFile)
8815 {
8816 msg("Generating code for file {}...\n",ctx->fd->docName());
8817 }
8818 else
8819 {
8820 msg("Parsing code for file {}...\n",ctx->fd->docName());
8821 }
8822 StringVector filesInSameTu;
8823 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8824 if (ctx->generateSourceFile) // sources need to be shown in the output
8825 {
8826 ctx->fd->writeSourceHeader(ctx->ol);
8827 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8828 ctx->fd->writeSourceFooter(ctx->ol);
8829 }
8830 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8831 // we needed to parse the sources even if we do not show them
8832 {
8833 ctx->fd->parseSource(nullptr);
8834 }
8835 return ctx;
8836 };
8837 results.emplace_back(threadPool.queue(processFile));
8838 }
8839 }
8840 for (auto &f : results)
8841 {
8842 auto ctx = f.get();
8843 }
8844 }
8845 else // single threaded version
8846 {
8847 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8848 {
8849 for (const auto &fd : *fn)
8850 {
8851 StringVector filesInSameTu;
8852 fd->getAllIncludeFilesRecursively(filesInSameTu);
8853 processSourceFile(fd.get(),*g_outputList,nullptr);
8854 }
8855 }
8856 }
8857 }
8858 }
8859}
8860
8861//----------------------------------------------------------------------------
8862
8863static void generateFileDocs()
8864{
8865 if (Index::instance().numDocumentedFiles()==0) return;
8866
8867 if (!Doxygen::inputNameLinkedMap->empty())
8868 {
8869 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8870 if (numThreads>1) // multi threaded processing
8871 {
8872 struct DocContext
8873 {
8874 DocContext(FileDef *fd_,const OutputList &ol_)
8875 : fd(fd_), ol(ol_) {}
8876 FileDef *fd;
8877 OutputList ol;
8878 };
8879 ThreadPool threadPool(numThreads);
8880 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8881 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8882 {
8883 for (const auto &fd : *fn)
8884 {
8885 bool doc = fd->isLinkableInProject();
8886 if (doc)
8887 {
8888 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8889 auto processFile = [ctx]() {
8890 msg("Generating docs for file {}...\n",ctx->fd->docName());
8891 ctx->fd->writeDocumentation(ctx->ol);
8892 return ctx;
8893 };
8894 results.emplace_back(threadPool.queue(processFile));
8895 }
8896 }
8897 }
8898 for (auto &f : results)
8899 {
8900 auto ctx = f.get();
8901 }
8902 }
8903 else // single threaded processing
8904 {
8905 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8906 {
8907 for (const auto &fd : *fn)
8908 {
8909 bool doc = fd->isLinkableInProject();
8910 if (doc)
8911 {
8912 msg("Generating docs for file {}...\n",fd->docName());
8913 fd->writeDocumentation(*g_outputList);
8914 }
8915 }
8916 }
8917 }
8918 }
8919}
8920
8921//----------------------------------------------------------------------------
8922
8924{
8925 // add source references for class definitions
8926 for (const auto &cd : *Doxygen::classLinkedMap)
8927 {
8928 const FileDef *fd=cd->getBodyDef();
8929 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8930 {
8931 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8932 }
8933 }
8934 // add source references for concept definitions
8935 for (const auto &cd : *Doxygen::conceptLinkedMap)
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 namespace definitions
8944 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8945 {
8946 const FileDef *fd=nd->getBodyDef();
8947 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8948 {
8949 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8950 }
8951 }
8952
8953 // add source references for member names
8954 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8955 {
8956 for (const auto &md : *mn)
8957 {
8958 //printf("class member %s: def=%s body=%d link?=%d\n",
8959 // qPrint(md->name()),
8960 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8961 // md->getStartBodyLine(),md->isLinkableInProject());
8962 const FileDef *fd=md->getBodyDef();
8963 if (fd &&
8964 md->getStartDefLine()!=-1 &&
8965 md->isLinkableInProject() &&
8967 )
8968 {
8969 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8970 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8971 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8972 }
8973 }
8974 }
8975 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8976 {
8977 for (const auto &md : *mn)
8978 {
8979 const FileDef *fd=md->getBodyDef();
8980 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8981 // qPrint(md->name()),
8982 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8983 // md->isLinkableInProject(),
8984 // Doxygen::parseSourcesNeeded);
8985 if (fd &&
8986 md->getStartDefLine()!=-1 &&
8987 md->isLinkableInProject() &&
8989 )
8990 {
8991 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8992 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8993 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8994 }
8995 }
8996 }
8997}
8998
8999//----------------------------------------------------------------------------
9000
9001// add the macro definitions found during preprocessing as file members
9002static void buildDefineList()
9003{
9004 AUTO_TRACE();
9005 for (const auto &s : g_inputFiles)
9006 {
9007 auto it = Doxygen::macroDefinitions.find(s);
9009 {
9010 for (const auto &def : it->second)
9011 {
9012 auto md = createMemberDef(
9013 def.fileName,def.lineNr,def.columnNr,
9014 "#define",def.name,def.args,DString(),
9015 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9016 ArgumentList(),ArgumentList(),"");
9017 auto mmd = toMemberDefMutable(md.get());
9018
9019 if (!def.args.empty())
9020 {
9021 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
9022 }
9023 mmd->setInitializer(def.definition);
9024 mmd->setFileDef(def.fileDef);
9025 mmd->setDefinition("#define "+def.name);
9026
9028 if (def.fileDef)
9029 {
9030 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9031 if (defMl)
9032 {
9033 const MemberDef *defMd = defMl->findRev(def.name);
9034 if (defMd) // definition already stored
9035 {
9036 mmd->setRedefineCount(defMd->redefineCount()+1);
9037 }
9038 }
9039 def.fileDef->insertMember(md.get());
9040 }
9041 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9042 mn->push_back(std::move(md));
9043 }
9044 }
9045 }
9046}
9047
9048//----------------------------------------------------------------------------
9049
9050static void sortMemberLists()
9051{
9052 // sort class member lists
9053 for (const auto &cd : *Doxygen::classLinkedMap)
9054 {
9055 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9056 if (cdm)
9057 {
9058 cdm->sortMemberLists();
9059 }
9060 }
9061
9062 // sort namespace member lists
9063 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9064 {
9066 if (ndm)
9067 {
9068 ndm->sortMemberLists();
9069 }
9070 }
9071
9072 // sort file member lists
9073 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9074 {
9075 for (const auto &fd : *fn)
9076 {
9077 fd->sortMemberLists();
9078 }
9079 }
9080
9081 // sort group member lists
9082 for (const auto &gd : *Doxygen::groupLinkedMap)
9083 {
9084 gd->sortMemberLists();
9085 }
9086
9088}
9089
9090//----------------------------------------------------------------------------
9091
9092static bool isSymbolHidden(const Definition *d)
9093{
9094 bool hidden = d->isHidden();
9095 const Definition *parent = d->getOuterScope();
9096 return parent ? hidden || isSymbolHidden(parent) : hidden;
9097}
9098
9100{
9101 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9102 if (numThreads>1)
9103 {
9104 ThreadPool threadPool(numThreads);
9105 std::vector < std::future< void > > results;
9106 // queue the work
9107 for (const auto &[name,symList] : *Doxygen::symbolMap)
9108 {
9109 for (const auto &def : symList)
9110 {
9112 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9113 {
9114 auto processTooltip = [dm]() {
9115 dm->computeTooltip();
9116 };
9117 results.emplace_back(threadPool.queue(processTooltip));
9118 }
9119 }
9120 }
9121 // wait for the results
9122 for (auto &f : results)
9123 {
9124 f.get();
9125 }
9126 }
9127 else
9128 {
9129 for (const auto &[name,symList] : *Doxygen::symbolMap)
9130 {
9131 for (const auto &def : symList)
9132 {
9134 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9135 {
9136 dm->computeTooltip();
9137 }
9138 }
9139 }
9140 }
9141}
9142
9143//----------------------------------------------------------------------------
9144
9146{
9147 for (const auto &cd : *Doxygen::classLinkedMap)
9148 {
9149 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9150 if (cdm)
9151 {
9152 cdm->setAnonymousEnumType();
9153 }
9154 }
9155}
9156
9157//----------------------------------------------------------------------------
9158
9159static void countMembers()
9160{
9161 for (const auto &cd : *Doxygen::classLinkedMap)
9162 {
9163 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9164 if (cdm)
9165 {
9166 cdm->countMembers();
9167 }
9168 }
9169
9170 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9171 {
9173 if (ndm)
9174 {
9175 ndm->countMembers();
9176 }
9177 }
9178
9179 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9180 {
9181 for (const auto &fd : *fn)
9182 {
9183 fd->countMembers();
9184 }
9185 }
9186
9187 for (const auto &gd : *Doxygen::groupLinkedMap)
9188 {
9189 gd->countMembers();
9190 }
9191
9192 auto &mm = ModuleManager::instance();
9193 mm.countMembers();
9194}
9195
9196
9197//----------------------------------------------------------------------------
9198// generate the documentation for all classes
9199
9200static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9201{
9202 AUTO_TRACE();
9203 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9204 if (numThreads>1) // multi threaded processing
9205 {
9206 struct DocContext
9207 {
9208 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9209 : cd(cd_), ol(ol_) {}
9210 ClassDefMutable *cd;
9211 OutputList ol;
9212 };
9213 ThreadPool threadPool(numThreads);
9214 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9215 for (const auto &cd : classList)
9216 {
9217 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9218 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9219 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9220 )
9221 {
9222 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9223 auto processFile = [ctx]()
9224 {
9225 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9226
9227 // skip external references, anonymous compounds and
9228 // template instances
9229 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9230 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9231 {
9232 ctx->cd->writeDocumentation(ctx->ol);
9233 ctx->cd->writeMemberList(ctx->ol);
9234 }
9235
9236 // even for undocumented classes, the inner classes can be documented.
9237 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9238 return ctx;
9239 };
9240 results.emplace_back(threadPool.queue(processFile));
9241 }
9242 }
9243 for (auto &f : results)
9244 {
9245 auto ctx = f.get();
9246 }
9247 }
9248 else // single threaded processing
9249 {
9250 for (const auto &cd : classList)
9251 {
9252 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9253 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9254 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9255 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9256 )
9257 {
9258 // skip external references, anonymous compounds and
9259 // template instances
9260 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9261 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9262 {
9263 msg("Generating docs for compound {}...\n",cd->displayName());
9264
9265 cd->writeDocumentation(*g_outputList);
9266 cd->writeMemberList(*g_outputList);
9267 }
9268 // even for undocumented classes, the inner classes can be documented.
9269 cd->writeDocumentationForInnerClasses(*g_outputList);
9270 }
9271 }
9272 }
9273}
9274
9275static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9276{
9277 list.push_back(cd);
9278 for (const auto &innerCdi : cd->getClasses())
9279 {
9280 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9281 if (innerCd)
9282 {
9283 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9284 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9285 innerCd->isEmbeddedInOuterScope());
9286 }
9287 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9288 protectionLevelVisible(innerCd->protection()) &&
9289 !innerCd->isEmbeddedInOuterScope()
9290 )
9291 {
9292 list.push_back(innerCd);
9293 addClassAndNestedClasses(list,innerCd);
9294 }
9295 }
9296}
9297
9299{
9300 std::vector<ClassDefMutable*> classList;
9301 for (const auto &cdi : *Doxygen::classLinkedMap)
9302 {
9303 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9304 if (cd && (cd->getOuterScope()==nullptr ||
9306 {
9307 addClassAndNestedClasses(classList,cd);
9308 }
9309 }
9310 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9311 {
9312 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9313 if (cd && (cd->getOuterScope()==nullptr ||
9315 {
9316 addClassAndNestedClasses(classList,cd);
9317 }
9318 }
9319 generateDocsForClassList(classList);
9320}
9321
9322//----------------------------------------------------------------------------
9323
9325{
9326 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9327 {
9329
9330 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9331 if (cd &&
9332 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9333 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9334 ) && !cd->isHidden() && cd->isLinkableInProject()
9335 )
9336 {
9337 msg("Generating docs for concept {}...\n",cd->displayName());
9339 }
9340 }
9341}
9342
9343//----------------------------------------------------------------------------
9344
9346{
9347 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9348 {
9349 for (const auto &imd : *mn)
9350 {
9351 MemberDefMutable *md = toMemberDefMutable(imd.get());
9352 //static int count=0;
9353 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9354 if (md && md->documentation().empty() && md->briefDescription().empty())
9355 { // no documentation yet
9356 const MemberDef *bmd = md->reimplements();
9357 while (bmd && bmd->documentation().empty() &&
9358 bmd->briefDescription().empty()
9359 )
9360 { // search up the inheritance tree for a documentation member
9361 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9362 bmd = bmd->reimplements();
9363 }
9364 if (bmd) // copy the documentation from the reimplemented member
9365 {
9366 md->setInheritsDocsFrom(bmd);
9367 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9369 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9370 md->copyArgumentNames(bmd);
9372 }
9373 }
9374 }
9375 }
9376}
9377
9378//----------------------------------------------------------------------------
9379
9381{
9382 // for each file
9383 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9384 {
9385 for (const auto &fd : *fn)
9386 {
9387 fd->combineUsingRelations();
9388 }
9389 }
9390
9391 // for each namespace
9392 NamespaceDefSet visitedNamespaces;
9393 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9394 {
9396 if (ndm)
9397 {
9398 ndm->combineUsingRelations(visitedNamespaces);
9399 }
9400 }
9401}
9402
9403//----------------------------------------------------------------------------
9404
9406{
9407 // for each class
9408 for (const auto &cd : *Doxygen::classLinkedMap)
9409 {
9410 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9411 if (cdm)
9412 {
9414 }
9415 }
9416 // for each file
9417 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9418 {
9419 for (const auto &fd : *fn)
9420 {
9421 fd->addMembersToMemberGroup();
9422 }
9423 }
9424 // for each namespace
9425 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9426 {
9428 if (ndm)
9429 {
9431 }
9432 }
9433 // for each group
9434 for (const auto &gd : *Doxygen::groupLinkedMap)
9435 {
9436 gd->addMembersToMemberGroup();
9437 }
9439}
9440
9441//----------------------------------------------------------------------------
9442
9444{
9445 // for each class
9446 for (const auto &cd : *Doxygen::classLinkedMap)
9447 {
9448 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9449 if (cdm)
9450 {
9452 }
9453 }
9454 // for each file
9455 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9456 {
9457 for (const auto &fd : *fn)
9458 {
9459 fd->distributeMemberGroupDocumentation();
9460 }
9461 }
9462 // for each namespace
9463 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9464 {
9466 if (ndm)
9467 {
9469 }
9470 }
9471 // for each group
9472 for (const auto &gd : *Doxygen::groupLinkedMap)
9473 {
9474 gd->distributeMemberGroupDocumentation();
9475 }
9477}
9478
9479//----------------------------------------------------------------------------
9480
9482{
9483 // for each class
9484 for (const auto &cd : *Doxygen::classLinkedMap)
9485 {
9486 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9487 if (cdm)
9488 {
9490 }
9491 }
9492 // for each concept
9493 for (const auto &cd : *Doxygen::conceptLinkedMap)
9494 {
9495 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9496 if (cdm)
9497 {
9499 }
9500 }
9501 // for each file
9502 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9503 {
9504 for (const auto &fd : *fn)
9505 {
9506 fd->findSectionsInDocumentation();
9507 }
9508 }
9509 // for each namespace
9510 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9511 {
9513 if (ndm)
9514 {
9516 }
9517 }
9518 // for each group
9519 for (const auto &gd : *Doxygen::groupLinkedMap)
9520 {
9521 gd->findSectionsInDocumentation();
9522 }
9523 // for each page
9524 for (const auto &pd : *Doxygen::pageLinkedMap)
9525 {
9526 pd->findSectionsInDocumentation();
9527 }
9528 // for each directory
9529 for (const auto &dd : *Doxygen::dirLinkedMap)
9530 {
9531 dd->findSectionsInDocumentation();
9532 }
9534 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9535}
9536
9537//----------------------------------------------------------------------
9538
9539
9541{
9542 // remove all references to classes from the cache
9543 // as there can be new template instances in the inheritance path
9544 // to this class. Optimization: only remove those classes that
9545 // have inheritance instances as direct or indirect sub classes.
9547
9548 // remove all cached typedef resolutions whose target is a
9549 // template class as this may now be a template instance
9550 // for each global function name
9551 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9552 {
9553 // for each function with that name
9554 for (const auto &ifmd : *fn)
9555 {
9556 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9557 if (fmd && fmd->isTypedefValCached())
9558 {
9559 const ClassDef *cd = fmd->getCachedTypedefVal();
9560 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9561 }
9562 }
9563 }
9564 // for each class method name
9565 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9566 {
9567 // for each function with that name
9568 for (const auto &imd : *nm)
9569 {
9570 MemberDefMutable *md = toMemberDefMutable(imd.get());
9571 if (md && md->isTypedefValCached())
9572 {
9573 const ClassDef *cd = md->getCachedTypedefVal();
9574 if (cd->isTemplate()) md->invalidateTypedefValCache();
9575 }
9576 }
9577 }
9578}
9579
9580//----------------------------------------------------------------------------
9581
9583{
9584 // Remove all unresolved references to classes from the cache.
9585 // This is needed before resolving the inheritance relations, since
9586 // it would otherwise not find the inheritance relation
9587 // for C in the example below, as B::I was already found to be unresolvable
9588 // (which is correct if you ignore the inheritance relation between A and B).
9589 //
9590 // class A { class I {} };
9591 // class B : public A {};
9592 // class C : public B::I {};
9594
9595 // for each class method name
9596 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9597 {
9598 // for each function with that name
9599 for (const auto &imd : *nm)
9600 {
9601 MemberDefMutable *md = toMemberDefMutable(imd.get());
9602 if (md)
9603 {
9605 }
9606 }
9607 }
9608
9609}
9610
9611//----------------------------------------------------------------------------
9612// Returns true if the entry and member definition have equal file names,
9613// otherwise false.
9614
9615static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9616{
9617 if (const FileDef *fd = md->getFileDef())
9618 {
9619 return fd->absFilePath() == root->fileName;
9620 }
9621 return false;
9622}
9623
9624//----------------------------------------------------------------------------
9625
9626static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9627{
9628 md->setDocumentation(root->doc,root->docFile,root->docLine);
9629 md->setDocsForDefinition(!root->proto);
9630 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9631 if (md->inbodyDocumentation().empty())
9632 {
9634 }
9635 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9636 {
9637 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9638 md->setBodyDef(root->fileDef());
9639 }
9641 md->setMaxInitLines(root->initLines);
9643 md->setRefItems(root->sli);
9644 md->setRequirementReferences(root->rqli);
9645 md->addQualifiers(root->qualifiers);
9646 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9647 addMemberToGroups(root,md);
9649}
9650
9651//----------------------------------------------------------------------------
9652
9654{
9655 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9656 {
9657 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9658 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9659
9660 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9661 {
9662 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9663 "#define",root->name,root->args,DString(),
9664 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9665 ArgumentList(),ArgumentList(),"");
9666 auto mmd = toMemberDefMutable(md.get());
9667 mmd->setTagInfo(root->tagInfo());
9668 mmd->setLanguage(root->lang);
9669 mmd->addQualifiers(root->qualifiers);
9670 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9671 mmd->setFileDef(root->parent()->fileDef());
9672 //printf("Adding member=%s\n",qPrint(md->name()));
9674 mn->push_back(std::move(md));
9675 }
9677 if (mn)
9678 {
9679 int count=0;
9680 for (const auto &md : *mn)
9681 {
9682 if (md->memberType()==MemberType::Define) count++;
9683 }
9684 if (count==1)
9685 {
9686 for (const auto &imd : *mn)
9687 {
9688 MemberDefMutable *md = toMemberDefMutable(imd.get());
9689 if (md && md->memberType()==MemberType::Define)
9690 {
9691 addDefineDoc(root,md);
9692 }
9693 }
9694 }
9695 else if (count>1 &&
9696 (!root->doc.empty() ||
9697 !root->brief.empty() ||
9698 root->bodyLine!=-1
9699 )
9700 )
9701 // multiple defines don't know where to add docs
9702 // but maybe they are in different files together with their documentation
9703 {
9704 for (const auto &imd : *mn)
9705 {
9706 MemberDefMutable *md = toMemberDefMutable(imd.get());
9707 if (md && md->memberType()==MemberType::Define)
9708 {
9709 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9710 // doc and define in the same file or group assume they belong together.
9711 {
9712 addDefineDoc(root,md);
9713 }
9714 }
9715 }
9716 //warn("define {} found in the following files:\n",root->name);
9717 //warn("Cannot determine where to add the documentation found "
9718 // "at line {} of file {}. \n",
9719 // root->startLine,root->fileName);
9720 }
9721 }
9722 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9723 {
9724 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9725 if (preEnabled)
9726 {
9727 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9728 }
9729 else
9730 {
9731 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9732 }
9733 }
9734 }
9735 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9736}
9737
9738//----------------------------------------------------------------------------
9739
9740static void findDirDocumentation(const Entry *root)
9741{
9742 if (root->section.isDirDoc())
9743 {
9744 DString normalizedName = root->name;
9745 normalizedName = substitute(normalizedName,"\\","/");
9746 //printf("root->docFile=%s normalizedName=%s\n",
9747 // qPrint(root->docFile),qPrint(normalizedName));
9748 if (root->docFile==normalizedName) // current dir?
9749 {
9750 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9751 {
9752 normalizedName=normalizedName.left(lastSlashPos);
9753 }
9754 }
9755 if (normalizedName.at(normalizedName.length()-1)!='/')
9756 {
9757 normalizedName+='/';
9758 }
9759 DirDef *matchingDir=nullptr;
9760 for (const auto &dir : *Doxygen::dirLinkedMap)
9761 {
9762 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9763 if (dir->name().right(normalizedName.length())==normalizedName)
9764 {
9765 if (matchingDir)
9766 {
9767 warn(root->fileName,root->startLine,
9768 "\\dir command matches multiple directories.\n"
9769 " Applying the command for directory {}\n"
9770 " Ignoring the command for directory {}",
9771 matchingDir->name(),dir->name()
9772 );
9773 }
9774 else
9775 {
9776 matchingDir=dir.get();
9777 }
9778 }
9779 }
9780 if (matchingDir)
9781 {
9782 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9783 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9784 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9785 matchingDir->setRefItems(root->sli);
9786 matchingDir->setRequirementReferences(root->rqli);
9787 matchingDir->addSectionsToDefinition(root->anchors);
9788 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9789 addDirToGroups(root,matchingDir);
9790 }
9791 else
9792 {
9793 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9794 }
9795 }
9796 for (const auto &e : root->children()) findDirDocumentation(e.get());
9797}
9798
9799//----------------------------------------------------------------------------
9801{
9802 if (root->section.isRequirementDoc())
9803 {
9805 }
9806 for (const auto &e : root->children()) buildRequirementsList(e.get());
9807}
9808
9809//----------------------------------------------------------------------------
9810// create a (sorted) list of separate documentation pages
9811
9812static void buildPageList(Entry *root)
9813{
9814 if (root->section.isPageDoc())
9815 {
9816 if (!root->name.empty())
9817 {
9818 addRelatedPage(root);
9819 }
9820 }
9821 else if (root->section.isMainpageDoc())
9822 {
9823 DString title=root->args.stripWhiteSpace();
9824 if (title.empty()) title=theTranslator->trMainPage();
9825 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9826 DString name = "index";
9827 addRefItem(root->sli,
9828 name,
9829 theTranslator->trPage(true,true),
9830 name,
9831 title,
9832 DString(),nullptr
9833 );
9834 }
9835 for (const auto &e : root->children()) buildPageList(e.get());
9836}
9837
9838// search for the main page defined in this project
9839static void findMainPage(Entry *root)
9840{
9841 if (root->section.isMainpageDoc())
9842 {
9843 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9844 {
9845 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9846 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9847 DString title=root->args.stripWhiteSpace();
9848 if (title.empty()) title = Config_getString(PROJECT_NAME);
9849 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9850 DString indexName="index";
9852 indexName, root->brief+root->doc+root->inbodyDocs,title);
9853 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9854 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9855 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9856 Doxygen::mainPage->setFileName(indexName);
9857 Doxygen::mainPage->setLocalToc(root->localToc);
9859
9861 if (si)
9862 {
9863 if (!si->ref().empty()) // we are from a tag file
9864 {
9865 // a page name is a label as well! but should no be double either
9867 Doxygen::mainPage->name(),
9868 indexName,
9869 root->startLine,
9870 Doxygen::mainPage->title(),
9872 0); // level 0
9873 }
9874 else if (si->lineNr() != -1)
9875 {
9876 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9877 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9878 }
9879 else
9880 {
9881 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9882 Doxygen::mainPage->name(),si->fileName());
9883 }
9884 }
9885 else
9886 {
9887 // a page name is a label as well! but should no be double either
9889 Doxygen::mainPage->name(),
9890 indexName,
9891 root->startLine,
9892 Doxygen::mainPage->title(),
9894 0); // level 0
9895 }
9896 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9897 }
9898 else if (root->tagInfo()==nullptr)
9899 {
9900 warn(root->fileName,root->startLine,
9901 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9902 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9903 }
9904 }
9905 for (const auto &e : root->children()) findMainPage(e.get());
9906}
9907
9908// search for the main page imported via tag files and add only the section labels
9909static void findMainPageTagFiles(Entry *root)
9910{
9911 if (root->section.isMainpageDoc())
9912 {
9913 if (Doxygen::mainPage && root->tagInfo())
9914 {
9915 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9916 }
9917 }
9918 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9919}
9920
9921static void computePageRelations(Entry *root)
9922{
9923 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
9924 {
9925 PageDef *pd = root->section.isPageDoc() ?
9927 Doxygen::mainPage.get();
9928 if (pd)
9929 {
9930 for (const BaseInfo &bi : root->extends)
9931 {
9933 if (pd==subPd)
9934 {
9935 term("page defined {} with label {} is a direct "
9936 "subpage of itself! Please remove this cyclic dependency.\n",
9937 warn_line(pd->docFile(),pd->docLine()),pd->name());
9938 }
9939 else if (subPd)
9940 {
9941 pd->addInnerCompound(subPd);
9942 //printf("*** Added subpage relation: %s->%s\n",
9943 // qPrint(pd->name()),qPrint(subPd->name()));
9944 }
9945 }
9946 }
9947 }
9948 for (const auto &e : root->children()) computePageRelations(e.get());
9949}
9950
9952{
9953 for (const auto &pd : *Doxygen::pageLinkedMap)
9954 {
9955 Definition *ppd = pd->getOuterScope();
9956 while (ppd)
9957 {
9958 if (ppd==pd.get())
9959 {
9960 term("page defined {} with label {} is a subpage "
9961 "of itself! Please remove this cyclic dependency.\n",
9962 warn_line(pd->docFile(),pd->docLine()),pd->name());
9963 }
9964 ppd=ppd->getOuterScope();
9965 }
9966 }
9967}
9968
9969//----------------------------------------------------------------------------
9970
9972{
9973 for (const auto &si : SectionManager::instance())
9974 {
9975 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9976 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9977 // qPrint(si->fileName));
9978 PageDef *pd=nullptr;
9979
9980 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9981 // different files, so the resulting section's all have the wrong file
9982 // name (not from the todo/test/bug/deprecated list, but from the file in
9983 // which they are defined). We correct this here by looking at the
9984 // generated section labels!
9986 {
9987 DString label="_"+rl->listName(); // "_todo", "_test", ...
9988 if (si->label().left(label.length())==label)
9989 {
9990 si->setFileName(rl->listName());
9991 si->setGenerated(true);
9992 break;
9993 }
9994 }
9995
9996 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9997 if (!si->generated())
9998 {
9999 // if this section is in a page and the page is in a group, then we
10000 // have to adjust the link file name to point to the group.
10001 if (!si->fileName().empty() &&
10002 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
10003 pd->getGroupDef())
10004 {
10005 si->setFileName(pd->getGroupDef()->getOutputFileBase());
10006 }
10007
10008 if (si->definition())
10009 {
10010 // TODO: there should be one function in Definition that returns
10011 // the file to link to, so we can avoid the following tests.
10012 const GroupDef *gd=nullptr;
10013 if (si->definition()->definitionType()==Definition::TypeMember)
10014 {
10015 gd = (toMemberDef(si->definition()))->getGroupDef();
10016 }
10017
10018 if (gd)
10019 {
10020 si->setFileName(gd->getOutputFileBase());
10021 }
10022 else
10023 {
10024 //si->fileName=si->definition->getOutputFileBase();
10025 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
10026 }
10027 }
10028 }
10029 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10030 }
10031}
10032
10033
10034
10035//----------------------------------------------------------------------------
10036// generate all separate documentation pages
10037
10038
10039static void generatePageDocs()
10040{
10041 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10042 if (Index::instance().numDocumentedPages()==0) return;
10043 for (const auto &pd : *Doxygen::pageLinkedMap)
10044 {
10045 if (!pd->getGroupDef() && !pd->isReference())
10046 {
10047 msg("Generating docs for page {}...\n",pd->name());
10048 pd->writeDocumentation(*g_outputList);
10049 }
10050 }
10051}
10052
10053//----------------------------------------------------------------------------
10054// create a (sorted) list & dictionary of example pages
10055
10056static void buildExampleList(Entry *root)
10057{
10058 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10059 {
10060 if (Doxygen::exampleLinkedMap->find(root->name))
10061 {
10062 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10063 }
10064 else
10065 {
10067 createPageDef(root->fileName,root->startLine,
10068 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10069 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10070 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10072 pd->setLanguage(root->lang);
10073 pd->setShowLineNo(root->section.isExampleLineno());
10074
10075 //we don't add example to groups
10076 //addExampleToGroups(root,pd);
10077 }
10078 }
10079 for (const auto &e : root->children()) buildExampleList(e.get());
10080}
10081
10082//----------------------------------------------------------------------------
10083// prints the Entry tree (for debugging)
10084
10085void printNavTree(Entry *root,int indent)
10086{
10088 {
10089 DString indentStr;
10090 indentStr.fill(' ',indent);
10091 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10092 indentStr.empty()?"":indentStr,
10093 root->name.empty()?"<empty>":root->name,
10094 root->fileName,root->startLine,
10095 root->section.to_string(),
10096 root->spec.to_string());
10097 for (const auto &e : root->children())
10098 {
10099 printNavTree(e.get(),indent+2);
10100 }
10101 }
10102}
10103
10104
10105//----------------------------------------------------------------------------
10106// prints the Sections tree (for debugging)
10107
10109{
10111 {
10112 for (const auto &si : SectionManager::instance())
10113 {
10114 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10115 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10116 }
10117 }
10118}
10119
10120
10121//----------------------------------------------------------------------------
10122// generate the example documentation
10123
10125{
10127 for (const auto &pd : *Doxygen::exampleLinkedMap)
10128 {
10129 msg("Generating docs for example {}...\n",pd->name());
10130 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10131 if (lang != SrcLangExt::Unknown)
10132 {
10133 DString ext = getFileNameExtension(pd->name());
10134 auto intf = Doxygen::parserManager->getCodeParser(ext);
10135 intf->resetCodeParserState();
10136 }
10137 DString n=pd->getOutputFileBase();
10138 startFile(*g_outputList,n,false,n,pd->name());
10140 g_outputList->docify(pd->name());
10143 DString lineNoOptStr;
10144 if (pd->showLineNo())
10145 {
10146 lineNoOptStr="{lineno}";
10147 }
10148 g_outputList->generateDoc(pd->docFile(), // file
10149 pd->docLine(), // startLine
10150 pd.get(), // context
10151 nullptr, // memberDef
10152 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10153 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10154 DocOptions()
10155 .setIndexWords(true)
10156 .setExample(pd->name()));
10157 endFile(*g_outputList); // contains g_outputList->endContents()
10158 }
10160}
10161
10162//----------------------------------------------------------------------------
10163// generate module pages
10164
10166{
10167 for (const auto &gd : *Doxygen::groupLinkedMap)
10168 {
10169 if (!gd->isReference())
10170 {
10171 gd->writeDocumentation(*g_outputList);
10172 }
10173 }
10174}
10175
10176//----------------------------------------------------------------------------
10177// generate module pages
10178
10180{
10181 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10182 if (numThreads>1) // multi threaded processing
10183 {
10184 struct DocContext
10185 {
10186 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10187 : cdm(cdm_), ol(ol_) {}
10188 ClassDefMutable *cdm;
10189 OutputList ol;
10190 };
10191 ThreadPool threadPool(numThreads);
10192 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10193 // for each class in the namespace...
10194 for (const auto &cd : classList)
10195 {
10197 if (cdm)
10198 {
10199 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10200 auto processFile = [ctx]()
10201 {
10202 if ( ( ctx->cdm->isLinkableInProject() &&
10203 !ctx->cdm->isImplicitTemplateInstance()
10204 ) // skip external references, anonymous compounds and
10205 // template instances and nested classes
10206 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10207 )
10208 {
10209 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10210 ctx->cdm->writeDocumentation(ctx->ol);
10211 ctx->cdm->writeMemberList(ctx->ol);
10212 }
10213 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10214 return ctx;
10215 };
10216 results.emplace_back(threadPool.queue(processFile));
10217 }
10218 }
10219 // wait for the results
10220 for (auto &f : results)
10221 {
10222 auto ctx = f.get();
10223 }
10224 }
10225 else // single threaded processing
10226 {
10227 // for each class in the namespace...
10228 for (const auto &cd : classList)
10229 {
10231 if (cdm)
10232 {
10233 if ( ( cd->isLinkableInProject() &&
10234 !cd->isImplicitTemplateInstance()
10235 ) // skip external references, anonymous compounds and
10236 // template instances and nested classes
10237 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10238 )
10239 {
10240 msg("Generating docs for compound {}...\n",cd->displayName());
10241
10244 }
10246 }
10247 }
10248 }
10249}
10250
10252{
10253 // for each concept in the namespace...
10254 for (const auto &cd : conceptList)
10255 {
10257 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10258 {
10259 msg("Generating docs for concept {}...\n",cd->name());
10261 }
10262 }
10263}
10264
10266{
10267 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10268
10269 //writeNamespaceIndex(*g_outputList);
10270
10271 // for each namespace...
10272 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10273 {
10274 if (nd->isLinkableInProject())
10275 {
10277 if (ndm)
10278 {
10279 msg("Generating docs for namespace {}\n",nd->displayName());
10281 }
10282 }
10283
10284 generateNamespaceClassDocs(nd->getClasses());
10285 if (sliceOpt)
10286 {
10287 generateNamespaceClassDocs(nd->getInterfaces());
10288 generateNamespaceClassDocs(nd->getStructs());
10289 generateNamespaceClassDocs(nd->getExceptions());
10290 }
10291 generateNamespaceConceptDocs(nd->getConcepts());
10292 }
10293}
10294
10296{
10297 std::string oldDir = Dir::currentDirPath();
10298 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10301 {
10302 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10303 }
10304 Dir::setCurrent(oldDir);
10305}
10306
10308{
10309 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10310 std::string oldDir = Dir::currentDirPath();
10311 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10312
10313 DString qhgLocation=Config_getString(QHG_LOCATION);
10314 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10315 {
10316 // run qhelpgenerator -v and extract the Qt version used
10317 DString cmd=qhgLocation+ " -v 2>&1";
10318 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10319 FILE *f=Portable::popen(cmd,"r");
10320 if (!f)
10321 {
10322 err("could not execute {}\n",qhgLocation);
10323 }
10324 else
10325 {
10326 const size_t bufSize = 1024;
10327 char inBuf[bufSize+1];
10328 size_t numRead=fread(inBuf,1,bufSize,f);
10329 inBuf[numRead] = '\0';
10330 Debug::print(Debug::Qhp,0,"{}",inBuf);
10332
10333 int qtVersion=0;
10334 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10335 reg::Match match;
10336 std::string s = inBuf;
10337 if (reg::search(s,match,versionReg))
10338 {
10339 qtVersion = 10000*DString(match[1].str()).toInt() +
10340 100*DString(match[2].str()).toInt() +
10341 DString(match[3].str()).toInt();
10342 }
10343 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10344 {
10345 // dump the output of qhelpgenerator -c file.qhp
10346 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10347 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10348 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10349 f=Portable::popen(cmd,"r");
10350 if (!f)
10351 {
10352 err("could not execute {}\n",qhgLocation);
10353 }
10354 else
10355 {
10356 std::string output;
10357 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10358 {
10359 inBuf[numRead] = '\0';
10360 output += inBuf;
10361 }
10363 Debug::print(Debug::Qhp,0,"{}",output);
10364 }
10365 }
10366 }
10367 }
10368
10369 if (Portable::system(qhgLocation, args, false))
10370 {
10371 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10372 }
10373 Dir::setCurrent(oldDir);
10374}
10375
10376//----------------------------------------------------------------------------
10377
10379{
10380 // check dot path
10381 DString dotPath = Config_getString(DOT_PATH);
10382 if (!dotPath.empty())
10383 {
10384 FileInfo fi(dotPath.str());
10385 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10386 {
10387 dotPath = dotPath+"/dot"+Portable::commandExtension();
10388 FileInfo dp(dotPath.str());
10389 if (!dp.exists() || !dp.isFile())
10390 {
10391 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10392 dotPath = "dot";
10393 dotPath += Portable::commandExtension();
10394 }
10395 }
10396#if defined(_WIN32) // convert slashes
10397 size_t l=dotPath.length();
10398 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10399#endif
10400 }
10401 else
10402 {
10403 dotPath = "dot";
10404 dotPath += Portable::commandExtension();
10405 }
10406 Doxygen::verifiedDotPath = dotPath;
10408}
10409
10410//----------------------------------------------------------------------------
10411
10412/*! Generate a template version of the configuration file.
10413 * If the \a shortList parameter is true a configuration file without
10414 * comments will be generated.
10415 */
10416static void generateConfigFile(const DString &configFile,bool shortList,
10417 bool updateOnly=false)
10418{
10419 std::ofstream f;
10420 bool fileOpened=openOutputFile(configFile,f);
10421 bool writeToStdout=configFile=="-";
10422 if (fileOpened)
10423 {
10424 TextStream t(&f);
10425 Config::writeTemplate(t,shortList,updateOnly);
10426 if (!writeToStdout)
10427 {
10428 if (!updateOnly)
10429 {
10430 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10431 msg("Now edit the configuration file and enter\n\n");
10432 if (configFile!="Doxyfile" && configFile!="doxyfile")
10433 msg(" doxygen {}\n\n",configFile);
10434 else
10435 msg(" doxygen\n\n");
10436 msg("to generate the documentation for your project\n\n");
10437 }
10438 else
10439 {
10440 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10441 }
10442 }
10443 }
10444 else
10445 {
10446 term("Cannot open file {} for writing\n",configFile);
10447 }
10448}
10449
10451{
10452 std::ofstream f;
10453 bool fileOpened=openOutputFile("-",f);
10454 if (fileOpened)
10455 {
10456 TextStream t(&f);
10457 Config::compareDoxyfile(t,diffList);
10458 }
10459 else
10460 {
10461 term("Cannot open stdout for writing\n");
10462 }
10463}
10464
10465//----------------------------------------------------------------------------
10466// read and parse a tag file
10467
10468static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10469{
10470 DString fileName;
10471 DString destName;
10472 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10473 {
10474 fileName = tagLine.left(eqPos).stripWhiteSpace();
10475 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10476 if (fileName.empty() || destName.empty()) return;
10477 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10478 }
10479 else
10480 {
10481 fileName = tagLine;
10482 }
10483
10484 FileInfo fi(fileName.str());
10485 if (!fi.exists() || !fi.isFile())
10486 {
10487 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10488 return;
10489 }
10490
10491 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10492
10493 Doxygen::tagFileSet.emplace(fi.absFilePath());
10494
10495 if (!destName.empty())
10496 {
10497 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10498 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10499 }
10500 else
10501 {
10502 msg("Reading tag file '{}'...\n",fileName);
10503 }
10504
10505 parseTagFile(root,fi.absFilePath().c_str());
10506}
10507
10508//----------------------------------------------------------------------------
10510{
10511 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10512 for (const auto &sheet : latexExtraStyleSheet)
10513 {
10514 std::string fileName = sheet;
10515 if (!fileName.empty())
10516 {
10517 FileInfo fi(fileName);
10518 if (!fi.exists())
10519 {
10520 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10521 }
10522 else if (fi.isDir())
10523 {
10524 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10525 }
10526 else
10527 {
10528 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10530 {
10531 destFileName += LATEX_STYLE_EXTENSION;
10532 }
10533 copyFile(fileName, destFileName);
10534 }
10535 }
10536 }
10537}
10538
10539//----------------------------------------------------------------------------
10540static void copyStyleSheet()
10541{
10542 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10543 if (!htmlStyleSheet.empty())
10544 {
10545 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10546 {
10547 FileInfo fi(htmlStyleSheet.str());
10548 if (!fi.exists())
10549 {
10550 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10551 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10552 }
10553 else if (fi.isDir())
10554 {
10555 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10556 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10557 }
10558 else
10559 {
10560 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10561 copyFile(htmlStyleSheet,destFileName);
10562 }
10563 }
10564 }
10565 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10566 for (const auto &sheet : htmlExtraStyleSheet)
10567 {
10568 DString fileName(sheet);
10569 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10570 {
10571 FileInfo fi(fileName.str());
10572 if (!fi.exists())
10573 {
10574 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10575 }
10576 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10577 {
10578 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10579 }
10580 else if (fi.isDir())
10581 {
10582 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10583 }
10584 else
10585 {
10586 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10587 copyFile(fileName, destFileName);
10588 }
10589 }
10590 }
10591}
10592
10593static void copyLogo(const DString &outputOption, bool toIndex)
10594{
10595 DString projectLogo = projectLogoFile();
10596 if (!projectLogo.empty())
10597 {
10598 FileInfo fi(projectLogo.str());
10599 if (!fi.exists())
10600 {
10601 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10602 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10603 }
10604 else if (fi.isDir())
10605 {
10606 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10607 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10608 }
10609 else
10610 {
10611 DString destFileName = outputOption+"/"+fi.fileName();
10612 copyFile(projectLogo,destFileName);
10613 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10614 }
10615 }
10616}
10617
10618static void copyIcon(const DString &outputOption, bool toIndex)
10619{
10620 DString projectIcon = Config_getString(PROJECT_ICON);
10621 if (!projectIcon.empty())
10622 {
10623 FileInfo fi(projectIcon.str());
10624 if (!fi.exists())
10625 {
10626 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10627 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10628 }
10629 else if (fi.isDir())
10630 {
10631 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10632 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10633 }
10634 else
10635 {
10636 DString destFileName = outputOption+"/"+fi.fileName();
10637 copyFile(projectIcon,destFileName);
10638 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10639 }
10640 }
10641}
10642
10643static void copyExtraFiles(const StringVector &files,const DString &filesOption,const DString &outputOption, bool toIndex)
10644{
10645 for (const auto &fileName : files)
10646 {
10647 if (!fileName.empty())
10648 {
10649 FileInfo fi(fileName);
10650 if (!fi.exists())
10651 {
10652 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10653 }
10654 else if (fi.isDir())
10655 {
10656 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10657 }
10658 else
10659 {
10660 DString destFileName = outputOption+"/"+fi.fileName();
10661 copyFile(fileName, destFileName);
10662 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10663 }
10664 }
10665 }
10666}
10667
10668//----------------------------------------------------------------------------
10669
10671{
10672 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10673 {
10674 struct FileEntry
10675 {
10676 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10677 DString path;
10678 FileDef *fileDef;
10679 };
10680
10681 // collect the entry for which to compute the longest common prefix (LCP) of the path
10682 std::vector<FileEntry> fileEntries;
10683 for (const auto &fd : *fn)
10684 {
10685 if (!fd->isReference()) // skip external references
10686 {
10687 fileEntries.emplace_back(fd->getPath(),fd.get());
10688 }
10689 }
10690
10691 size_t size = fileEntries.size();
10692
10693 if (size==1) // name if unique, so diskname is simply the name
10694 {
10695 FileDef *fd = fileEntries[0].fileDef;
10696 fd->setDiskName(fn->fileName());
10697 }
10698 else if (size>1) // multiple occurrences of the same file name
10699 {
10700 // sort the array
10701 std::stable_sort(fileEntries.begin(),
10702 fileEntries.end(),
10703 [](const FileEntry &fe1,const FileEntry &fe2)
10704 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10705 );
10706
10707 // since the entries are sorted, the common prefix of the whole array is same
10708 // as the common prefix between the first and last entry
10709 const FileEntry &first = fileEntries[0];
10710 const FileEntry &last = fileEntries[size-1];
10711 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10712 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10713 int j=0;
10714 int i=0;
10715 for (i=0;i<first_path_size && i<last_path_size;i++)
10716 {
10717 if (first.path[i]=='/') j=i;
10718 if (first.path[i]!=last.path[i]) break;
10719 }
10720 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10721 {
10722 // case first='some/path' and last='some/path/more' => match is 'some/path'
10723 j=first_path_size;
10724 }
10725 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10726 {
10727 // case first='some/path/more' and last='some/path' => match is 'some/path'
10728 j=last_path_size;
10729 }
10730
10731 // add non-common part of the path to the name
10732 for (auto &fileEntry : fileEntries)
10733 {
10734 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10735 fileEntry.fileDef->setName(prefix+fn->fileName());
10736 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10737 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10738 }
10739 }
10740 }
10741}
10742
10743
10744
10745//----------------------------------------------------------------------------
10746
10747static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10748{
10749 DString fileName=fn;
10750 DString extension;
10751 size_t sep = fileName.rfind('/');
10752 size_t ei = fileName.rfind('.');
10753 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10754 {
10755 extension=fileName.mid(ei);
10756 }
10757 else
10758 {
10759 extension = ".no_extension";
10760 }
10761
10762 return Doxygen::parserManager->getOutlineParser(extension);
10763}
10764
10765static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10766 FileDef *fd,const DString &fn,
10767 ClangTUParser *clangParser,bool newTU)
10768{
10769 DString fileName=fn;
10770 AUTO_TRACE("fileName={}",fileName);
10771 DString extension;
10772 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10773 {
10774 extension=fileName.mid(ei);
10775 }
10776 else
10777 {
10778 extension = ".no_extension";
10779 }
10780
10781 FileInfo fi(fileName.str());
10782 std::string preBuf;
10783
10784 if (Config_getBool(ENABLE_PREPROCESSING) &&
10785 parser.needsPreprocessing(extension))
10786 {
10787 Preprocessor preprocessor;
10788 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10789 for (const auto &s : includePath)
10790 {
10791 std::string absPath = FileInfo(s).absFilePath();
10792 preprocessor.addSearchDir(absPath);
10793 }
10794 std::string inBuf;
10795 msg("Preprocessing {}...\n",fn);
10796 readInputFile(fileName,inBuf);
10797 addTerminalCharIfMissing(inBuf,'\n');
10798 preprocessor.processFile(fileName,inBuf,preBuf);
10799 }
10800 else // no preprocessing
10801 {
10802 msg("Reading {}...\n",fn);
10803 readInputFile(fileName,preBuf);
10804 addTerminalCharIfMissing(preBuf,'\n');
10805 }
10806
10807 std::string convBuf;
10808 convBuf.reserve(preBuf.size()+1024);
10809
10810 // convert multi-line C++ comments to C style comments
10811 convertCppComments(preBuf,convBuf,fileName.str());
10812
10813 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10814 // use language parse to parse the file
10815 if (clangParser)
10816 {
10817 if (newTU) clangParser->parse();
10818 clangParser->switchToFile(fd);
10819 }
10820 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10821 fileRoot->setFileDef(fd);
10822 return fileRoot;
10823}
10824
10825//! parse the list of input files
10826static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10827{
10828 AUTO_TRACE();
10829#if USE_LIBCLANG
10831 {
10832 StringUnorderedSet processedFiles;
10833
10834 // create a dictionary with files to process
10835 StringUnorderedSet filesToProcess;
10836 for (const auto &s : g_inputFiles)
10837 {
10838 filesToProcess.insert(s);
10839 }
10840
10841 std::mutex processedFilesLock;
10842 // process source files (and their include dependencies)
10843 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10844 msg("Processing input using {} threads.\n",numThreads);
10845 ThreadPool threadPool(numThreads);
10846 using FutureType = std::vector< std::shared_ptr<Entry> >;
10847 std::vector< std::future< FutureType > > results;
10848 for (const auto &s : g_inputFiles)
10849 {
10850 bool ambig = false;
10851 DString qs = s;
10853 ASSERT(fd!=nullptr);
10854 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10855 {
10856 // lambda representing the work to executed by a thread
10857 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10858 bool ambig_l = false;
10859 std::vector< std::shared_ptr<Entry> > roots;
10861 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10862 auto parser = getParserForFile(qs);
10863 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10864 roots.push_back(fileRoot);
10865
10866 // Now process any include files in the same translation unit
10867 // first. When libclang is used this is much more efficient.
10868 for (auto incFile : clangParser->filesInSameTU())
10869 {
10870 DString qincFile = incFile;
10871 if (filesToProcess.find(incFile)!=filesToProcess.end())
10872 {
10873 bool needsToBeProcessed = false;
10874 {
10875 std::lock_guard<std::mutex> lock(processedFilesLock);
10876 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10877 if (needsToBeProcessed) processedFiles.insert(incFile);
10878 }
10879 if (qincFile!=qs && needsToBeProcessed)
10880 {
10881 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10882 if (ifd && !ifd->isReference())
10883 {
10884 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10885 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10886 roots.push_back(fileRoot);
10887 }
10888 }
10889 }
10890 }
10891 return roots;
10892 };
10893 // dispatch the work and collect the future results
10894 results.emplace_back(threadPool.queue(processFile));
10895 }
10896 }
10897 // synchronize with the Entry result lists produced and add them to the root
10898 for (auto &f : results)
10899 {
10900 auto l = f.get();
10901 for (auto &e : l)
10902 {
10903 root->moveToSubEntryAndKeep(e);
10904 }
10905 }
10906 // process remaining files
10907 results.clear();
10908 for (const auto &s : g_inputFiles)
10909 {
10910 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10911 {
10912 // lambda representing the work to executed by a thread
10913 auto processFile = [s]() {
10914 bool ambig = false;
10915 DString qs = s;
10916 std::vector< std::shared_ptr<Entry> > roots;
10918 auto parser { getParserForFile(qs) };
10919 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10920 if (useClang)
10921 {
10922 auto clangParser = ClangParser::instance()->createTUParser(fd);
10923 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10924 roots.push_back(fileRoot);
10925 }
10926 else
10927 {
10928 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10929 roots.push_back(fileRoot);
10930 }
10931 return roots;
10932 };
10933 results.emplace_back(threadPool.queue(processFile));
10934 }
10935 }
10936 // synchronize with the Entry result lists produced and add them to the root
10937 for (auto &f : results)
10938 {
10939 auto l = f.get();
10940 for (auto &e : l)
10941 {
10942 root->moveToSubEntryAndKeep(e);
10943 }
10944 }
10945 }
10946 else // normal processing
10947#endif
10948 {
10949 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10950 msg("Processing input using {} threads.\n",numThreads);
10951 ThreadPool threadPool(numThreads);
10952 using FutureType = std::shared_ptr<Entry>;
10953 std::vector< std::future< FutureType > > results;
10954 for (const auto &s : g_inputFiles)
10955 {
10956 // lambda representing the work to executed by a thread
10957 auto processFile = [s]() {
10958 bool ambig = false;
10959 DString qs = s;
10961 auto parser = getParserForFile(qs);
10962 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10963 return fileRoot;
10964 };
10965 // dispatch the work and collect the future results
10966 results.emplace_back(threadPool.queue(processFile));
10967 }
10968 // synchronize with the Entry results produced and add them to the root
10969 for (auto &f : results)
10970 {
10971 root->moveToSubEntryAndKeep(f.get());
10972 }
10973 }
10974}
10975
10976//! parse the list of input files
10977static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10978{
10979 AUTO_TRACE();
10980#if USE_LIBCLANG
10982 {
10983 StringUnorderedSet processedFiles;
10984
10985 // create a dictionary with files to process
10986 StringUnorderedSet filesToProcess;
10987 for (const auto &s : g_inputFiles)
10988 {
10989 filesToProcess.insert(s);
10990 }
10991
10992 // process source files (and their include dependencies)
10993 for (const auto &s : g_inputFiles)
10994 {
10995 bool ambig = false;
10996 DString qs =s;
10998 ASSERT(fd!=nullptr);
10999 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
11000 {
11001 auto clangParser = ClangParser::instance()->createTUParser(fd);
11002 auto parser { getParserForFile(qs) };
11003 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11004 root->moveToSubEntryAndKeep(fileRoot);
11005 processedFiles.insert(s);
11006
11007 // Now process any include files in the same translation unit
11008 // first. When libclang is used this is much more efficient.
11009 for (auto incFile : clangParser->filesInSameTU())
11010 {
11011 //printf(" file %s\n",qPrint(incFile));
11012 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
11013 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
11014 {
11016 if (ifd && !ifd->isReference())
11017 {
11018 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
11019 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
11020 root->moveToSubEntryAndKeep(fileRoot);
11021 processedFiles.insert(incFile);
11022 }
11023 }
11024 }
11025 }
11026 }
11027 // process remaining files
11028 for (const auto &s : g_inputFiles)
11029 {
11030 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11031 {
11032 bool ambig = false;
11033 DString qs = s;
11035 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11036 {
11037 auto clangParser = ClangParser::instance()->createTUParser(fd);
11038 auto parser { getParserForFile(qs) };
11039 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11040 root->moveToSubEntryAndKeep(fileRoot);
11041 }
11042 else
11043 {
11044 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11045 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11046 root->moveToSubEntryAndKeep(fileRoot);
11047 }
11048 processedFiles.insert(s);
11049 }
11050 }
11051 }
11052 else // normal processing
11053#endif
11054 {
11055 for (const auto &s : g_inputFiles)
11056 {
11057 bool ambig = false;
11058 DString qs = s;
11060 ASSERT(fd!=nullptr);
11061 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11062 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11063 root->moveToSubEntryAndKeep(std::move(fileRoot));
11064 }
11065 }
11066}
11067
11068// resolves a path that may include symlinks, if a recursive symlink is
11069// found an empty string is returned.
11070static std::string resolveSymlink(const std::string &path)
11071{
11072 size_t sepPos=0;
11073 size_t oldPos=0;
11074 StringUnorderedSet nonSymlinks;
11075 StringUnorderedSet known;
11076 DString result(path);
11077 DString oldPrefix = "/";
11078 do
11079 {
11080#if defined(_WIN32)
11081 // UNC path, skip server and share name
11082 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11083 sepPos = result.find('/',2);
11084 if (sepPos!=DString::npos)
11085 sepPos = result.find('/',sepPos+1);
11086#else
11087 sepPos = result.find('/',sepPos+1);
11088#endif
11089 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11090 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11091 {
11092 FileInfo fi(prefix.str());
11093 if (fi.isSymLink())
11094 {
11095 DString target = fi.readLink();
11096 bool isRelative = FileInfo(target.str()).isRelative();
11097 if (isRelative)
11098 {
11099 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11100 }
11101 if (sepPos!=DString::npos)
11102 {
11103 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11104 {
11105 target+='/';
11106 }
11107 target+=result.mid(sepPos);
11108 }
11109 result = Dir::cleanDirPath(target.str());
11110 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11111 known.insert(result.str());
11112 if (isRelative)
11113 {
11114 sepPos = oldPos;
11115 }
11116 else // link to absolute path
11117 {
11118 sepPos = 0;
11119 oldPrefix = "/";
11120 }
11121 }
11122 else
11123 {
11124 nonSymlinks.insert(prefix.str());
11125 oldPrefix = prefix;
11126 }
11127 oldPos = sepPos;
11128 }
11129 }
11130 while (sepPos!=DString::npos);
11131 return Dir::cleanDirPath(result.str());
11132}
11133
11135
11136//----------------------------------------------------------------------------
11137// Read all files matching at least one pattern in 'patList' in the
11138// directory represented by 'fi'.
11139// The directory is read iff the recursiveFlag is set.
11140// The contents of all files is append to the input string
11141
11142static void readDir(FileInfo *fi,
11143 FileNameLinkedMap *fnMap,
11144 StringUnorderedSet *exclSet,
11145 const StringVector *patList,
11146 const StringVector *exclPatList,
11147 StringVector *resultList,
11148 StringUnorderedSet *resultSet,
11149 bool errorIfNotExist,
11150 bool recursive,
11151 StringUnorderedSet *killSet,
11152 StringUnorderedSet *paths
11153 )
11154{
11155 std::string dirName = fi->absFilePath();
11156 if (paths && !dirName.empty())
11157 {
11158 paths->insert(dirName);
11159 }
11160 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11161 if (fi->isSymLink())
11162 {
11163 dirName = resolveSymlink(dirName);
11164 if (dirName.empty())
11165 {
11166 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11167 return; // recursive symlink
11168 }
11169 }
11170
11171 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11172 {
11173 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11174 return; // already visited path
11175 }
11176 g_pathsVisited.insert(dirName);
11177
11178 Dir dir(dirName);
11179 msg("Searching for files in directory {}\n", fi->absFilePath());
11180 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11181
11182 StringVector dirResultList;
11183
11184 for (const auto &dirEntry : dir.iterator())
11185 {
11186 FileInfo cfi(dirEntry.path());
11187 auto checkPatterns = [&]() -> bool
11188 {
11189 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11190 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11191 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11192 };
11193
11194 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11195 { // file should not be excluded
11196 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11197 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11198 {
11199 }
11200 else if (!cfi.exists() || !cfi.isReadable())
11201 {
11202 if (errorIfNotExist && checkPatterns())
11203 {
11204 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11205 }
11206 }
11207 else if (cfi.isFile() && checkPatterns())
11208 {
11209 std::string name=cfi.fileName();
11210 std::string path=cfi.dirPath()+"/";
11211 std::string fullName=path+name;
11212 if (fnMap)
11213 {
11214 auto fd = createFileDef(path,name);
11215 FileName *fn=nullptr;
11216 if (!name.empty())
11217 {
11218 fn = fnMap->add(name);
11219 fn->push_back(std::move(fd));
11220 }
11221 }
11222 dirResultList.push_back(fullName);
11223 if (resultSet) resultSet->insert(fullName);
11224 if (killSet) killSet->insert(fullName);
11225 }
11226 else if (recursive &&
11227 cfi.isDir() &&
11228 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11229 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11230 {
11231 FileInfo acfi(cfi.absFilePath());
11232 readDir(&acfi,fnMap,exclSet,
11233 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11234 recursive,killSet,paths);
11235 }
11236 }
11237 }
11238 if (resultList && !dirResultList.empty())
11239 {
11240 // sort the resulting list to make the order platform independent.
11241 std::stable_sort(dirResultList.begin(),
11242 dirResultList.end(),
11243 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11244
11245 // append the sorted results to resultList
11246 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11247 }
11248}
11249
11250
11251//----------------------------------------------------------------------------
11252// read a file or all files in a directory and append their contents to the
11253// input string. The names of the files are appended to the 'fiList' list.
11254
11256 FileNameLinkedMap *fnMap,
11257 StringUnorderedSet *exclSet,
11258 const StringVector *patList,
11259 const StringVector *exclPatList,
11260 StringVector *resultList,
11261 StringUnorderedSet *resultSet,
11262 bool recursive,
11263 bool errorIfNotExist,
11264 StringUnorderedSet *killSet,
11265 StringUnorderedSet *paths
11266 )
11267{
11268 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11269 // strip trailing slashes
11270 if (s.empty()) return;
11271
11272 g_pathsVisited.clear();
11273
11274 FileInfo fi(s.str());
11275 //printf("readFileOrDirectory(%s)\n",s);
11276 {
11277 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11278 {
11279 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11280 {
11281 }
11282 else if (!fi.exists() || !fi.isReadable())
11283 {
11284 if (errorIfNotExist)
11285 {
11286 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11287 }
11288 }
11289 else if (fi.isFile())
11290 {
11291 std::string dirPath = fi.dirPath(true);
11292 std::string filePath = fi.absFilePath();
11293 if (paths && !dirPath.empty())
11294 {
11295 paths->insert(dirPath);
11296 }
11297 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11298 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11299 {
11300 std::string name=fi.fileName();
11301 if (fnMap)
11302 {
11303 auto fd = createFileDef(dirPath+"/",name);
11304 if (!name.empty())
11305 {
11306 FileName *fn = fnMap->add(name);
11307 fn->push_back(std::move(fd));
11308 }
11309 }
11310 if (resultList || resultSet)
11311 {
11312 if (resultList) resultList->push_back(filePath);
11313 if (resultSet) resultSet->insert(filePath);
11314 }
11315
11316 if (killSet) killSet->insert(fi.absFilePath());
11317 }
11318 }
11319 else if (fi.isDir()) // readable dir
11320 {
11321 readDir(&fi,fnMap,exclSet,patList,
11322 exclPatList,resultList,resultSet,errorIfNotExist,
11323 recursive,killSet,paths);
11324 }
11325 }
11326 }
11327}
11328
11329//----------------------------------------------------------------------------
11330
11332{
11333 DString anchor;
11335 {
11336 MemberDef *md = toMemberDef(d);
11337 anchor=":"+md->anchor();
11338 }
11339 DString scope;
11340 DString fn = d->getOutputFileBase();
11343 {
11344 scope = fn;
11345 }
11346 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11347 << fn+anchor << "','"
11348 << scope << "','"
11349 << d->name() << "','"
11350 << d->getDefFileName() << "','"
11351 << d->getDefLine()
11352 << "');\n";
11353}
11354
11355static void dumpSymbolMap()
11356{
11357 std::ofstream f = Portable::openOutputStream("symbols.sql");
11358 if (f.is_open())
11359 {
11360 TextStream t(&f);
11361 for (const auto &[name,symList] : *Doxygen::symbolMap)
11362 {
11363 for (const auto &def : symList)
11364 {
11365 dumpSymbol(t,def);
11366 }
11367 }
11368 }
11369}
11370
11371// print developer options of Doxygen
11372static void devUsage()
11373{
11375 msg("Developer parameters:\n");
11376 msg(" -m dump symbol map\n");
11377 msg(" -b making messages output unbuffered\n");
11378 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11379#if ENABLE_TRACING
11380 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11381 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11382 " and include time and thread information\n");
11383#endif
11384 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11386}
11387
11388
11389//----------------------------------------------------------------------------
11390// print the version of Doxygen
11391
11392static void version(const bool extended)
11393{
11395 DString versionString = getFullVersion();
11396 msg("{}\n",versionString);
11397 if (extended)
11398 {
11399 DString extVers;
11400 if (!extVers.empty()) extVers+= ", ";
11401 extVers += "sqlite3 ";
11402 extVers += sqlite3_libversion();
11403#if USE_LIBCLANG
11404 if (!extVers.empty()) extVers+= ", ";
11405 extVers += "clang support ";
11406 extVers += CLANG_VERSION_STRING;
11407#endif
11408 if (!extVers.empty())
11409 {
11410 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11411 {
11412 extVers = extVers.replace(lastComma,1," and");
11413 }
11414 msg(" with {}.\n",extVers);
11415 }
11416 }
11417}
11418
11419//----------------------------------------------------------------------------
11420// print the usage of Doxygen
11421
11422static void usage(const DString &name,const DString &versionString)
11423{
11425 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11426 "You can use Doxygen in a number of ways:\n\n"
11427 "1) Use Doxygen to generate a template configuration file*:\n"
11428 " {1} [-s] -g [configName]\n\n"
11429 "2) Use Doxygen to update an old configuration file*:\n"
11430 " {1} [-s] -u [configName]\n\n"
11431 "3) Use Doxygen to generate documentation using an existing "
11432 "configuration file*:\n"
11433 " {1} [configName]\n\n"
11434 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11435 " generated documentation:\n"
11436 " {1} -l [layoutFileName]\n\n"
11437 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11438 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11439 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11440 " RTF: {1} -w rtf styleSheetFile\n"
11441 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11442 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11443 "6) Use Doxygen to generate a rtf extensions file\n"
11444 " {1} -e rtf extensionsFile\n\n"
11445 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11446 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11447 " {1} -x [configFile]\n\n"
11448 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11449 " without replacing the environment variables or CMake type replacement variables\n"
11450 " {1} -x_noenv [configFile]\n\n"
11451 "8) Use Doxygen to show a list of built-in emojis.\n"
11452 " {1} -f emoji outputFileName\n\n"
11453 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11454 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11455 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11456 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11457 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11458 "-v print version string, -V print extended version information\n"
11459 "-h,-? prints usage help information\n"
11460 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11461}
11462
11463//----------------------------------------------------------------------------
11464// read the argument of option 'c' from the comment argument list and
11465// update the option index 'optInd'.
11466
11467static const char *getArg(int argc,char **argv,int &optInd)
11468{
11469 char *s=nullptr;
11470 if (dstrlen(&argv[optInd][2])>0)
11471 s=&argv[optInd][2];
11472 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11473 s=argv[++optInd];
11474 return s;
11475}
11476
11477//----------------------------------------------------------------------------
11478
11479/** @brief /dev/null outline parser */
11481{
11482 public:
11483 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11484 bool needsPreprocessing(const DString &) const override { return false; }
11485 void parsePrototype(const DString &) override {}
11486};
11487
11488
11489template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11490{
11491 return []() { return std::make_unique<T>(); };
11492}
11493
11495{
11496 initResources();
11497 DString lang = Portable::getenv("LC_ALL");
11498 if (!lang.empty()) Portable::setenv("LANG",lang);
11499 std::setlocale(LC_ALL,"");
11500 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11501 std::setlocale(LC_NUMERIC,"C");
11502
11504
11528
11529 // register any additional parsers here...
11530
11532
11533#if USE_LIBCLANG
11535#endif
11544 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11545 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11546 //Doxygen::tagDestinationDict.setAutoDelete(true);
11548
11549 // initialization of these globals depends on
11550 // configuration switches so we need to postpone these
11551 Doxygen::globalScope = nullptr;
11561
11562}
11563
11596
11597void readConfiguration(int argc, char **argv)
11598{
11599 DString versionString = getFullVersion();
11600
11601 // helper that calls \a func to write to file \a fileName via a TextStream
11602 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11603 {
11604 std::ofstream f;
11605 if (openOutputFile(fileName,f))
11606 {
11607 TextStream t(&f);
11608 func(t);
11609 return true;
11610 }
11611 return false;
11612 };
11613
11614
11615 /**************************************************************************
11616 * Handle arguments *
11617 **************************************************************************/
11618
11619 int optInd=1;
11620 DString configName;
11621 DString traceName;
11622 bool genConfig=false;
11623 bool shortList=false;
11624 bool traceTiming=false;
11626 bool updateConfig=false;
11627 bool quiet = false;
11628 while (optInd<argc && argv[optInd][0]=='-' &&
11629 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11630 argv[optInd][1]=='-')
11631 )
11632 {
11633 switch(argv[optInd][1])
11634 {
11635 case 'g':
11636 {
11637 genConfig=true;
11638 }
11639 break;
11640 case 'l':
11641 {
11642 DString layoutName;
11643 if (optInd+1>=argc)
11644 {
11645 layoutName="DoxygenLayout.xml";
11646 }
11647 else
11648 {
11649 layoutName=argv[optInd+1];
11650 }
11651 writeDefaultLayoutFile(layoutName);
11653 exit(0);
11654 }
11655 break;
11656 case 'c':
11657 if (optInd+1>=argc) // no file name given
11658 {
11659 msg("option \"-c\" is missing the file name to read\n");
11660 devUsage();
11662 exit(1);
11663 }
11664 else
11665 {
11666 g_commentFileName=argv[optInd+1];
11667 optInd++;
11668 }
11669 g_singleComment=true;
11670 quiet=true;
11671 break;
11672 case 'd':
11673 {
11674 DString debugLabel=getArg(argc,argv,optInd);
11675 if (debugLabel.empty())
11676 {
11677 devUsage();
11679 exit(0);
11680 }
11681 int retVal = Debug::setFlagStr(debugLabel);
11682 if (!retVal)
11683 {
11684 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11685 devUsage();
11687 exit(1);
11688 }
11689 }
11690 break;
11691 case 't':
11692 {
11693#if ENABLE_TRACING
11694 if (!strcmp(argv[optInd]+1,"t_time"))
11695 {
11696 traceTiming = true;
11697 }
11698 else if (!strcmp(argv[optInd]+1,"t"))
11699 {
11700 traceTiming = false;
11701 }
11702 else
11703 {
11704 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11706 exit(1);
11707 }
11708 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11709 {
11710 traceName="stdout";
11711 }
11712 else
11713 {
11714 traceName=argv[optInd+1];
11715 optInd++;
11716 }
11717#else
11718 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11720 exit(1);
11721#endif
11722 }
11723 break;
11724 case 'x':
11725 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11726 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11727 else
11728 {
11729 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11731 exit(1);
11732 }
11733 break;
11734 case 's':
11735 shortList=true;
11736 break;
11737 case 'u':
11738 updateConfig=true;
11739 break;
11740 case 'e':
11741 {
11742 DString formatName=getArg(argc,argv,optInd);
11743 if (formatName.empty())
11744 {
11745 err("option \"-e\" is missing format specifier rtf.\n");
11747 exit(1);
11748 }
11749 if (dstricmp(formatName.data(),"rtf")==0)
11750 {
11751 if (optInd+1>=argc)
11752 {
11753 err("option \"-e rtf\" is missing an extensions file name\n");
11755 exit(1);
11756 }
11757 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11759 exit(0);
11760 }
11761 err("option \"-e\" has invalid format specifier.\n");
11763 exit(1);
11764 }
11765 break;
11766 case 'f':
11767 {
11768 DString listName=getArg(argc,argv,optInd);
11769 if (listName.empty())
11770 {
11771 err("option \"-f\" is missing list specifier.\n");
11773 exit(1);
11774 }
11775 if (dstricmp(listName.data(),"emoji")==0)
11776 {
11777 if (optInd+1>=argc)
11778 {
11779 err("option \"-f emoji\" is missing an output file name\n");
11781 exit(1);
11782 }
11783 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11785 exit(0);
11786 }
11787 err("option \"-f\" has invalid list specifier.\n");
11789 exit(1);
11790 }
11791 break;
11792 case 'w':
11793 {
11794 DString formatName=getArg(argc,argv,optInd);
11795 if (formatName.empty())
11796 {
11797 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11799 exit(1);
11800 }
11801 if (dstricmp(formatName.data(),"rtf")==0)
11802 {
11803 if (optInd+1>=argc)
11804 {
11805 err("option \"-w rtf\" is missing a style sheet file name\n");
11807 exit(1);
11808 }
11809 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11810 {
11811 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11813 exit(1);
11814 }
11816 exit(0);
11817 }
11818 else if (dstricmp(formatName.data(),"html")==0)
11819 {
11820 Config::init();
11821 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11822 // explicit config file mentioned or default found on disk
11823 {
11824 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11825 if (!Config::parse(df)) // parse the config file
11826 {
11827 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11829 exit(1);
11830 }
11831 }
11832 if (optInd+3>=argc)
11833 {
11834 err("option \"-w html\" does not have enough arguments\n");
11836 exit(1);
11837 }
11838 Config::postProcess(true);
11841 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11842 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11843 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11844 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11846 exit(0);
11847 }
11848 else if (dstricmp(formatName.data(),"latex")==0)
11849 {
11850 Config::init();
11851 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11852 {
11853 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11854 if (!Config::parse(df))
11855 {
11856 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11858 exit(1);
11859 }
11860 }
11861 if (optInd+3>=argc)
11862 {
11863 err("option \"-w latex\" does not have enough arguments\n");
11865 exit(1);
11866 }
11867 Config::postProcess(true);
11870 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11871 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11872 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11873 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11875 exit(0);
11876 }
11877 else
11878 {
11879 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11881 exit(1);
11882 }
11883 }
11884 break;
11885 case 'm':
11886 g_dumpSymbolMap = true;
11887 break;
11888 case 'v':
11889 version(false);
11891 exit(0);
11892 break;
11893 case 'V':
11894 version(true);
11896 exit(0);
11897 break;
11898 case '-':
11899 if (dstrcmp(&argv[optInd][2],"help")==0)
11900 {
11901 usage(argv[0],versionString);
11902 exit(0);
11903 }
11904 else if (dstrcmp(&argv[optInd][2],"version")==0)
11905 {
11906 version(false);
11908 exit(0);
11909 }
11910 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
11911 (dstrcmp(&argv[optInd][2],"VERSION")==0))
11912 {
11913 version(true);
11915 exit(0);
11916 }
11917 else
11918 {
11919 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11920 usage(argv[0],versionString);
11921 exit(1);
11922 }
11923 break;
11924 case 'b':
11925 setvbuf(stdout,nullptr,_IONBF,0);
11926 break;
11927 case 'q':
11928 quiet = true;
11929 break;
11930 case 'h':
11931 case '?':
11932 usage(argv[0],versionString);
11933 exit(0);
11934 break;
11935 default:
11936 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11937 usage(argv[0],versionString);
11938 exit(1);
11939 }
11940 optInd++;
11941 }
11942
11943 /**************************************************************************
11944 * Parse or generate the config file *
11945 **************************************************************************/
11946
11947 initTracing(traceName.data(),traceTiming);
11948 TRACE("Doxygen version used: {}",getFullVersion());
11949 Config::init();
11950
11951 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11952 if (optInd>=argc)
11953 {
11954 if (configFileInfo1.exists())
11955 {
11956 configName="Doxyfile";
11957 }
11958 else if (configFileInfo2.exists())
11959 {
11960 configName="doxyfile";
11961 }
11962 else if (genConfig)
11963 {
11964 configName="Doxyfile";
11965 }
11966 else
11967 {
11968 err("Doxyfile not found and no input file specified!\n");
11969 usage(argv[0],versionString);
11970 exit(1);
11971 }
11972 }
11973 else
11974 {
11975 FileInfo fi(argv[optInd]);
11976 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
11977 {
11978 configName=argv[optInd];
11979 }
11980 else
11981 {
11982 err("configuration file {} not found!\n",argv[optInd]);
11983 usage(argv[0],versionString);
11984 exit(1);
11985 }
11986 }
11987
11988 if (genConfig)
11989 {
11990 generateConfigFile(configName,shortList);
11992 exit(0);
11993 }
11994
11995 if (!Config::parse(configName,updateConfig,diffList))
11996 {
11997 err("could not open or read configuration file {}!\n",configName);
11999 exit(1);
12000 }
12001
12002 if (diffList!=Config::CompareMode::Full)
12003 {
12005 compareDoxyfile(diffList);
12007 exit(0);
12008 }
12009
12010 if (updateConfig)
12011 {
12013 generateConfigFile(configName,shortList,true);
12015 exit(0);
12016 }
12017
12018 /* Perlmod wants to know the path to the config file.*/
12019 FileInfo configFileInfo(configName.str());
12020 setPerlModDoxyfile(configFileInfo.absFilePath());
12021
12022 /* handle -q option */
12023 if (quiet) Config_updateBool(QUIET,true);
12024}
12025
12026/** check and resolve config options */
12028{
12029 AUTO_TRACE();
12030
12031 Config::postProcess(false);
12035}
12036
12037/** adjust globals that depend on configuration settings. */
12039{
12040 AUTO_TRACE();
12041 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12052
12053 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12054
12055 /* Set the global html file extension. */
12056 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12057
12058
12060 Config_getBool(CALLER_GRAPH) ||
12061 Config_getBool(REFERENCES_RELATION) ||
12062 Config_getBool(REFERENCED_BY_RELATION);
12063
12064 /**************************************************************************
12065 * Add custom extension mappings
12066 **************************************************************************/
12067
12068 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
12069 for (const auto &mapping : extMaps)
12070 {
12071 DString mapStr = mapping;
12072 if (size_t i=mapStr.find('='); i==DString::npos)
12073 {
12074 continue;
12075 }
12076 else
12077 {
12078 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12079 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12080 if (ext.empty() || language.empty())
12081 {
12082 continue;
12083 }
12084
12085 if (!updateLanguageMapping(ext,language))
12086 {
12087 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12088 "Check the EXTENSION_MAPPING setting in the config file.\n",
12089 ext,language);
12090 }
12091 else
12092 {
12093 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12094 ext,language);
12095 }
12096 }
12097 }
12098 // create input file exncodings
12099
12100 // check INPUT_ENCODING
12101 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12102 if (cd==reinterpret_cast<void *>(-1))
12103 {
12104 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12105 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12106 Config_getString(INPUT_ENCODING),strerror(errno));
12107 }
12108 else
12109 {
12111 }
12112
12113 // check and split INPUT_FILE_ENCODING
12114 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
12115 for (const auto &mapping : fileEncod)
12116 {
12117 DString mapStr = mapping;
12118 if (size_t i=mapStr.find('='); i==DString::npos)
12119 {
12120 continue;
12121 }
12122 else
12123 {
12124 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12125 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12126 if (pattern.empty() || encoding.empty())
12127 {
12128 continue;
12129 }
12130 cd = portable_iconv_open("UTF-8",encoding.data());
12131 if (cd==reinterpret_cast<void *>(-1))
12132 {
12133 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12134 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12135 encoding,strerror(errno));
12136 }
12137 else
12138 {
12140 }
12141
12142 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12143 }
12144 }
12145
12146 // add predefined macro name to a dictionary
12147 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
12148 for (const auto &s : expandAsDefinedList)
12149 {
12151 }
12152
12153 // read aliases and store them in a dictionary
12154 readAliases();
12155
12156 // store number of spaces in a tab into Doxygen::spaces
12157 int tabSize = Config_getInt(TAB_SIZE);
12158 Doxygen::spaces.resize(tabSize);
12159 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12160 Doxygen::spaces.at(tabSize)='\0';
12161}
12162
12163#ifdef HAS_SIGNALS
12164static void stopDoxygen(int)
12165{
12166 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12167 Dir thisDir;
12168 msg("Cleaning up...\n");
12169 if (!Doxygen::filterDBFileName.empty())
12170 {
12171 thisDir.remove(Doxygen::filterDBFileName.str());
12172 }
12173 killpg(0,SIGINT);
12175 exitTracing();
12176 exit(1);
12177}
12178#endif
12179
12180static void writeTagFile()
12181{
12182 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12183 if (generateTagFile.empty()) return;
12184
12185 std::ofstream f = Portable::openOutputStream(generateTagFile);
12186 if (!f.is_open())
12187 {
12188 err("cannot open tag file {} for writing\n", generateTagFile);
12189 return;
12190 }
12191 TextStream tagFile(&f);
12192 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12193 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12194 std::string gitVersion = getGitVersion();
12195 if (!gitVersion.empty())
12196 {
12197 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12198 }
12199 tagFile << ">\n";
12200
12201 // for each file
12202 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12203 {
12204 for (const auto &fd : *fn)
12205 {
12206 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12207 }
12208 }
12209 // for each class
12210 for (const auto &cd : *Doxygen::classLinkedMap)
12211 {
12212 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12213 if (cdm && cdm->isLinkableInProject())
12214 {
12215 cdm->writeTagFile(tagFile);
12216 }
12217 }
12218 // for each concept
12219 for (const auto &cd : *Doxygen::conceptLinkedMap)
12220 {
12221 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12222 if (cdm && cdm->isLinkableInProject())
12223 {
12224 cdm->writeTagFile(tagFile);
12225 }
12226 }
12227 // for each namespace
12228 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12229 {
12231 if (ndm && nd->isLinkableInProject())
12232 {
12233 ndm->writeTagFile(tagFile);
12234 }
12235 }
12236 // for each group
12237 for (const auto &gd : *Doxygen::groupLinkedMap)
12238 {
12239 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12240 }
12241 // for each module
12242 for (const auto &mod : ModuleManager::instance().modules())
12243 {
12244 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12245 }
12246 // for each page
12247 for (const auto &pd : *Doxygen::pageLinkedMap)
12248 {
12249 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12250 }
12251 // for requirements
12253 // for each directory
12254 for (const auto &dd : *Doxygen::dirLinkedMap)
12255 {
12256 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12257 }
12258 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12259
12260 tagFile << "</tagfile>\n";
12261}
12262
12263static void exitDoxygen() noexcept
12264{
12265 if (!g_successfulRun) // premature exit
12266 {
12267 Dir thisDir;
12268 msg("Exiting...\n");
12269 if (!Doxygen::filterDBFileName.empty())
12270 {
12271 thisDir.remove(Doxygen::filterDBFileName.str());
12272 }
12273 }
12274}
12275
12276static DString createOutputDirectory(const DString &baseDirName,
12277 const DString &formatDirName,
12278 const char *defaultDirName)
12279{
12280 DString result = formatDirName;
12281 if (result.empty())
12282 {
12283 result = baseDirName + defaultDirName;
12284 }
12285 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12286 {
12287 result.prepend(baseDirName+"/");
12288 }
12289 Dir formatDir(result.str());
12290 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12291 {
12292 term("Could not create output directory {}\n", result);
12293 }
12294 return result;
12295}
12296
12298{
12299 StringUnorderedSet killSet;
12300
12301 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12302 bool alwaysRecursive = Config_getBool(RECURSIVE);
12303 StringUnorderedSet excludeNameSet;
12304
12305 // gather names of all files in the include path
12306 g_s.begin("Searching for include files...\n");
12307 killSet.clear();
12308 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12309 for (const auto &s : includePathList)
12310 {
12311 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12312 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12313 Config_getList(INCLUDE_FILE_PATTERNS);
12314 readFileOrDirectory(s, // s
12316 nullptr, // exclSet
12317 &pl, // patList
12318 &exclPatterns, // exclPatList
12319 nullptr, // resultList
12320 nullptr, // resultSet
12321 false, // INCLUDE_PATH isn't recursive
12322 true, // errorIfNotExist
12323 &killSet); // killSet
12324 }
12325 g_s.end();
12326
12327 g_s.begin("Searching for example files...\n");
12328 killSet.clear();
12329 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12330 for (const auto &s : examplePathList)
12331 {
12332 readFileOrDirectory(s, // s
12334 nullptr, // exclSet
12335 &Config_getList(EXAMPLE_PATTERNS), // patList
12336 nullptr, // exclPatList
12337 nullptr, // resultList
12338 nullptr, // resultSet
12339 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12340 true, // errorIfNotExist
12341 &killSet); // killSet
12342 }
12343 g_s.end();
12344
12345 g_s.begin("Searching for images...\n");
12346 killSet.clear();
12347 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12348 for (const auto &s : imagePathList)
12349 {
12350 readFileOrDirectory(s, // s
12352 nullptr, // exclSet
12353 nullptr, // patList
12354 nullptr, // exclPatList
12355 nullptr, // resultList
12356 nullptr, // resultSet
12357 alwaysRecursive, // recursive
12358 true, // errorIfNotExist
12359 &killSet); // killSet
12360 }
12361 g_s.end();
12362
12363 g_s.begin("Searching for dot files...\n");
12364 killSet.clear();
12365 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12366 for (const auto &s : dotFileList)
12367 {
12368 readFileOrDirectory(s, // s
12370 nullptr, // exclSet
12371 nullptr, // patList
12372 nullptr, // exclPatList
12373 nullptr, // resultList
12374 nullptr, // resultSet
12375 alwaysRecursive, // recursive
12376 true, // errorIfNotExist
12377 &killSet); // killSet
12378 }
12379 g_s.end();
12380
12381 g_s.begin("Searching for msc files...\n");
12382 killSet.clear();
12383 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12384 for (const auto &s : mscFileList)
12385 {
12386 readFileOrDirectory(s, // s
12388 nullptr, // exclSet
12389 nullptr, // patList
12390 nullptr, // exclPatList
12391 nullptr, // resultList
12392 nullptr, // resultSet
12393 alwaysRecursive, // recursive
12394 true, // errorIfNotExist
12395 &killSet); // killSet
12396 }
12397 g_s.end();
12398
12399 g_s.begin("Searching for dia files...\n");
12400 killSet.clear();
12401 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12402 for (const auto &s : diaFileList)
12403 {
12404 readFileOrDirectory(s, // s
12406 nullptr, // exclSet
12407 nullptr, // patList
12408 nullptr, // exclPatList
12409 nullptr, // resultList
12410 nullptr, // resultSet
12411 alwaysRecursive, // recursive
12412 true, // errorIfNotExist
12413 &killSet); // killSet
12414 }
12415 g_s.end();
12416
12417 g_s.begin("Searching for plantuml files...\n");
12418 killSet.clear();
12419 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12420 for (const auto &s : plantUmlFileList)
12421 {
12422 readFileOrDirectory(s, // s
12424 nullptr, // exclSet
12425 nullptr, // patList
12426 nullptr, // exclPatList
12427 nullptr, // resultList
12428 nullptr, // resultSet
12429 alwaysRecursive, // recursive
12430 true, // errorIfNotExist
12431 &killSet); // killSet
12432 }
12433 g_s.end();
12434
12435 g_s.begin("Searching for mermaid files...\n");
12436 killSet.clear();
12437 const StringVector &mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12438 for (const auto &s : mermaidFileList)
12439 {
12440 readFileOrDirectory(s, // s
12442 nullptr, // exclSet
12443 nullptr, // patList
12444 nullptr, // exclPatList
12445 nullptr, // resultList
12446 nullptr, // resultSet
12447 alwaysRecursive, // recursive
12448 true, // errorIfNotExist
12449 &killSet); // killSet
12450 }
12451 g_s.end();
12452
12453 g_s.begin("Searching for files to exclude\n");
12454 const StringVector &excludeList = Config_getList(EXCLUDE);
12455 for (const auto &s : excludeList)
12456 {
12457 readFileOrDirectory(s, // s
12458 nullptr, // fnDict
12459 nullptr, // exclSet
12460 &Config_getList(FILE_PATTERNS), // patList
12461 nullptr, // exclPatList
12462 nullptr, // resultList
12463 &excludeNameSet, // resultSet
12464 alwaysRecursive, // recursive
12465 false); // errorIfNotExist
12466 }
12467 g_s.end();
12468
12469 /**************************************************************************
12470 * Determine Input Files *
12471 **************************************************************************/
12472
12473 g_s.begin("Searching INPUT for files to process...\n");
12474 killSet.clear();
12475 Doxygen::inputPaths.clear();
12476 const StringVector &inputList=Config_getList(INPUT);
12477 for (const auto &s : inputList)
12478 {
12479 DString path = s;
12480 size_t l = path.length();
12481 if (l>0)
12482 {
12483 // strip trailing slashes
12484 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12485
12487 path, // s
12489 &excludeNameSet, // exclSet
12490 &Config_getList(FILE_PATTERNS), // patList
12491 &exclPatterns, // exclPatList
12492 &g_inputFiles, // resultList
12493 nullptr, // resultSet
12494 alwaysRecursive, // recursive
12495 true, // errorIfNotExist
12496 &killSet, // killSet
12497 &Doxygen::inputPaths); // paths
12498 }
12499 }
12500
12501 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12502 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12503 {
12504 if (fileName->size()>1)
12505 {
12506 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12507 {
12508 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12509 });
12510 }
12511 }
12512 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12514 [](const auto &f1,const auto &f2)
12515 {
12516 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12517 });
12518 if (Doxygen::inputNameLinkedMap->empty())
12519 {
12520 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12521 }
12522 g_s.end();
12523}
12524
12525
12527{
12528 if (Config_getBool(MARKDOWN_SUPPORT))
12529 {
12530 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12531 if (mdfileAsMainPage.empty()) return;
12532 FileInfo fi(mdfileAsMainPage.data());
12533 if (!fi.exists())
12534 {
12535 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12536 return;
12537 }
12538 bool ambig = false;
12539 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12540 {
12541 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12542 return;
12543 }
12544 }
12545}
12546
12548{
12549 AUTO_TRACE();
12550 std::atexit(exitDoxygen);
12551
12552 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12553
12554#if USE_LIBCLANG
12555 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12556#endif
12557
12558 // we would like to show the versionString earlier, but we first have to handle the configuration file
12559 // to know the value of the QUIET setting.
12560 DString versionString = getFullVersion();
12561 msg("Doxygen version used: {}\n",versionString);
12562
12564
12565 /**************************************************************************
12566 * Make sure the output directory exists
12567 **************************************************************************/
12568 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12569 if (!g_singleComment)
12570 {
12571 if (outputDirectory.empty())
12572 {
12573 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12574 }
12575 else
12576 {
12577 Dir dir(outputDirectory.str());
12578 if (!dir.exists())
12579 {
12581 if (!dir.mkdir(outputDirectory.str()))
12582 {
12583 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12584 "exist and cannot be created\n",outputDirectory);
12585 }
12586 else
12587 {
12588 msg("Notice: Output directory '{}' does not exist. "
12589 "I have created it for you.\n", outputDirectory);
12590 }
12591 dir.setPath(outputDirectory.str());
12592 }
12593 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12594 }
12595 }
12596 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12597
12598 /**************************************************************************
12599 * Initialize global lists and dictionaries
12600 **************************************************************************/
12601
12602#ifdef HAS_SIGNALS
12603 signal(SIGINT, stopDoxygen);
12604#endif
12605
12606 uint32_t pid = Portable::pid();
12607 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12608 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12609
12610 /**************************************************************************
12611 * Check/create output directories *
12612 **************************************************************************/
12613
12614 bool generateHtml = Config_getBool(GENERATE_HTML);
12615 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12616 bool generateXml = Config_getBool(GENERATE_XML);
12617 bool generateLatex = Config_getBool(GENERATE_LATEX);
12618 bool generateRtf = Config_getBool(GENERATE_RTF);
12619 bool generateMan = Config_getBool(GENERATE_MAN);
12620 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12621 DString htmlOutput;
12622 DString docbookOutput;
12623 DString xmlOutput;
12624 DString latexOutput;
12625 DString rtfOutput;
12626 DString manOutput;
12627 DString sqlOutput;
12628
12629 if (!g_singleComment)
12630 {
12631 if (generateHtml)
12632 {
12633 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12634 Config_updateString(HTML_OUTPUT,htmlOutput);
12635
12636 DString sitemapUrl = Config_getString(SITEMAP_URL);
12637 bool generateSitemap = !sitemapUrl.empty();
12638 if (generateSitemap && !sitemapUrl.endsWith("/"))
12639 {
12640 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12641 }
12642
12643 // add HTML indexers that are enabled
12644 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12645 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12646 bool generateQhp = Config_getBool(GENERATE_QHP);
12647 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12648 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12649 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12650 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12651 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12652 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12653 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12654 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12657 }
12658
12659 if (generateDocbook)
12660 {
12661 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12662 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12663 }
12664
12665 if (generateXml)
12666 {
12667 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12668 Config_updateString(XML_OUTPUT,xmlOutput);
12669 }
12670
12671 if (generateLatex)
12672 {
12673 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12674 Config_updateString(LATEX_OUTPUT,latexOutput);
12675 }
12676
12677 if (generateRtf)
12678 {
12679 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12680 Config_updateString(RTF_OUTPUT,rtfOutput);
12681 }
12682
12683 if (generateMan)
12684 {
12685 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12686 Config_updateString(MAN_OUTPUT,manOutput);
12687 }
12688
12689 if (generateSql)
12690 {
12691 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12692 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12693 }
12694 }
12695
12696 if (Config_getBool(HAVE_DOT))
12697 {
12698 DString curFontPath = Config_getString(DOT_FONTPATH);
12699 if (curFontPath.empty())
12700 {
12701 Portable::getenv("DOTFONTPATH");
12702 DString newFontPath = ".";
12703 if (!curFontPath.empty())
12704 {
12705 newFontPath+=Portable::pathListSeparator();
12706 newFontPath+=curFontPath;
12707 }
12708 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12709 }
12710 else
12711 {
12712 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12713 }
12714 }
12715
12716 /**************************************************************************
12717 * Handle layout file *
12718 **************************************************************************/
12719
12721 DString layoutFileName = Config_getString(LAYOUT_FILE);
12722 bool defaultLayoutUsed = false;
12723 if (layoutFileName.empty())
12724 {
12725 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12726 defaultLayoutUsed = true;
12727 }
12728 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12729
12730 FileInfo fi(layoutFileName.str());
12731 if (fi.exists())
12732 {
12733 msg("Parsing layout file {}...\n",layoutFileName);
12734 LayoutDocManager::instance().parse(layoutFileName);
12735 }
12736 else if (!defaultLayoutUsed)
12737 {
12738 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12739 }
12740 printLayout();
12741
12742 /**************************************************************************
12743 * Read and preprocess input *
12744 **************************************************************************/
12745
12746 // prevent search in the output directories
12747 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12748 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12749 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12750 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12751 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12752 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12753 if (generateMan) exclPatterns.push_back(manOutput.str());
12754 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12755
12756 if (!g_singleComment)
12757 {
12759
12761 }
12762
12763 // Notice: the order of the function calls below is very important!
12764
12765 if (generateHtml && !Config_getBool(USE_MATHJAX))
12766 {
12768 }
12769 if (generateRtf)
12770 {
12772 }
12773 if (generateDocbook)
12774 {
12776 }
12777
12779
12780 /**************************************************************************
12781 * Handle Tag Files *
12782 **************************************************************************/
12783
12784 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12785
12786 if (!g_singleComment)
12787 {
12788 msg("Reading and parsing tag files\n");
12789 const StringVector &tagFileList = Config_getList(TAGFILES);
12790 for (const auto &s : tagFileList)
12791 {
12792 readTagFile(root,s.c_str());
12793 }
12794 }
12795
12796 /**************************************************************************
12797 * Parse source files *
12798 **************************************************************************/
12799
12800 addSTLSupport(root);
12801
12802 g_s.begin("Parsing files\n");
12803 if (g_singleComment)
12804 {
12805 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12806 if (g_commentFileName=="-")
12807 {
12808 std::string text = fileToString(g_commentFileName).str();
12809 addTerminalCharIfMissing(text,'\n');
12810 generateHtmlForComment("stdin.md",text);
12811 }
12812 else if (FileInfo(g_commentFileName.str()).isFile())
12813 {
12814 std::string text;
12816 addTerminalCharIfMissing(text,'\n');
12818 }
12819 else
12820 {
12821 }
12823 exit(0);
12824 }
12825 else
12826 {
12827 if (Config_getInt(NUM_PROC_THREADS)==1)
12828 {
12830 }
12831 else
12832 {
12834 }
12835 }
12836 g_s.end();
12837
12838 /**************************************************************************
12839 * Gather information *
12840 **************************************************************************/
12841
12842 g_s.begin("Building macro definition list...\n");
12844 g_s.end();
12845
12846 g_s.begin("Building group list...\n");
12847 buildGroupList(root.get());
12848 organizeSubGroups(root.get());
12849 g_s.end();
12850
12851 g_s.begin("Building directory list...\n");
12853 findDirDocumentation(root.get());
12854 g_s.end();
12855
12856 g_s.begin("Building namespace list...\n");
12857 buildNamespaceList(root.get());
12858 findUsingDirectives(root.get());
12859 g_s.end();
12860
12861 g_s.begin("Building file list...\n");
12862 buildFileList(root.get());
12863 g_s.end();
12864
12865 g_s.begin("Building class list...\n");
12866 buildClassList(root.get());
12867 g_s.end();
12868
12869 g_s.begin("Building concept list...\n");
12870 buildConceptList(root.get());
12871 g_s.end();
12872
12873 // build list of using declarations here (global list)
12874 buildListOfUsingDecls(root.get());
12875 g_s.end();
12876
12877 g_s.begin("Computing nesting relations for classes...\n");
12879 g_s.end();
12880 // 1.8.2-20121111: no longer add nested classes to the group as well
12881 //distributeClassGroupRelations();
12882
12883 // calling buildClassList may result in cached relations that
12884 // become invalid after resolveClassNestingRelations(), that's why
12885 // we need to clear the cache here
12887 // we don't need the list of using declaration anymore
12888 g_usingDeclarations.clear();
12889
12890 g_s.begin("Associating documentation with classes...\n");
12891 buildClassDocList(root.get());
12892 g_s.end();
12893
12894 g_s.begin("Associating documentation with concepts...\n");
12895 buildConceptDocList(root.get());
12897 g_s.end();
12898
12899 g_s.begin("Associating documentation with modules...\n");
12900 findModuleDocumentation(root.get());
12901 g_s.end();
12902
12903 g_s.begin("Building example list...\n");
12904 buildExampleList(root.get());
12905 g_s.end();
12906
12907 g_s.begin("Searching for enumerations...\n");
12908 findEnums(root.get());
12909 g_s.end();
12910
12911 // Since buildVarList calls isVarWithConstructor
12912 // and this calls getResolvedClass we need to process
12913 // typedefs first so the relations between classes via typedefs
12914 // are properly resolved. See bug 536385 for an example.
12915 g_s.begin("Searching for documented typedefs...\n");
12916 buildTypedefList(root.get());
12917 g_s.end();
12918
12919 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12920 {
12921 g_s.begin("Searching for documented sequences...\n");
12922 buildSequenceList(root.get());
12923 g_s.end();
12924
12925 g_s.begin("Searching for documented dictionaries...\n");
12926 buildDictionaryList(root.get());
12927 g_s.end();
12928 }
12929
12930 g_s.begin("Searching for members imported via using declarations...\n");
12931 // this should be after buildTypedefList in order to properly import
12932 // used typedefs
12933 findUsingDeclarations(root.get(),true); // do for python packages first
12934 findUsingDeclarations(root.get(),false); // then the rest
12935 g_s.end();
12936
12937 g_s.begin("Searching for included using directives...\n");
12939 g_s.end();
12940
12941 g_s.begin("Searching for documented variables...\n");
12942 buildVarList(root.get());
12943 g_s.end();
12944
12945 g_s.begin("Building interface member list...\n");
12946 buildInterfaceAndServiceList(root.get()); // UNO IDL
12947
12948 g_s.begin("Building member list...\n"); // using class info only !
12949 buildFunctionList(root.get());
12950 g_s.end();
12951
12952 g_s.begin("Searching for friends...\n");
12953 findFriends();
12954 g_s.end();
12955
12956 g_s.begin("Searching for documented defines...\n");
12957 findDefineDocumentation(root.get());
12958 g_s.end();
12959
12960 g_s.begin("Computing class inheritance relations...\n");
12961 findClassEntries(root.get());
12963 g_s.end();
12964
12965 g_s.begin("Computing class usage relations...\n");
12967 g_s.end();
12968
12969 g_s.begin("Flushing cached template relations that have become invalid...\n");
12971 g_s.end();
12972
12973 g_s.begin("Warn for undocumented namespaces...\n");
12975 g_s.end();
12976
12977 g_s.begin("Computing class relations...\n");
12980 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12981 {
12983 }
12985 g_classEntries.clear();
12986 g_s.end();
12987
12988 g_s.begin("Add enum values to enums...\n");
12989 addEnumValuesToEnums(root.get());
12990 findEnumDocumentation(root.get());
12991 g_s.end();
12992
12993 g_s.begin("Searching for member function documentation...\n");
12994 findObjCMethodDefinitions(root.get());
12995 findMemberDocumentation(root.get()); // may introduce new members !
12996 findUsingDeclImports(root.get()); // may introduce new members !
12997 g_usingClassMap.clear();
13001 g_s.end();
13002
13003 // moved to after finding and copying documentation,
13004 // as this introduces new members see bug 722654
13005 g_s.begin("Creating members for template instances...\n");
13007 g_s.end();
13008
13009 g_s.begin("Searching for tag less structs...\n");
13011 g_s.end();
13012
13013 g_s.begin("Building page list...\n");
13014 buildPageList(root.get());
13015 g_s.end();
13016
13017 g_s.begin("Building requirements list...\n");
13018 buildRequirementsList(root.get());
13019 g_s.end();
13020
13021 g_s.begin("Search for main page...\n");
13022 findMainPage(root.get());
13023 findMainPageTagFiles(root.get());
13024 g_s.end();
13025
13026 g_s.begin("Computing page relations...\n");
13027 computePageRelations(root.get());
13029 g_s.end();
13030
13031 g_s.begin("Determining the scope of groups...\n");
13032 findGroupScope(root.get());
13033 g_s.end();
13034
13035 g_s.begin("Computing module relations...\n");
13036 auto &mm = ModuleManager::instance();
13037 mm.resolvePartitions();
13038 mm.resolveImports();
13039 mm.collectExportedSymbols();
13040 g_s.end();
13041
13042 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13043 {
13044 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13045 n2->memberName().data()+getPrefixIndex(n2->memberName())
13046 )<0;
13047 };
13048
13049 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13050 {
13051 if (Config_getBool(SORT_BY_SCOPE_NAME))
13052 {
13053 return dstricmp_sort(c1->name(), c2->name())<0;
13054 }
13055 else
13056 {
13057 int i = dstricmp_sort(c1->className(), c2->className());
13058 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13059 }
13060 };
13061
13062 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13063 {
13064 return dstricmp_sort(n1->name(),n2->name())<0;
13065 };
13066
13067 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13068 {
13069 return dstricmp_sort(c1->name(),c2->name())<0;
13070 };
13071
13072 g_s.begin("Sorting lists...\n");
13073 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13075 memberNameComp);
13076 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13078 memberNameComp);
13079 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13081 classComp);
13082 std::stable_sort(Doxygen::classLinkedMap->begin(),
13084 classComp);
13085 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13087 conceptComp);
13088 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13090 namespaceComp);
13091 g_s.end();
13092
13093 g_s.begin("Determining which enums are documented\n");
13095 g_s.end();
13096
13097 g_s.begin("Computing member relations...\n");
13100 g_s.end();
13101
13102 g_s.begin("Building full member lists recursively...\n");
13104 g_s.end();
13105
13106 g_s.begin("Adding members to member groups.\n");
13108 g_s.end();
13109
13110 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13111 {
13112 g_s.begin("Distributing member group documentation.\n");
13114 g_s.end();
13115 }
13116
13117 g_s.begin("Computing member references...\n");
13119 g_s.end();
13120
13121 if (Config_getBool(INHERIT_DOCS))
13122 {
13123 g_s.begin("Inheriting documentation...\n");
13125 g_s.end();
13126 }
13127
13128
13129 // compute the shortest possible names of all files
13130 // without losing the uniqueness of the file names.
13131 g_s.begin("Generating disk names...\n");
13133 g_s.end();
13134
13135 g_s.begin("Adding source references...\n");
13137 g_s.end();
13138
13139 g_s.begin("Adding xrefitems...\n");
13142 g_s.end();
13143
13144 g_s.begin("Adding requirements...\n");
13147 g_s.end();
13148
13149 g_s.begin("Sorting member lists...\n");
13151 g_s.end();
13152
13153 g_s.begin("Setting anonymous enum type...\n");
13155 g_s.end();
13156
13157 g_s.begin("Computing dependencies between directories...\n");
13159 g_s.end();
13160
13161 g_s.begin("Generating citations page...\n");
13163 g_s.end();
13164
13165 g_s.begin("Counting data structures...\n");
13167 g_s.end();
13168
13169 g_s.begin("Resolving user defined references...\n");
13171 g_s.end();
13172
13173 g_s.begin("Finding anchors and sections in the documentation...\n");
13175 g_s.end();
13176
13177 g_s.begin("Transferring function references...\n");
13179 g_s.end();
13180
13181 g_s.begin("Combining using relations...\n");
13183 g_s.end();
13184
13186 g_s.begin("Adding members to index pages...\n");
13188 addToIndices();
13189 g_s.end();
13190
13191 g_s.begin("Correcting members for VHDL...\n");
13193 g_s.end();
13194
13195 g_s.begin("Computing tooltip texts...\n");
13197 g_s.end();
13198
13199 if (Config_getBool(SORT_GROUP_NAMES))
13200 {
13201 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13203 [](const auto &g1,const auto &g2)
13204 { return g1->groupTitle() < g2->groupTitle(); });
13205
13206 for (const auto &gd : *Doxygen::groupLinkedMap)
13207 {
13208 gd->sortSubGroups();
13209 }
13210 }
13211
13212 printNavTree(root.get(),0);
13214}
13215
13217{
13218 AUTO_TRACE();
13219 /**************************************************************************
13220 * Initialize output generators *
13221 **************************************************************************/
13222
13223 /// add extra languages for which we can only produce syntax highlighted code
13225
13226 //// dump all symbols
13227 if (g_dumpSymbolMap)
13228 {
13229 dumpSymbolMap();
13230 exit(0);
13231 }
13232
13233 bool generateHtml = Config_getBool(GENERATE_HTML);
13234 bool generateLatex = Config_getBool(GENERATE_LATEX);
13235 bool generateMan = Config_getBool(GENERATE_MAN);
13236 bool generateRtf = Config_getBool(GENERATE_RTF);
13237 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13238
13239
13241 if (generateHtml)
13242 {
13246 }
13247 if (generateLatex)
13248 {
13251 }
13252 if (generateDocbook)
13253 {
13256 }
13257 if (generateMan)
13258 {
13261 }
13262 if (generateRtf)
13263 {
13266 }
13267 if (Config_getBool(USE_HTAGS))
13268 {
13269 Htags::useHtags = true;
13270 DString htmldir = Config_getString(HTML_OUTPUT);
13271 if (!Htags::execute(htmldir))
13272 err("USE_HTAGS is YES but htags(1) failed. \n");
13273 else if (!Htags::loadFilemap(htmldir))
13274 err("htags(1) ended normally but failed to load the filemap. \n");
13275 }
13276
13277 /**************************************************************************
13278 * Generate documentation *
13279 **************************************************************************/
13280
13281 g_s.begin("Generating style sheet...\n");
13282 //printf("writing style info\n");
13283 g_outputList->writeStyleInfo(0); // write first part
13284 g_s.end();
13285
13286 bool searchEngine = Config_getBool(SEARCHENGINE);
13287 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13288
13289 g_s.begin("Generating search indices...\n");
13290 if (searchEngine && !serverBasedSearch && generateHtml)
13291 {
13293 }
13294
13295 // generate search indices (need to do this before writing other HTML
13296 // pages as these contain a drop down menu with options depending on
13297 // what categories we find in this function.
13298 if (generateHtml && searchEngine)
13299 {
13300 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13301 Dir searchDir(searchDirName.str());
13302 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13303 {
13304 term("Could not create search results directory '{}' $PWD='{}'\n",
13305 searchDirName,Dir::currentDirPath());
13306 }
13307 HtmlGenerator::writeSearchData(searchDirName);
13308 if (!serverBasedSearch) // client side search index
13309 {
13311 }
13312 }
13313 g_s.end();
13314
13315 // copy static stuff
13316 if (generateHtml)
13317 {
13319 copyLogo(Config_getString(HTML_OUTPUT),true);
13320 copyIcon(Config_getString(HTML_OUTPUT),true);
13321 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13322 }
13323 if (generateLatex)
13324 {
13326 copyLogo(Config_getString(LATEX_OUTPUT),false);
13327 copyIcon(Config_getString(LATEX_OUTPUT),false);
13328 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13329 }
13330 if (generateDocbook)
13331 {
13332 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13333 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13334 }
13335 if (generateRtf)
13336 {
13337 copyLogo(Config_getString(RTF_OUTPUT),false);
13338 copyIcon(Config_getString(RTF_OUTPUT),false);
13339 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13340 }
13341
13343 if (fm.hasFormulas() && generateHtml
13344 && !Config_getBool(USE_MATHJAX))
13345 {
13346 g_s.begin("Generating images for formulas in HTML...\n");
13347 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13349 g_s.end();
13350 }
13351 if (fm.hasFormulas() && generateRtf)
13352 {
13353 g_s.begin("Generating images for formulas in RTF...\n");
13355 g_s.end();
13356 }
13357
13358 if (fm.hasFormulas() && generateDocbook)
13359 {
13360 g_s.begin("Generating images for formulas in Docbook...\n");
13362 g_s.end();
13363 }
13364
13365 g_s.begin("Generating example documentation...\n");
13367 g_s.end();
13368
13369 g_s.begin("Generating file sources...\n");
13371 g_s.end();
13372
13373 g_s.begin("Counting members...\n");
13374 // needs to be done after generating the sources
13375 // but before generating the compound documentation, see bug #12233
13376 countMembers();
13377 g_s.end();
13378
13379 g_s.begin("Generating file documentation...\n");
13381 g_s.end();
13382
13383 g_s.begin("Generating page documentation...\n");
13385 g_s.end();
13386
13387 g_s.begin("Generating group documentation...\n");
13389 g_s.end();
13390
13391 g_s.begin("Generating class documentation...\n");
13393 g_s.end();
13394
13395 g_s.begin("Generating concept documentation...\n");
13397 g_s.end();
13398
13399 g_s.begin("Generating module documentation...\n");
13401 g_s.end();
13402
13403 g_s.begin("Generating namespace documentation...\n");
13405 g_s.end();
13406
13407 if (Config_getBool(GENERATE_LEGEND))
13408 {
13409 g_s.begin("Generating graph info page...\n");
13411 g_s.end();
13412 }
13413
13414 g_s.begin("Generating directory documentation...\n");
13416 g_s.end();
13417
13418 if (g_outputList->size()>0)
13419 {
13421 }
13422
13423 g_s.begin("finalizing index lists...\n");
13425 g_s.end();
13426
13427 g_s.begin("writing tag file...\n");
13428 writeTagFile();
13429 g_s.end();
13430
13431 if (Config_getBool(GENERATE_XML))
13432 {
13433 g_s.begin("Generating XML output...\n");
13435 generateXML();
13437 g_s.end();
13438 }
13439 if (Config_getBool(GENERATE_SQLITE3))
13440 {
13441 g_s.begin("Generating SQLITE3 output...\n");
13443 g_s.end();
13444 }
13445
13446 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13447 {
13448 g_s.begin("Generating AutoGen DEF output...\n");
13449 generateDEF();
13450 g_s.end();
13451 }
13452 if (Config_getBool(GENERATE_PERLMOD))
13453 {
13454 g_s.begin("Generating Perl module output...\n");
13456 g_s.end();
13457 }
13458 if (generateHtml && searchEngine && serverBasedSearch)
13459 {
13460 g_s.begin("Generating search index\n");
13461 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13462 {
13464 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13465 }
13466 else // write data for external search index
13467 {
13469 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13470 if (searchDataFile.empty())
13471 {
13472 searchDataFile="searchdata.xml";
13473 }
13474 if (!Portable::isAbsolutePath(searchDataFile.data()))
13475 {
13476 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13477 }
13478 Doxygen::searchIndex.write(searchDataFile);
13479 }
13480 g_s.end();
13481 }
13482
13483 if (generateRtf)
13484 {
13485 g_s.begin("Combining RTF output...\n");
13486 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13487 {
13488 err("An error occurred during post-processing the RTF files!\n");
13489 }
13490 g_s.end();
13491 }
13492
13493 if (PlantumlManager::instance().needToRun())
13494 {
13495 g_s.begin("Running plantuml with JAVA...\n");
13497 g_s.end();
13498 }
13499
13500 if (MermaidManager::instance().needToRun())
13501 {
13502 g_s.begin("Running mermaid (mmdc)...\n");
13504 g_s.end();
13505 }
13506
13507 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13508 {
13509 g_s.begin("Running dot...\n");
13511 g_s.end();
13512 }
13513
13514 if (generateHtml &&
13515 Config_getBool(GENERATE_HTMLHELP) &&
13516 !Config_getString(HHC_LOCATION).empty())
13517 {
13518 g_s.begin("Running html help compiler...\n");
13520 g_s.end();
13521 }
13522
13523 if ( generateHtml &&
13524 Config_getBool(GENERATE_QHP) &&
13525 !Config_getString(QHG_LOCATION).empty())
13526 {
13527 g_s.begin("Running qhelpgenerator...\n");
13529 g_s.end();
13530 }
13531
13534
13536
13538 {
13539
13540 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13541 if (numThreads<1) numThreads=1;
13542 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13543 (static_cast<double>(Debug::elapsedTime())),
13544 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13545 );
13546 g_s.print();
13547
13549 msg("finished...\n");
13551 }
13552 else
13553 {
13554 msg("finished...\n");
13555 }
13556
13557
13558 /**************************************************************************
13559 * Start cleaning up *
13560 **************************************************************************/
13561
13563
13565 Dir thisDir;
13566 thisDir.remove(Doxygen::filterDBFileName.str());
13568 exitTracing();
13570 delete Doxygen::clangUsrMap;
13571 g_successfulRun=true;
13572
13573 //dumpDocNodeSizes();
13574}
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
bool hasParameters() const
Definition arguments.h:76
DString trailingReturnType() const
Definition arguments.h:114
bool isDeleted() const
Definition arguments.h:115
size_t size() const
Definition arguments.h:100
void setPureSpecifier(bool b)
Definition arguments.h:121
bool constSpecifier() const
Definition arguments.h:111
void setTrailingReturnType(const DString &s)
Definition arguments.cpp:36
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 DString requiresClause() const =0
virtual const MemberDef * getMemberByName(const DString &) const =0
Returns the member with the given name.
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
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 int isBaseClass(const ClassDef *bcd, bool followInstances, const DString &templSpec=DString()) const =0
Returns true iff bcd is a direct or indirect base class of this class.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template 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 DString compoundTypeString() const =0
Returns the type of compound as a string.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual std::unique_ptr< ClassDef > deepCopy(const DString &name) const =0
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 DString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) 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 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 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 addUsedClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const DString &spec)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual void setClassName(const DString &name)=0
virtual void setFileDef(FileDef *fd)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual ClassDef * insertTemplateInstance(const DString &fileName, int startLine, int startColumn, const DString &templSpec, bool &freshInstance)=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void makeTemplateArgument(bool b=true)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertUsedFile(const FileDef *)=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 setMetaData(const DString &md)=0
virtual void findSectionsInDocumentation()=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const DString &templSpec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const DString &t=DString())=0
virtual void addUsedByClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void setSubGrouping(bool enabled)=0
virtual void setRequiresClause(const DString &req)=0
virtual void insertBaseClass(ClassDef *, const DString &name, Protection p, Specifier s, const DString &t=DString())=0
virtual void addCodePart(const DString &code, int lineNr, int colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void addDocPart(const DString &doc, int lineNr, int colNr)=0
virtual void setGroupId(int id)=0
virtual void setInitializer(const DString &init)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
void clear()
Definition dstring.h:219
DString & setNum(short n)
Definition dstring.h:541
void resize(size_t newlen)
Definition dstring.h:214
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
DString lower() const
Definition dstring.h:331
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:145
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:228
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:183
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:675
DString right(size_t len) const
Definition dstring.h:316
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:159
DString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition dstring.h:283
DString & prepend(const char *s)
Definition dstring.h:504
int contains(char c, bool cs=true) const
Definition dstring.cpp:76
size_t find(char c, size_t pos=0) const
Definition dstring.h:244
DString & sprintf(const char *format,...)
Definition dstring.cpp:29
@ ExplicitSize
Definition dstring.h:136
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:342
DString left(size_t len) const
Definition dstring.h:311
const std::string & str() const
Definition dstring.h:634
bool stripPrefix(const DString &prefix)
Definition dstring.h:295
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:162
bool startsWith(const char *s) const
Definition dstring.h:589
bool endsWith(const char *s) const
Definition dstring.h:606
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:156
@ 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 DString &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 DString briefDescription(bool abbreviate=false) const =0
virtual DString inbodyFile() const =0
virtual int getEndBodyLine() const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual DString inbodyDocumentation() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual const DString & localName() 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 bool isAnonymous() const =0
virtual DString displayName(bool includeScope=true) const =0
virtual DString qualifiedName() const =0
virtual bool isHidden() const =0
virtual DString anchor() const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual bool isArtificial() const =0
virtual Definition * getOuterScope() const =0
virtual DString docFile() const =0
virtual int getStartBodyLine() const =0
virtual int getDefColumn() const =0
virtual bool isReference() const =0
virtual const Definition * findInnerCompound(const DString &name) const =0
virtual DString getOutputFileBase() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setName(const DString &name)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setDefFile(const DString &df, int defLine, int defColumn)=0
virtual void setHidden(bool b)=0
virtual void setReference(const DString &r)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setInbodyDocumentation(const DString &d, const DString &docFile, int docLine)=0
virtual void setDocumentation(const DString &d, const DString &docFile, int docLine, bool stripWhiteSpace=true)=0
virtual void addInnerCompound(Definition *d)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setBriefDescription(const DString &b, const DString &briefFile, int briefLine)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setId(const DString &name)=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 DString filterDBFileName
Definition doxygen.h:131
static ParserManager * parserManager
Definition doxygen.h:129
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:138
static DString verifiedDotPath
Definition doxygen.h:137
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 MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:118
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 DString spaces
Definition doxygen.h:133
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 DString htmlFileExtension
Definition doxygen.h:122
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:199
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:229
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:233
int docLine
line number at which the documentation was found
Definition entry.h:203
DString includeName
include name (3 arg of \class)
Definition entry.h:201
DString bitfields
member's bit fields
Definition entry.h:194
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:217
void markAsProcessed() const
Definition entry.h:168
int endBodyLine
line number where the definition ends
Definition entry.h:220
DString write
property write accessor
Definition entry.h:214
bool exported
is the symbol exported from a C++20 module
Definition entry.h:190
const TagInfo * tagInfo() const
Definition entry.h:178
DString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:200
DString fileName
file this entry was extracted from
Definition entry.h:225
ArgumentLists tArgLists
template argument declarations
Definition entry.h:197
DString docFile
file in which the documentation was found
Definition entry.h:204
LocalToc localToc
Definition entry.h:235
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:182
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:122
DString args
member argument string
Definition entry.h:193
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:230
Entry * parent() const
Definition entry.h:135
DString inside
name of the class in which documents are found
Definition entry.h:215
DString doc
documentation block (partly parsed)
Definition entry.h:202
DString req
C++20 requires clause.
Definition entry.h:237
int startColumn
start column of entry in the source
Definition entry.h:227
bool explicitExternal
explicitly defined as external?
Definition entry.h:187
DString brief
brief description (doc block)
Definition entry.h:205
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:224
RelatesType relatesType
how relates is handled
Definition entry.h:212
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:223
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:191
int startLine
start line of entry in the source
Definition entry.h:226
ArgumentList argList
member arguments as a list
Definition entry.h:196
DString type
member type
Definition entry.h:174
int inbodyLine
line number at which the body doc was found
Definition entry.h:209
EntryType section
entry type (see Sections);
Definition entry.h:173
int bodyLine
line number of the body in the source
Definition entry.h:218
DString exception
throw specification
Definition entry.h:216
DString relates
related class (doc block)
Definition entry.h:211
int mGrpId
member group id
Definition entry.h:221
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:222
DString inbodyFile
file in which the body doc was found
Definition entry.h:210
Specifier virt
virtualness of the entry
Definition entry.h:192
DString metaData
Slice metadata.
Definition entry.h:236
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:238
DString name
member name
Definition entry.h:175
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:228
Protection protection
class protection
Definition entry.h:181
bool artificial
Artificially introduced item.
Definition entry.h:232
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:231
DString inbodyDocs
documentation inside the body of a function
Definition entry.h:208
int briefLine
line number at which the brief desc. was found
Definition entry.h:206
FileDef * fileDef() const
Definition entry.h:170
DString id
libclang id
Definition entry.h:234
int initLines
define/variable initializer lines to show
Definition entry.h:185
bool isStatic
static ?
Definition entry.h:186
TypeSpecifier spec
class/member specifiers
Definition entry.h:183
DString briefFile
file in which the brief desc. was found
Definition entry.h:207
DString read
property read accessor
Definition entry.h:213
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 setDiskName(const DString &name)=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 DString absFilePath() const =0
virtual const DString & docName() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =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 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 isSymLink() const
Definition fileinfo.cpp:77
FileInfo(const std::string &name)
Definition fileinfo.h:25
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
bool hasFormulas() const
Definition formula.cpp:719
void initFromRepository(const DString &dir)
Definition formula.cpp:59
void checkRepositories()
Definition formula.cpp:172
static FormulaManager & instance()
Definition formula.cpp:53
void generateImages(const DString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:634
A model of a group of symbols.
Definition groupdef.h:52
virtual DString 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 void setGroupTitle(const DString &newtitle)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:96
static void writeSearchData(const DString &dir)
Definition htmlgen.cpp:1421
static void init()
Definition htmlgen.cpp:1248
static void writeSearchPage()
Definition htmlgen.cpp:3238
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1589
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 writeExternalSearchPage()
Definition htmlgen.cpp:3337
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1583
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const DString 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
void initialize()
Definition indexlist.h:100
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor=DString(), const DString &title=DString())
Definition indexlist.h:117
void addIndex(As &&... args)
Add an index generator to the list, using a syntax similar to std::make_unique<T>().
Definition indexlist.h:97
void addImageFile(const DString &name)
Definition indexlist.h:123
void finalize()
Definition indexlist.h:103
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
void parse(const DString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1468
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1435
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 DString requiresClause() const =0
virtual bool isFriend() const =0
virtual DString argsString() const =0
virtual bool isForeign() const =0
virtual bool isRelated() const =0
virtual DString definition() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual const ClassDef * getClassDef() const =0
virtual DString excpString() const =0
virtual const ArgumentList & templateArguments() const =0
virtual const DString & initializer() 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 DString bitfieldString() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() 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 DString typeString() 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 void setRequiresClause(const DString &req)=0
virtual void setClassDefOfAnonymousType(const ClassDef *cd)=0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setDefinition(const DString &d)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual void setToAnonymousMember(MemberDef *m)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setArgsString(const DString &as)=0
virtual void setExplicitExternal(bool b, const DString &df, int line, int column)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=false)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setInitializer(const DString &i)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setRelatedAlso(ClassDef *cd)=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 overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setBitfields(const DString &s)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void setPrototype(bool p, const DString &df, int line, int column)=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 invalidateCachedArgumentTypes()=0
virtual void setDeclFile(const DString &df, int line, int column)=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
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:65
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:106
const MemberDef * find(const DString &name) const
Definition memberlist.h:93
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:250
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 findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void setFileName(const DString &fn)=0
virtual void insertMember(MemberDef *md)=0
virtual void setMetaData(const DString &m)=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 sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
void parseInput(const DString &, 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 DString &) override
Callback function called by the comment block scanner.
bool needsPreprocessing(const DString &) const override
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
Abstract interface for outline parsers.
Definition parserintf.h:42
virtual bool needsPreprocessing(const DString &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 DString &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
void disable(OutputType o)
void add()
Definition outputlist.h:352
void enable(OutputType o)
size_t size() const
Definition outputlist.h:361
void docify(const DString &s)
Definition outputlist.h:437
void writeStyleInfo(int part)
Definition outputlist.h:395
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:752
void startContents()
Definition outputlist.h:618
A model of a page symbol.
Definition pagedef.h:26
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const DString &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
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:254
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:216
ParserManager(const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Create the parser manager.
Definition parserintf.h:202
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:245
static PlantumlManager & instance()
Definition plantuml.cpp:230
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:388
void processFile(const DString &fileName, const std::string &input, std::string &output)
Definition pre.l:4276
void addSearchDir(const DString &dir)
Definition pre.l:4258
Definition qhp.h:27
static DString getQchFileName()
Definition qhp.cpp:426
static const DString qhpFileName
Definition qhp.h:47
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:461
static bool preProcessFileInplace(const DString &path, const DString &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.
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
void write(const DString &file)
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
DString ref() const
Definition section.h:72
SectionInfo * replace(const DString &label, const DString &fileName, int lineNr, const DString &title, SectionType type, int level, const DString &ref=DString())
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)
ClassDefMutable * resolveClassMutable(const Definition *scope, const DString &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 ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
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
virtual DString trPage(bool first_capital, bool singular)=0
virtual DString trMainPage()=0
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 DString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:799
ClassDef * toClassDef(Definition *d)
std::unique_ptr< ClassDef > createClassDef(const DString &fileName, int startLine, int startColumn, const DString &name, ClassDef::CompoundType ct, const DString &ref, const DString &fName, bool isSymbol, bool isJavaEnum)
Factory method to create a new ClassDef object.
Definition classdef.cpp:573
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:95
std::map< std::string, int > TemplateNameMap
Definition classdef.h:93
ClassDefMutable * getClassMutable(const DString &key)
Definition classdef.h:470
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
Object representing the matching results.
Definition regex.h:154
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 DString &fileName, int startLine, int startColumn, const DString &name, const DString &tagRef, const DString &tagFile)
ConceptDefMutable * getConceptMutable(const DString &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 DString &decl, const SrcLangExt lang, DString &clName, DString &type, DString &name, DString &args, DString &funcTempList, DString &exceptions)
Definition declinfo.l:325
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:828
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:1118
void computeDirDependencies()
Definition dirdef.cpp:1192
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1209
#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:5411
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:5426
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:432
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6795
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2171
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9540
static MemberDef * addVariableToFile(const Entry *root, MemberType mtype, const DString &scope, const DString &type, const DString &name, const DString &args, bool fromAnnScope, MemberDef *fromAnnMemb)
Definition doxygen.cpp:2774
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9200
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3038
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6096
static void computeTemplateClassRelations()
Definition doxygen.cpp:5505
static std::string resolveSymlink(const std::string &path)
void adjustConfiguration()
adjust globals that depend on configuration settings.
static void findDEV(const MemberNameLinkedMap &mnsd)
Definition doxygen.cpp:8255
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1164
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()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5684
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:713
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2324
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:1495
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9740
void checkConfiguration()
check and resolve config options
static void processTagLessClasses(const Definition *root, const Container *cd, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const DString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1688
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:5012
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4942
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:472
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5460
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4621
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1325
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &funcDecl, const DString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6664
static void resolveClassNestingRelations()
Definition doxygen.cpp:1380
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5376
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8499
static void addMemberFunction(const Entry *root, MemberName *mn, const DString &scopeName, const DString &namespaceName, const DString &className, const DString &funcTyp, const DString &funcName, const DString &funcArgs, const DString &funcTempList, const DString &exceptions, const DString &type, const DString &args, bool isFriend, TypeSpecifier spec, const DString &relates, const DString &funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6308
static void generateExampleDocs()
void generateOutput()
static void stopDoxygen(int)
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6210
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5574
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4535
static void addMembersToMemberGroup()
Definition doxygen.cpp:9405
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9909
static void addLocalObjCMethod(const Entry *root, const DString &scopeName, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &exceptions, const DString &funcDecl, TypeSpecifier spec)
Definition doxygen.cpp:6254
static void distributeConceptGroups()
Definition doxygen.cpp:1347
static void copyExtraFiles(const StringVector &files, const DString &filesOption, const DString &outputOption, bool toIndex)
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2001
static void transferFunctionDocumentation()
Definition doxygen.cpp:4454
static void setAnonymousEnumType()
Definition doxygen.cpp:9145
static void sortMemberLists()
Definition doxygen.cpp:9050
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6838
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8627
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4584
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7644
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5698
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3523
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:447
static void generateFileDocs()
Definition doxygen.cpp:8863
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3241
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9653
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:4684
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7614
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9443
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7847
static void generatePageDocs()
static void resolveUserReferences()
Definition doxygen.cpp:9971
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9800
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:311
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const DString &fn, ClangTUParser *clangParser, bool newTU)
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3660
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3623
static void generateFileSources()
Definition doxygen.cpp:8697
static void generateClassDocs()
Definition doxygen.cpp:9298
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:4982
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1832
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2574
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9626
static void countMembers()
Definition doxygen.cpp:9159
void clearAll()
Definition doxygen.cpp:202
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4649
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:491
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2263
void searchInputFiles()
static void addOverloaded(const Entry *root, MemberName *mn, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &funcDecl, const DString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6727
static void findFriends()
Definition doxygen.cpp:4357
static void findEnums(const Entry *root)
Definition doxygen.cpp:7672
static void generateConfigFile(const DString &configFile, bool shortList, bool updateOnly=false)
static DString g_commentFileName
Definition doxygen.cpp:193
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9275
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8095
static void addListReferences()
Definition doxygen.cpp:5675
static void exitDoxygen() noexcept
static bool findGlobalMember(const Entry *root, const DString &namespaceName, const DString &type, const DString &name, const DString &tempArg, const DString &, const DString &decl, TypeSpecifier)
Definition doxygen.cpp:5888
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5354
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:361
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8289
static bool g_dumpSymbolMap
Definition doxygen.cpp:192
static void version(const bool extended)
static OutputList * g_outputList
Definition doxygen.cpp:189
static DString extractClassName(const Entry *root)
Definition doxygen.cpp:5385
static void findMainPage(Entry *root)
Definition doxygen.cpp:9839
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:897
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:686
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2014
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3692
static bool g_successfulRun
Definition doxygen.cpp:191
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8137
static void addSourceReferences()
Definition doxygen.cpp:8923
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1529
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2275
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6126
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9345
static void flushUnresolvedRelations()
Definition doxygen.cpp:9582
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9092
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 void findTemplateInstanceRelation(const Entry *root, Definition *context, ClassDefMutable *templateClass, const DString &templSpec, const TemplateNameMap &templateNames, bool isArtificial)
Definition doxygen.cpp:4891
static void findDocumentedEnumValues()
Definition doxygen.cpp:8281
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8331
static void computeClassRelations()
Definition doxygen.cpp:5480
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4052
static void checkPageRelations()
Definition doxygen.cpp:9951
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:3943
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1315
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8170
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9921
static void addMethodToClass(const Entry *root, ClassDefMutable *cd, const DString &rtype, const DString &rname, const DString &rargs, bool isFriend, Protection protection, bool stat, Specifier virt, TypeSpecifier spec, const DString &relates)
Definition doxygen.cpp:3807
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3098
static StringSet g_usingDeclarations
Definition doxygen.cpp:190
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3641
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9615
void readFileOrDirectory(const DString &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 generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1150
static void buildPageList(Entry *root)
Definition doxygen.cpp:9812
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5667
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:194
static void findSectionsInDocumentation()
Definition doxygen.cpp:9481
static void mergeCategories()
Definition doxygen.cpp:8646
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:199
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6112
static void buildFileList(const Entry *root)
Definition doxygen.cpp:503
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1140
static void usage(const DString &name, const DString &versionString)
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const DString &type, const DString &name, const DString &args, bool fromAnnScope, MemberDef *fromAnnMemb, Protection prot, Relationship related)
Definition doxygen.cpp:2591
static void findUsedTemplateInstances()
Definition doxygen.cpp:5444
static void computeTooltipTexts()
Definition doxygen.cpp:9099
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3309
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:4840
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8667
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5849
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7464
static void generateConceptDocs()
Definition doxygen.cpp:9324
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:4971
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9380
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:867
static void findTagLessClasses(std::set< const Definition * > &candidates, const Container *cd)
Definition doxygen.cpp:1767
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName)
Definition doxygen.cpp:1556
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2322
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1335
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5864
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5610
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static DString createOutputDirectory(const DString &baseDirName, const DString &formatDirName, const char *defaultDirName)
static void transferFunctionReferences()
Definition doxygen.cpp:4487
static void buildDefineList()
Definition doxygen.cpp:9002
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3755
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:782
static void computeMemberRelations()
Definition doxygen.cpp:8611
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2158
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8531
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
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:476
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:435
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:44
int dstricmp_sort(const char *str1, const char *str2)
Definition dstring.h:72
int dstrcmp(const char *str1, const char *str2)
Definition dstring.h:55
const char * qPrint(const char *s)
Definition dstring.h:769
#define ASSERT(x)
Definition dstring.h:29
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1966
std::unique_ptr< FileDef > createFileDef(const DString &p, const DString &n, const DString &ref, const DString &dn)
Definition filedef.cpp:268
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:44
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 DString &fileName, int line, const DString &name, const DString &title, const DString &refFileName)
Definition groupdef.cpp:178
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4102
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:396
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5816
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const DString &navPath)
Definition index.cpp:429
void startTitle(OutputList &ol, const DString &fileName, const DefinitionMutable *def)
Definition index.cpp:386
void startFile(OutputList &ol, const DString &name, bool isSource, const DString &manName, const DString &title, HighlightedItem hli, bool additionalIndices, const DString &altSidebarName, int hierarchyLevel, const DString &allMembersFile)
Definition index.cpp:403
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 DString &fileName)
Definition layout.cpp:1732
void printLayout()
Definition layout.cpp:1820
std::unique_ptr< MemberDef > createMemberDef(const DString &defFileName, int defLine, int defColumn, const DString &type, const DString &name, const DString &args, const DString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const DString &metaData)
Factory method to create a new instance of a MemberDef.
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::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:27
void initWarningFormat()
Definition message.cpp:236
void warn_flush()
Definition message.cpp:229
DString warn_line(const DString &file, int line)
Definition message.cpp:214
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 writeTemplate(TextStream &t, bool shortList, bool updateOnly=false)
void deinit()
void init()
void updateObsolete()
bool parse(const DString &fileName, bool update=false, CompareMode compareMode=CompareMode::Full)
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:516
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:479
double getSysElapsedTime()
Definition portable.cpp:97
void setenv(const DString &variable, const DString &value)
Definition portable.cpp:286
uint32_t pid()
Definition portable.cpp:248
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:105
int pclose(FILE *stream)
Definition portable.cpp:488
DString getenv(const DString &variable)
Definition portable.cpp:321
bool isAbsolutePath(const DString &fileName)
Definition portable.cpp:497
DString pathListSeparator()
Definition portable.cpp:383
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:648
const char * commandExtension()
Definition portable.cpp:461
void setShortDir()
Definition portable.cpp:553
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
std::unique_ptr< NamespaceDef > createNamespaceDef(const DString &defFileName, int defLine, int defColumn, const DString &name, const DString &ref, const DString &refFile, const DString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
void replaceNamespaceAliases(DString &name)
NamespaceDef * getResolvedNamespace(const DString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const DString &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:847
std::unique_ptr< PageDef > createPageDef(const DString &f, int l, const DString &n, const DString &d, const DString &t)
Definition pagedef.cpp:84
void generatePerlMod()
void setPerlModDoxyfile(const DString &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)
void initSearchIndexer()
void finalizeSearchIndexer()
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
DString defval
Definition arguments.h:46
DString array
Definition arguments.h:45
DString name
Definition arguments.h:44
DString type
Definition arguments.h:42
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
DString 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
DString 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 DString &htmldir)
Definition htags.cpp:38
static bool loadFilemap(const DString &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
DString tagName
Definition entry.h:105
DString fileName
Definition entry.h:106
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:52
void initTracing(const DString &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
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4407
bool findAndRemoveWord(DString &sentence, const char *word)
removes occurrences of whole word from sentence, while keeps internal spaces and reducing multiple se...
Definition util.cpp:5028
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5975
DString stripFromIncludePath(const DString &path)
Definition util.cpp:329
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:4636
DString filterTitle(const DString &title)
Definition util.cpp:5670
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2271
void addCodeOnlyMappings()
Definition util.cpp:5246
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:870
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:5355
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:218
void addRefItem(const RefItemVector &sli, const DString &key, const DString &prefix, const DString &name, const DString &title, const DString &args, const Definition *scope)
Definition util.cpp:4866
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6862
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5744
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:567
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:4958
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3545
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:881
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1297
DString showFileDefMatches(const FileNameLinkedMap *fnMap, const DString &n)
Definition util.cpp:3055
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const DString &n, bool &ambig)
Definition util.cpp:2917
DString getFileNameExtension(const DString &fn)
Definition util.cpp:5294
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:373
bool readInputFile(const DString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:5590
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4342
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:5252
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:2027
void initDefaultExtensionMapping()
Definition util.cpp:5179
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:4963
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1491
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3734
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:4569
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1253
DString projectLogoFile()
Definition util.cpp:3180
bool copyFile(const DString &src, const DString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:5895
int getPrefixIndex(const DString &name)
Definition util.cpp:3269
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5935
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:5147
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6899
DString getOverloadDocs()
Definition util.cpp:4131
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:6951
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2127
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:4681
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:4257
void cleanupInlineGraph()
Definition util.cpp:7027
DString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5929
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:6334
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:230
EntryType guessSection(const DString &name)
Definition util.cpp:338
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2315