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// own header
17#include "doxygen.h"
18
19// standard includes
20#include <algorithm>
21#include <cerrno>
22#include <chrono>
23#include <cinttypes>
24#include <clocale>
25#include <cstdio>
26#include <cstdlib>
27#include <locale>
28#include <memory>
29#include <sys/stat.h>
30#include <unordered_map>
31
32// other includes
33#include "aliases.h"
34#include "arguments.h"
35#include "cite.h"
36#include "clangparser.h"
37#include "classlist.h"
38#include "cmdmapper.h"
39#include "code.h"
40#include "commentcnv.h"
41#include "conceptdef.h"
42#include "config.h"
43#include "debug.h"
44#include "declinfo.h"
45#include "defargs.h"
46#include "defgen.h"
47#include "dir.h"
48#include "dirdef.h"
49#include "docbookgen.h"
50#include "docparser.h"
51#include "docsets.h"
52#include "dot.h"
53#include "eclipsehelp.h"
54#include "emoji.h"
55#include "entry.h"
56#include "fileinfo.h"
57#include "filename.h"
58#include "fileparser.h"
59#include "formula.h"
60#include "fortrancode.h"
61#include "fortranscanner.h"
62#include "ftvhelp.h"
63#include "groupdef.h"
64#include "htags.h"
65#include "htmlgen.h"
66#include "htmlhelp.h"
67#include "index.h"
68#include "indexlist.h"
69#include "language.h"
70#include "latexgen.h"
71#include "layout.h"
72#include "lexcode.h"
73#include "lexscanner.h"
74#include "mangen.h"
75#include "markdown.h"
76#include "membergroup.h"
77#include "memberlist.h"
78#include "membername.h"
79#include "mermaid.h"
80#include "message.h"
81#include "moduledef.h"
82#include "msc.h"
83#include "namespacedef.h"
84#include "outputlist.h"
85#include "pagedef.h"
86#include "parserintf.h"
87#include "perlmodgen.h"
88#include "plantuml.h"
89#include "portable.h"
90#include "pre.h"
91#include "pycode.h"
92#include "pyscanner.h"
93#include "qhp.h"
94#include "reflist.h"
95#include "regex.h"
96#include "requirement.h"
97#include "rtfgen.h"
98#include "scanner.h"
99#include "searchindex_js.h"
100#include "settings.h"
101#include "singlecomment.h"
102#include "sitemap.h"
103#include "sqlcode.h"
104#include "sqlite3gen.h"
105#include "stlsupport.h"
106#include "stringutil.h"
107#include "symbolresolver.h"
108#include "tagreader.h"
109#include "threadpool.h"
110#include "trace.h"
111#include "util.h"
112#include "version.h"
113#include "vhdlcode.h"
114#include "vhdldocgen.h"
115#include "vhdljjparser.h"
116#include "xmlcode.h"
117#include "xmlgen.h"
118
119#include <sqlite3.h>
120
121#if USE_LIBCLANG
122#if defined(__GNUC__)
123#pragma GCC diagnostic push
124#pragma GCC diagnostic ignored "-Wshadow"
125#endif
126#include <clang/Basic/Version.h>
127#if defined(__GNUC__)
128#pragma GCC diagnostic pop
129#endif
130#endif
131
132// provided by the generated file resources.cpp
133extern void initResources();
134
135#if !defined(_WIN32) || defined(__CYGWIN__)
136#include <signal.h>
137#define HAS_SIGNALS
138#endif
139
140// globally accessible variables
152FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
158FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
159FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
161StringMap Doxygen::tagDestinationMap; // all tag locations
162StringUnorderedSet Doxygen::tagFileSet; // all tag file names
163StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
164MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
165std::unique_ptr<PageDef> Doxygen::mainPage;
166std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
168bool Doxygen::parseSourcesNeeded = false;
186std::mutex Doxygen::addExampleMutex;
188
189// locally accessible globals
190static std::multimap< std::string, const Entry* > g_classEntries;
192static OutputList *g_outputList = nullptr; // list of output generating objects
193static StringSet g_usingDeclarations; // used classes
194static bool g_successfulRun = false;
195static bool g_dumpSymbolMap = false;
197static bool g_singleComment=false;
198
199
200
201// keywords recognized as compounds
203{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
204
232
234{
235 public:
237 void begin(const char *name)
238 {
239 msg("{}", name);
240 stats.emplace_back(name,0);
241 startTime = std::chrono::steady_clock::now();
242 }
243 void end()
244 {
245 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
246 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
247 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
248 warn_flush();
249 }
250 void print()
251 {
252 bool restore=false;
254 {
256 restore=true;
257 }
258 msg("----------------------\n");
259 for (const auto &s : stats)
260 {
261 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
262 }
263 if (restore) Debug::setFlag(Debug::Time);
264 }
265 private:
266 struct stat
267 {
268 const char *name;
269 double elapsed;
270 //stat() : name(nullptr),elapsed(0) {}
271 stat(const char *n, double el) : name(n),elapsed(el) {}
272 };
273 std::vector<stat> stats;
274 std::chrono::steady_clock::time_point startTime;
276
277
278static void addMemberDocs(const Entry *root,MemberDefMutable *md, const DString &funcDecl,
279 const ArgumentList *al,bool over_load,TypeSpecifier spec);
280static void findMember(const Entry *root,
281 const DString &relates,
282 const DString &type,
283 const DString &args,
284 DString funcDecl,
285 bool overloaded,
286 bool isFunc
287 );
288
295
296
297static bool findClassRelation(
298 const Entry *root,
299 Definition *context,
300 ClassDefMutable *cd,
301 const BaseInfo *bi,
302 const TemplateNameMap &templateNames,
303 /*bool insertUndocumented*/
305 bool isArtificial
306 );
307
308//----------------------------------------------------------------------
309
310template<class Def>
311static void addIncludeFile(Def *def,FileDef *ifd,const Entry *root)
312{
313 AUTO_TRACE();
314 if (
315 (!root->doc.stripWhiteSpace().empty() ||
316 !root->brief.stripWhiteSpace().empty() ||
317 Config_getBool(EXTRACT_ALL)
318 ) && root->protection!=Protection::Private
319 )
320 {
321 AUTO_TRACE_ADD("includefile={}",root->includeFile);
322 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
323
324 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
325 DString includeFile = root->includeFile;
326 if (!includeFile.empty() && includeFile.at(0)=='"')
327 {
328 local = true;
329 includeFile=includeFile.mid(1,includeFile.length()-2);
330 }
331 else if (!includeFile.empty() && includeFile.at(0)=='<')
332 {
333 local = false;
334 includeFile=includeFile.mid(1,includeFile.length()-2);
335 }
336
337 bool ambig = false;
338 FileDef *fd=nullptr;
339 // see if we need to include a verbatim copy of the header file
340 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
341 if (!includeFile.empty() &&
342 (fd=Doxygen::inputNameLinkedMap->findFileDef(includeFile,ambig))==nullptr
343 )
344 { // explicit request
345 DString text;
346 text.sprintf("the name '%s' supplied as "
347 "the argument of the \\class, \\struct, \\union, or \\headerfile command ",
348 qPrint(includeFile)
349 );
350 if (ambig) // name is ambiguous
351 {
352 text+="matches the following input files:\n";
354 text+="\n";
355 text+="Please use a more specific name by "
356 "including a (larger) part of the path!";
357 }
358 else // name is not an input file
359 {
360 text+="is not an input file";
361 }
362 warn(root->fileName,root->startLine, "{}", text);
363 }
364 else if (includeFile.empty() && ifd &&
365 // see if the file extension makes sense
366 EntryType::guessSection(ifd->name()).isHeader())
367 { // implicit assumption
368 fd=ifd;
369 }
370
371 // if a file is found, we mark it as a source file.
372 if (fd)
373 {
374 DString iName = !root->includeName.empty() ?
375 root->includeName : includeFile;
376 if (!iName.empty()) // user specified include file
377 {
378 if (iName.at(0)=='<') local=false; // explicit override
379 else if (iName.at(0)=='"') local=true;
380 if (iName.at(0)=='"' || iName.at(0)=='<')
381 {
382 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
383 }
384 if (iName.empty())
385 {
386 iName=fd->name();
387 }
388 }
389 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
390 {
392 }
393 else // use name of the file containing the class definition
394 {
395 iName=fd->name();
396 }
397 AUTO_TRACE_ADD("iName={}",iName);
398 if (fd->generateSourceFile()) // generate code for header
399 {
400 def->setIncludeFile(fd,iName,local,!root->includeName.empty());
401 }
402 else // put #include in the class documentation without link
403 {
404 def->setIncludeFile(nullptr,iName,local,true);
405 }
406 }
407 }
408}
409
410
411//----------------------------------------------------------------------------
412
414 FileDef *fileScope,const TagInfo *tagInfo);
415static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
416
417static void addPageToContext(PageDef *pd,Entry *root)
418{
419 if (root->parent()) // add the page to it's scope
420 {
421 DString scope = root->parent()->name;
422 if (root->parent()->section.isPackageDoc())
423 {
424 scope=substitute(scope,".","::");
425 }
426 scope = stripAnonymousNamespaceScope(scope);
427 scope+="::"+pd->name();
429 if (d)
430 {
431 pd->setPageScope(d);
432 }
433 }
434}
435
436static void addRelatedPage(Entry *root)
437{
438 GroupDef *gd=nullptr;
439 for (const Grouping &g : root->groups)
440 {
441 if (!g.groupname.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
442 }
443 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
444 DString doc=root->doc+root->inbodyDocs;
445
446 PageDef *pd = addRelatedPage(root->name, // name
447 root->args, // ptitle
448 doc, // doc
449 root->docFile, // fileName
450 root->docLine, // docLine
451 root->startLine, // startLine
452 root->sli, // sli
453 gd, // gd
454 root->tagInfo(), // tagInfo
455 false, // xref
456 root->lang // lang
457 );
458 if (pd)
459 {
460 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
462 pd->setLocalToc(root->localToc);
463 addPageToContext(pd,root);
464 }
465}
466
467static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
468{
469 if (root->section.isGroupDoc() && !root->name.empty() &&
470 ((!includeExternal && root->tagInfo()==nullptr) ||
471 ( includeExternal && root->tagInfo()!=nullptr))
472 )
473 {
474 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
475 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
476 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
477 {
479 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
480
481 if (gd)
482 {
483 if ( !gd->hasGroupTitle() )
484 {
485 gd->setGroupTitle( root->type );
486 }
487 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
488 {
489 warn( root->fileName,root->startLine,
490 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
491 root->name, root->type, gd->groupTitle() );
492 }
493 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
494 gd->setDocumentation( root->doc, root->docFile, root->docLine );
495 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
497 gd->setRefItems(root->sli);
499 gd->setLanguage(root->lang);
501 {
502 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
503 }
504 addIncludeFile(gd,nullptr,root);
505 }
506 else
507 {
508 if (root->tagInfo())
509 {
510 gd = Doxygen::groupLinkedMap->add(root->name,
511 std::unique_ptr<GroupDef>(
512 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
513 gd->setReference(root->tagInfo()->tagName);
514 }
515 else
516 {
517 gd = Doxygen::groupLinkedMap->add(root->name,
518 std::unique_ptr<GroupDef>(
519 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
520 }
521 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
522 // allow empty docs for group
523 gd->setDocumentation(!root->doc.empty() ? root->doc : DString(" "),root->docFile,root->docLine,false);
524 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
526 gd->setRefItems(root->sli);
528 gd->setLanguage(root->lang);
529 addIncludeFile(gd,nullptr,root);
531 {
532 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
533 }
534 }
535 }
536 }
537 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
538}
539
540static void buildGroupList(const Entry *root)
541{
542 // --- first process only local groups
543 // first process the @defgroups blocks
544 buildGroupListFiltered(root,false,false);
545 // then process the @addtogroup, @weakgroup blocks
546 buildGroupListFiltered(root,true,false);
547
548 // --- then also process external groups
549 // first process the @defgroups blocks
550 buildGroupListFiltered(root,false,true);
551 // then process the @addtogroup, @weakgroup blocks
552 buildGroupListFiltered(root,true,true);
553}
554
555static void findGroupScope(const Entry *root)
556{
557 if (root->section.isGroupDoc() && !root->name.empty() &&
558 root->parent() && !root->parent()->name.empty())
559 {
561 if (gd)
562 {
563 DString scope = root->parent()->name;
564 if (root->parent()->section.isPackageDoc())
565 {
566 scope=substitute(scope,".","::");
567 }
568 scope = stripAnonymousNamespaceScope(scope);
569 scope+="::"+gd->name();
571 if (d)
572 {
573 gd->setGroupScope(d);
574 }
575 }
576 }
577 for (const auto &e : root->children()) findGroupScope(e.get());
578}
579
580static void organizeSubGroupsFiltered(const Entry *root,bool additional)
581{
582 if (root->section.isGroupDoc() && !root->name.empty())
583 {
584 AUTO_TRACE("additional={}",additional);
585 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
586 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
587 {
589 if (gd)
590 {
591 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
592 addGroupToGroups(root,gd);
593 }
594 }
595 }
596 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
597}
598
599static void organizeSubGroups(const Entry *root)
600{
601 //printf("Defining groups\n");
602 // first process the @defgroups blocks
603 organizeSubGroupsFiltered(root,false);
604 //printf("Additional groups\n");
605 // then process the @addtogroup, @weakgroup blocks
606 organizeSubGroupsFiltered(root,true);
607}
608
609//----------------------------------------------------------------------
610
611static void buildFileList(const Entry *root)
612{
613 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
614 !root->name.empty() && !root->tagInfo() // skip any file coming from tag files
615 )
616 {
617 bool ambig = false;
619 if (!fd || ambig)
620 {
621 bool save_ambig = ambig;
622 // use the directory of the file to see if the described file is in the same
623 // directory as the describing file.
624 DString fn = root->fileName;
625 size_t newIndex=fn.rfind('/');
626 if (newIndex==DString::npos)
627 {
628 fn = root->name;
629 }
630 else
631 {
632 fn = fn.left(newIndex)+"/"+root->name;
633 }
635 if (!fd) ambig = save_ambig;
636 }
637 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
638 if (fd && !ambig)
639 {
640 //printf("Adding documentation!\n");
641 // using false in setDocumentation is small hack to make sure a file
642 // is documented even if a \file command is used without further
643 // documentation
644 fd->setDocumentation(root->doc,root->docFile,root->docLine,false);
645 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
647 fd->setRefItems(root->sli);
649 addIncludeFile(fd,fd,root);
650 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
651 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
652 for (const Grouping &g : root->groups)
653 {
654 GroupDef *gd=nullptr;
656 {
657 if (!gd->containsFile(fd))
658 {
659 gd->addFile(fd);
660 fd->makePartOfGroup(gd);
661 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
662 }
663 }
664 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
665 {
666 warn(root->fileName, root->startLine,
667 "Found non-existing group '{}' for the command '{}', ignoring command",
669 );
670 }
671 }
672 }
673 else
674 {
675 DString text(4096, DString::ExplicitSize);
676 text.sprintf("the name '%s' supplied as "
677 "the argument in the \\file statement ",
678 qPrint(root->name));
679 if (ambig) // name is ambiguous
680 {
681 text+="matches the following input files:\n";
683 text+="\n";
684 text+="Please use a more specific name by "
685 "including a (larger) part of the path!";
686 }
687 else // name is not an input file
688 {
689 text+="is not an input file";
690 }
691 warn(root->fileName,root->startLine,"{}", text);
692 }
693 }
694 for (const auto &e : root->children()) buildFileList(e.get());
695}
696
698{
699 size_t l = s.length();
700 int count=0;
701 int round=0;
702 DString result;
703 for (size_t i=0;i<l;i++)
704 {
705 char c=s.at(i);
706 if (c=='(') round++;
707 else if (c==')' && round>0) round--;
708 else if (c=='<' && round==0) count++;
709 if (count==0)
710 {
711 result+=c;
712 }
713 if (c=='>' && round==0 && count>0) count--;
714 }
715 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
716 return result;
717}
718
719/*! returns the Definition object belonging to the first \a level levels of
720 * full qualified name \a name. Creates an artificial scope if the scope is
721 * not found and set the parent/child scope relation if the scope is found.
722 */
723[[maybe_unused]]
724static Definition *buildScopeFromQualifiedName(const DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
725{
726 DString name = stripTemplateSpecifiers(name_);
727 name.stripPrefix("::");
728 int level = name.contains("::");
729 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
730 int i=0, p=0, l=0;
732 DString fullScope;
733 while (i<level)
734 {
735 int idx=getScopeFragment(name,p,&l);
736 if (idx==-1) return prevScope;
737 DString nsName = name.mid(idx,l);
738 if (nsName.empty()) return prevScope;
739 if (!fullScope.empty()) fullScope+="::";
740 fullScope+=nsName;
742 DefinitionMutable *innerScope = toDefinitionMutable(nd);
743 ClassDef *cd=nullptr;
744 if (nd==nullptr) cd = getClass(fullScope);
745 if (nd==nullptr && cd) // scope is a class
746 {
747 innerScope = toDefinitionMutable(cd);
748 }
749 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==DString::npos) // scope is not known and could be a namespace!
750 {
751 // introduce bogus namespace
752 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
753 NamespaceDefMutable *newNd=
755 Doxygen::namespaceLinkedMap->add(fullScope,
757 "[generated]",1,1,fullScope,
758 tagInfo?tagInfo->tagName:DString(),
759 tagInfo?tagInfo->fileName:DString())));
760 if (newNd)
761 {
762 newNd->setLanguage(lang);
763 newNd->setArtificial(true);
764 // add namespace to the list
765 innerScope = newNd;
766 }
767 }
768 else // scope is a namespace
769 {
770 }
771 if (innerScope)
772 {
773 // make the parent/child scope relation
774 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
775 if (prevScopeMutable)
776 {
777 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
778 }
779 innerScope->setOuterScope(prevScope);
780 }
781 else // current scope is a class, so return only the namespace part...
782 {
783 return prevScope;
784 }
785 // proceed to the next scope fragment
786 p=idx+l+2;
787 prevScope=toDefinition(innerScope);
788 i++;
789 }
790 return prevScope;
791}
792
794 FileDef *fileScope,const TagInfo *tagInfo)
795{
796 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
797 Definition *resultScope=toDefinition(startScope);
798 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
800 int l1 = 0;
801 int i1 = getScopeFragment(scope,0,&l1);
802 if (i1==-1)
803 {
804 //printf(">no fragments!\n");
805 return resultScope;
806 }
807 int p=i1+l1,l2=0,i2=0;
808 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
809 {
810 DString nestedNameSpecifier = scope.mid(i1,l1);
811 Definition *orgScope = resultScope;
812 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
813 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
814 //printf(" resultScope=%p\n",resultScope);
815 if (resultScope==nullptr)
816 {
817 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
818 // also search for used namespaces
819 {
820 for (const auto &nd : fileScope->getUsedNamespaces())
821 {
823 if (mnd)
824 {
825 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
826 if (resultScope!=nullptr) break;
827 }
828 }
829 if (resultScope)
830 {
831 // for a nested class A::I in used namespace N, we get
832 // N::A::I while looking for A, so we should compare
833 // resultScope->name() against scope.left(i2+l2)
834 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
835 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
836 {
837 break;
838 }
839 goto nextFragment;
840 }
841 }
842
843 // also search for used classes. Complication: we haven't been able
844 // to put them in the right scope yet, because we are still resolving
845 // the scope relations!
846 // Therefore loop through all used classes and see if there is a right
847 // scope match between the used class and nestedNameSpecifier.
848 for (const auto &usedName : g_usingDeclarations)
849 {
850 //printf("Checking using class %s\n",qPrint(usedName));
851 if (rightScopeMatch(usedName,nestedNameSpecifier))
852 {
853 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
854 // so use this instead.
855 DString fqn = usedName + scope.mid(p);
856 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
857 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
858 if (resultScope)
859 {
860 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
861 return resultScope;
862 }
863 }
864 }
865
866 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
867 return nullptr;
868 }
869 nextFragment:
870 i1=i2;
871 l1=l2;
872 p=i2+l2;
873 }
874 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
875 return resultScope;
876}
877
878std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
879 const DString &name,
880 const ArgumentLists &tArgLists)
881{
882 // for each scope fragment, check if it is a template and advance through
883 // the list if so.
884 size_t i=0, p=0;
885 auto alIt = tArgLists.begin();
886 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
887 {
889 if (nd==nullptr)
890 {
891 ClassDef *cd = getClass(name.left(i));
892 if (cd)
893 {
894 if (!cd->templateArguments().empty())
895 {
896 ++alIt;
897 }
898 }
899 }
900 p=i+2;
901 }
902 return alIt!=tArgLists.end() ?
903 std::make_unique<ArgumentList>(*alIt) :
904 std::unique_ptr<ArgumentList>();
905}
906
907static
909{
911
912 if (specifier.isStruct())
914 else if (specifier.isUnion())
915 sec=ClassDef::Union;
916 else if (specifier.isCategory())
918 else if (specifier.isInterface())
920 else if (specifier.isProtocol())
922 else if (specifier.isException())
924 else if (specifier.isService())
926 else if (specifier.isSingleton())
928
929 if (section.isUnionDoc())
930 sec=ClassDef::Union;
931 else if (section.isStructDoc())
933 else if (section.isInterfaceDoc())
935 else if (section.isProtocolDoc())
937 else if (section.isCategoryDoc())
939 else if (section.isExceptionDoc())
941 else if (section.isServiceDoc())
943 else if (section.isSingletonDoc())
945
946 return sec;
947}
948
949
950static void addClassToContext(const Entry *root)
951{
952 AUTO_TRACE("name={}",root->name);
953 FileDef *fd = root->fileDef();
954
955 DString scName;
956 if (root->parent()->section.isScope())
957 {
958 scName=root->parent()->name;
959 }
960 // name without parent's scope
961 DString fullName = root->name;
962
963 // strip off any template parameters (but not those for specializations)
964 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
965 {
966 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
967 }
968 fullName=stripTemplateSpecifiersFromScope(fullName);
969
970 // name with scope (if not present already)
971 DString qualifiedName = fullName;
972 if (!scName.empty() && !leftScopeMatch(scName,fullName))
973 {
974 qualifiedName.prepend(scName+"::");
975 }
976
977 // see if we already found the class before
978 ClassDefMutable *cd = getClassMutable(qualifiedName);
979
980 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
981
982 if (cd)
983 {
984 fullName=cd->name();
985 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
986 //if (cd->templateArguments()==0)
987 //{
988 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
989 // cd->setTemplateArguments(tArgList);
990 //}
991
992 cd->setDocumentation(root->doc,root->docFile,root->docLine);
993 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
994 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
995 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
996
997 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
998 {
999 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
1000 if (root->bodyLine!=-1)
1001 {
1002 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1003 cd->setBodyDef(fd);
1004 }
1005 }
1006
1007 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
1008 {
1009 // this happens if a template class declared with @class is found
1010 // before the actual definition or if a forward declaration has different template
1011 // parameter names.
1012 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1013 if (tArgList)
1014 {
1015 cd->setTemplateArguments(*tArgList);
1016 }
1017 }
1018 if (cd->requiresClause().empty() && !root->req.empty())
1019 {
1020 cd->setRequiresClause(root->req);
1021 }
1022
1024
1025 cd->setMetaData(root->metaData);
1026 }
1027 else // new class
1028 {
1030
1031 DString className;
1032 DString namespaceName;
1033 extractNamespaceName(fullName,className,namespaceName);
1034
1035 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1036 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1037
1038 DString tagName;
1039 DString refFileName;
1040 const TagInfo *tagInfo = root->tagInfo();
1041 if (tagInfo)
1042 {
1043 tagName = tagInfo->tagName;
1044 refFileName = tagInfo->fileName;
1045 if (fullName.find("::")!=DString::npos)
1046 // symbols imported via tag files may come without the parent scope,
1047 // so we artificially create it here
1048 {
1049 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1050 }
1051 }
1052 std::unique_ptr<ArgumentList> tArgList;
1053 size_t i=0;
1054 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1055 (i=fullName.find('<'))!=DString::npos)
1056 {
1057 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1058 // name and template arguments here
1059 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1060 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
1061 // A -> A
1062 // A<T> -> A-1-g
1063 // A<T,S> -> A-2-g
1064 {
1065 fullName=mangleCSharpGenericName(fullName);
1066 }
1067 else
1068 {
1069 fullName=fullName.left(i);
1070 }
1071 }
1072 else
1073 {
1074 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1075 }
1076 // add class to the list
1077 cd = toClassDefMutable(
1078 Doxygen::classLinkedMap->add(fullName,
1079 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1080 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1081 if (cd)
1082 {
1083 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1084 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1085 fmt::ptr(tagInfo),root->hidden,root->artificial);
1086 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1087 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1088 cd->setLanguage(root->lang);
1089 cd->setId(root->id);
1090 cd->setHidden(root->hidden);
1091 cd->setArtificial(root->artificial);
1092 cd->setClassSpecifier(root->spec);
1093 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1094 {
1096 }
1097 cd->addQualifiers(root->qualifiers);
1098 cd->setTypeConstraints(root->typeConstr);
1099 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1100 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1101
1102 if (tArgList)
1103 {
1104 cd->setTemplateArguments(*tArgList);
1105 }
1106 cd->setRequiresClause(root->req);
1107 cd->setProtection(root->protection);
1108 cd->setIsStatic(root->isStatic);
1109
1110 // file definition containing the class cd
1111 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1112 cd->setBodyDef(fd);
1113
1114 cd->setMetaData(root->metaData);
1115
1116 cd->insertUsedFile(fd);
1117 }
1118 else
1119 {
1120 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1121 }
1122 }
1123
1124 if (cd)
1125 {
1127 if (!root->subGrouping) cd->setSubGrouping(false);
1128 if (!root->spec.isForwardDecl())
1129 {
1130 if (cd->hasDocumentation())
1131 {
1132 addIncludeFile(cd,fd,root);
1133 }
1134 if (fd && root->section.isCompound())
1135 {
1136 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1137 cd->setFileDef(fd);
1138 fd->insertClass(cd);
1139 }
1140 }
1141 addClassToGroups(root,cd);
1143 cd->setRefItems(root->sli);
1144 cd->setRequirementReferences(root->rqli);
1145 }
1146}
1147
1148//----------------------------------------------------------------------
1149// build a list of all classes mentioned in the documentation
1150// and all classes that have a documentation block before their definition.
1151static void buildClassList(const Entry *root)
1152{
1153 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1154 {
1155 AUTO_TRACE();
1156 addClassToContext(root);
1157 }
1158 for (const auto &e : root->children()) buildClassList(e.get());
1159}
1160
1161static void buildClassDocList(const Entry *root)
1162{
1163 if ((root->section.isCompoundDoc()) && !root->name.empty())
1164 {
1165 AUTO_TRACE();
1166 addClassToContext(root);
1167 }
1168 for (const auto &e : root->children()) buildClassDocList(e.get());
1169}
1170
1171//----------------------------------------------------------------------
1172// build a list of all classes mentioned in the documentation
1173// and all classes that have a documentation block before their definition.
1174
1175static void addConceptToContext(const Entry *root)
1176{
1177 AUTO_TRACE();
1178 FileDef *fd = root->fileDef();
1179
1180 DString scName;
1181 if (root->parent()->section.isScope())
1182 {
1183 scName=root->parent()->name;
1184 }
1185
1186 // name with scope (if not present already)
1187 DString qualifiedName = root->name;
1188 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1189 {
1190 qualifiedName.prepend(scName+"::");
1191 }
1192
1193 // see if we already found the concept before
1194 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1195
1196 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1197
1198 if (cd)
1199 {
1200 qualifiedName=cd->name();
1201 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1202
1203 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1204 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1205
1206 addIncludeFile(cd,fd,root);
1207 }
1208 else // new concept
1209 {
1210 DString className;
1211 DString namespaceName;
1212 extractNamespaceName(qualifiedName,className,namespaceName);
1213
1214 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1215 qualifiedName,namespaceName,className,root->brief,root->doc);
1216
1217 DString tagName;
1218 DString refFileName;
1219 const TagInfo *tagInfo = root->tagInfo();
1220 if (tagInfo)
1221 {
1222 tagName = tagInfo->tagName;
1223 refFileName = tagInfo->fileName;
1224 if (qualifiedName.find("::")!=DString::npos)
1225 // symbols imported via tag files may come without the parent scope,
1226 // so we artificially create it here
1227 {
1228 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1229 }
1230 }
1231 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1232 // add concept to the list
1234 Doxygen::conceptLinkedMap->add(qualifiedName,
1235 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1236 qualifiedName,tagName,refFileName)));
1237 if (cd)
1238 {
1239 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1240 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1241 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1242 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1243 cd->setLanguage(root->lang);
1244 cd->setId(root->id);
1245 cd->setHidden(root->hidden);
1246 cd->setGroupId(root->mGrpId);
1247 if (tArgList)
1248 {
1249 cd->setTemplateArguments(*tArgList);
1250 }
1251 cd->setInitializer(root->initializer.str());
1252 // file definition containing the class cd
1253 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1254 cd->setBodyDef(fd);
1256 cd->setRefItems(root->sli);
1257 cd->setRequirementReferences(root->rqli);
1258 addIncludeFile(cd,fd,root);
1259
1260 // also add namespace to the correct structural context
1261 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1263 {
1265 if (dm)
1266 {
1267 dm->addInnerCompound(cd);
1268 }
1269 cd->setOuterScope(d);
1270 }
1271 for (const auto &ce : root->children())
1272 {
1273 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1274 if (ce->section.isConceptDocPart())
1275 {
1276 cd->addSectionsToDefinition(ce->anchors);
1277 cd->setRefItems(ce->sli);
1278 cd->setRequirementReferences(ce->rqli);
1279 if (!ce->brief.empty())
1280 {
1281 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1282 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1283 }
1284 if (!ce->doc.empty())
1285 {
1286 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1287 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1288 }
1289 }
1290 else if (ce->section.isConceptCodePart())
1291 {
1292 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1293 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1294 }
1295 }
1296 }
1297 else
1298 {
1299 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1300 }
1301 }
1302
1303 if (cd)
1304 {
1306 for (const auto &ce : root->children())
1307 {
1308 if (ce->section.isConceptDocPart())
1309 {
1310 cd->addSectionsToDefinition(ce->anchors);
1311 }
1312 }
1313 if (fd)
1314 {
1315 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1316 cd->setFileDef(fd);
1317 fd->insertConcept(cd);
1318 }
1319 addConceptToGroups(root,cd);
1321 cd->setRefItems(root->sli);
1322 cd->setRequirementReferences(root->rqli);
1323 }
1324}
1325
1326static void findModuleDocumentation(const Entry *root)
1327{
1328 if (root->section.isModuleDoc())
1329 {
1330 AUTO_TRACE();
1332 }
1333 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1334}
1335
1336static void buildConceptList(const Entry *root)
1337{
1338 if (root->section.isConcept())
1339 {
1340 AUTO_TRACE();
1341 addConceptToContext(root);
1342 }
1343 for (const auto &e : root->children()) buildConceptList(e.get());
1344}
1345
1346static void buildConceptDocList(const Entry *root)
1347{
1348 if (root->section.isConceptDoc())
1349 {
1350 AUTO_TRACE();
1351 addConceptToContext(root);
1352 }
1353 for (const auto &e : root->children()) buildConceptDocList(e.get());
1354}
1355
1356// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1357// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1359{
1360 AUTO_TRACE();
1361 for (const auto &cd : *Doxygen::conceptLinkedMap)
1362 {
1363 if (cd->groupId()!=DOX_NOGROUP)
1364 {
1365 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1366 {
1367 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1368 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1369 {
1370 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1371 if (ocdm)
1372 {
1373 for (const auto &gd : cd->partOfGroups())
1374 {
1375 if (gd)
1376 {
1377 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1378 ocdm->makePartOfGroup(gd);
1379 gd->addConcept(ocd.get());
1380 }
1381 }
1382 }
1383 }
1384 }
1385 }
1386 }
1387}
1388
1389//----------------------------------------------------------------------
1390
1392{
1393 ClassDefSet visitedClasses;
1394
1395 bool done=false;
1396 //int iteration=0;
1397 while (!done)
1398 {
1399 done=true;
1400 //++iteration;
1401 struct ClassAlias
1402 {
1403 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1404 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1405 DString aliasFullName;
1406 std::unique_ptr<ClassDef> aliasCd;
1407 DefinitionMutable *aliasContext;
1408 };
1409 std::vector<ClassAlias> aliases;
1410 for (const auto &icd : *Doxygen::classLinkedMap)
1411 {
1412 ClassDefMutable *cd = toClassDefMutable(icd.get());
1413 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1414 {
1415 DString name = stripAnonymousNamespaceScope(icd->name());
1416 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1417 // also add class to the correct structural context
1419 name,icd->getFileDef(),nullptr);
1420 if (d)
1421 {
1422 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1424 if (dm)
1425 {
1426 dm->addInnerCompound(cd);
1427 }
1428 cd->setOuterScope(d);
1429
1430 // for inline namespace add an alias of the class to the outer scope
1432 {
1434 //printf("nd->isInline()=%d\n",nd->isInline());
1435 if (nd && nd->isInline())
1436 {
1437 d = d->getOuterScope();
1438 if (d)
1439 {
1440 dm = toDefinitionMutable(d);
1441 if (dm)
1442 {
1443 auto aliasCd = createClassDefAlias(d,cd);
1444 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1445 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1446 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1447 }
1448 }
1449 }
1450 else
1451 {
1452 break;
1453 }
1454 }
1455
1456 visitedClasses.insert(icd.get());
1457 done=false;
1458 }
1459 //else
1460 //{
1461 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1462 //}
1463 }
1464 }
1465 // add aliases
1466 for (auto &alias : aliases)
1467 {
1468 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1469 if (aliasCd)
1470 {
1471 alias.aliasContext->addInnerCompound(aliasCd);
1472 }
1473 }
1474 }
1475
1476 //give warnings for unresolved compounds
1477 for (const auto &icd : *Doxygen::classLinkedMap)
1478 {
1479 ClassDefMutable *cd = toClassDefMutable(icd.get());
1480 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1481 {
1483 /// create the scope artificially
1484 // anyway, so we can at least relate scopes properly.
1485 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1486 if (d && d!=cd && !cd->getDefFileName().empty())
1487 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1488 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1489 // also avoid warning for stuff imported via a tagfile.
1490 {
1492 if (dm)
1493 {
1494 dm->addInnerCompound(cd);
1495 }
1496 cd->setOuterScope(d);
1497 // A specialization can only be written for a template that has been declared before, so a missing
1498 // scope means that declaration is outside the input - e.g. `template<> struct std::hash<MyClass>`.
1499 if (cd->localName().find('<')==DString::npos)
1500 {
1501 warn(cd->getDefFileName(),cd->getDefLine(),
1502 "Incomplete input: scope for class {} not found!{}",name,
1503 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1504 );
1505 }
1506 }
1507 }
1508 }
1509}
1510
1512{
1513 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1514 //if (!inlineGroupedClasses) return;
1515 //printf("** distributeClassGroupRelations()\n");
1516
1517 ClassDefSet visitedClasses;
1518 for (const auto &cd : *Doxygen::classLinkedMap)
1519 {
1520 //printf("Checking %s\n",qPrint(cd->name()));
1521 // distribute the group to nested classes as well
1522 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1523 {
1524 //printf(" Candidate for merging\n");
1525 GroupDef *gd = cd->partOfGroups().front();
1526 for (auto &ncd : cd->getClasses())
1527 {
1529 if (ncdm && ncdm->partOfGroups().empty())
1530 {
1531 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1532 // gd->groupTitle());
1533 ncdm->makePartOfGroup(gd);
1534 gd->addClass(ncdm);
1535 }
1536 }
1537 visitedClasses.insert(cd.get()); // only visit every class once
1538 }
1539 }
1540}
1541
1542//----------------------------------------------------------------------
1543
1544template<typename Container>
1546 const Container *cd,
1547 const MemberDef *enumTypeMember,
1548 MemberListType mlFilter)
1549{
1550 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1551 {
1552 MemberList *eiml = cd->getMemberList(mlFilter);
1553 if (eiml)
1554 {
1555 for (const auto &eimd : *eiml)
1556 {
1557 DString vtype = eimd->typeString();
1558 if (vtype.find(md->name())!=DString::npos)
1559 {
1561 if (mimd)
1562 {
1563 mimd->setAnonymousEnumType(enumTypeMember);
1564 break;
1565 }
1566 }
1567 }
1568 }
1569 }
1570}
1571
1572static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName)
1573{
1574 DString n = templ->name();
1575 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1576 if (size_t sn = n.find('@'); sn!=DString::npos)
1577 {
1578 const char *p = n.data()+sn;
1579 char c;
1580 while ((c=*p))
1581 {
1582 if (!isdigit(c) && c!='@' && c!=':') break;
1583 p++;
1584 }
1585 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1586 }
1587 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1588 DString fullName = n+"."+fieldName;
1589
1590 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1592 Doxygen::classLinkedMap->add(fullName,
1594 templ->getDefLine(),
1595 templ->getDefColumn(),
1596 fullName,
1597 templ->compoundType())));
1598 if (cd)
1599 {
1600 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1601 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1602 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1603 cd->setLanguage(templ->getLanguage());
1604 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1605 cd->setBodyDef(templ->getBodyDef());
1606
1607 if (root!=Doxygen::globalScope)
1608 {
1609 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1610 if (root && root->definitionType()==Definition::TypeFile)
1611 {
1612 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1613 fd->insertClass(cd);
1614 cd->setFileDef(fd);
1616 }
1617 else if (outerScope)
1618 {
1619 outerScope->addInnerCompound(cd);
1620 cd->setOuterScope(const_cast<Definition*>(root));
1621 }
1622 }
1623
1624 for (auto &gd : root->partOfGroups())
1625 {
1626 cd->makePartOfGroup(gd);
1627 gd->addClass(cd);
1628 }
1629
1630 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1631 {
1632 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1633 md->typeString(),md->name(),md->argsString(),md->excpString(),
1634 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1635 md->memberType(),
1636 ArgumentList(),ArgumentList(),"");
1637 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1638 imd->setMemberClass(cd);
1639 imd->setDefinition(md->definition());
1640 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1641 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1642 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1643 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1644 imd->setId(md->id());
1645 imd->addQualifiers(md->getQualifiers());
1646 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1647 imd->setMemberGroupId(md->getMemberGroupId());
1648 imd->setInitializer(md->initializer());
1649 imd->setRequiresClause(md->requiresClause());
1650 imd->setMaxInitLines(md->initializerLines());
1651 imd->setBitfields(md->bitfieldString());
1652 imd->setLanguage(md->getLanguage());
1654 cd->insertMember(imd);
1655 associateVariableWithAnonymousEnumType(md,cd,imd,MemberListType::PubAttribs());
1656 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1657 mn->push_back(std::move(newMd));
1658 return imd;
1659
1660 };
1661
1662 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1663 if (ml)
1664 {
1665 for (const auto &md : *ml)
1666 {
1667 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1668 addMember(md);
1669 }
1670 }
1671 ml = templ->getMemberList(MemberListType::PubTypes());
1672 if (ml)
1673 {
1674 for (const auto &md : *ml)
1675 {
1676 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1677 MemberDefMutable *mdm = addMember(md);
1678 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1679 {
1680 for (const auto &emd : md->enumFieldList())
1681 {
1682 //printf(" enum field %s\n",qPrint(emd->name()));
1683 MemberDefMutable *emdm = addMember(emd);
1684 mdm->insertEnumField(emdm);
1685 emdm->setEnumScope(md);
1686 }
1687 }
1688 }
1689 }
1690 }
1691 return cd;
1692}
1693
1694/** Look through the members of class \a cd and its public members.
1695 * If there is a member m of a tag less struct/union,
1696 * then we create a duplicate of the struct/union with the name of the
1697 * member to identify it.
1698 * So if cd has name S, then the tag less struct/union will get name S.m
1699 * Since tag less structs can be nested we need to call this function
1700 * recursively. Later on we need to patch the member types so we keep
1701 * track of the hierarchy of classes we create.
1702 */
1703template<typename Container, typename TagContainer>
1704static void processTagLessClasses(const Definition *root,
1705 const Container *cd,
1706 const TagContainer *tagParent,
1707 MemberListType varFilter,
1708 MemberListType typeFilter,
1709 const DString &prefix,int count)
1710{
1711 AUTO_TRACE("count={} name={}\n",count,cd->name());
1712 if (tagParent /*&& !cd->getClasses().empty()*/)
1713 {
1714 MemberList *ml = cd->getMemberList(varFilter);
1715 if (ml)
1716 {
1717 int pos=0;
1718 for (const auto &md : *ml)
1719 {
1720 DString type = md->typeString();
1721 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1722 if ((cd->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // part namespace members only if cd is a namespace
1723 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1724 {
1725 std::vector<const ClassDef *> candidates;
1726 for (const auto &icd : cd->getClasses())
1727 {
1728 candidates.push_back(icd);
1729 }
1730 for (const auto &icd : candidates)
1731 {
1732 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1733 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1734 {
1735 DString name = md->name();
1736 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__";
1737 if (!prefix.empty()) name.prepend(prefix+".");
1738 //printf(" found %s in scope %s\n",qPrint(name),qPrint(cd->name()));
1739 ClassDefMutable *ncd = createTagLessInstance(root,icd,name);
1740 if (ncd)
1741 {
1742 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1743 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1744 ncd->setTagLessReference(icd);
1745
1746 // associate the variable of the anonymous type with the type member
1747 MemberList *pml = tagParent->getMemberList(varFilter);
1748 if (pml)
1749 {
1750 for (const auto &pmd : *pml)
1751 {
1753 if (pmdm && pmd->name()==md->name())
1754 {
1755 pmdm->setClassDefOfAnonymousType(ncd);
1756 }
1757 }
1758 }
1759 }
1760 }
1761 else
1762 {
1763 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1764 }
1765 }
1766 }
1767 }
1768 }
1769 // associate the variable of the anonymous enum type with the type member
1770 ml = cd->getMemberList(typeFilter);
1771 if (ml)
1772 {
1773 for (const auto &md : *ml)
1774 {
1775 MemberListType mlFilter = cd->definitionType()==Definition::TypeClass ? MemberListType::PubAttribs() : MemberListType::DecVarMembers();
1776 associateVariableWithAnonymousEnumType(md,cd,md,mlFilter);
1777 }
1778 }
1779 }
1780}
1781
1782template<typename Container>
1783static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1784{
1785 for (const auto &icd : cd->getClasses())
1786 {
1787 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1788 {
1789 findTagLessClasses(candidates,icd);
1790 }
1791 }
1792
1793 candidates.insert(cd);
1794}
1795
1797{
1798 std::set<const Definition *> candidates;
1799 for (auto &cd : *Doxygen::classLinkedMap)
1800 {
1801 Definition *scope = cd->getOuterScope();
1802 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1803 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1804 {
1805 const NamespaceDef *nd = toNamespaceDef(scope);
1806 if (nd && nd==Doxygen::globalScope) // class at global namespace
1807 {
1808 const FileDef *fd = cd->getFileDef();
1809 if (fd)
1810 {
1811 findTagLessClasses(candidates,fd);
1812 }
1813 }
1814 else if (nd) // class in a namespace
1815 {
1816 findTagLessClasses(candidates,nd);
1817 }
1818 }
1819 }
1820
1821 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1822 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1823 for (const auto &d : candidates)
1824 {
1825 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1826 if (d->definitionType()==Definition::TypeNamespace)
1827 {
1828 const NamespaceDef *nd = toNamespaceDef(d);
1829 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1830 }
1831 else if (d->definitionType()==Definition::TypeFile)
1832 {
1833 const FileDef *fd = toFileDef(d);
1834 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1835 }
1836 else if (d->definitionType()==Definition::TypeClass)
1837 {
1838 const ClassDef *cd = toClassDef(d);
1839 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1840 }
1841 }
1842}
1843
1844
1845//----------------------------------------------------------------------
1846// build a list of all namespaces mentioned in the documentation
1847// and all namespaces that have a documentation block before their definition.
1848static void buildNamespaceList(const Entry *root)
1849{
1850 if (
1851 (root->section.isNamespace() ||
1852 root->section.isNamespaceDoc() ||
1853 root->section.isPackageDoc()
1854 ) &&
1855 !root->name.empty()
1856 )
1857 {
1858 AUTO_TRACE("name={}",root->name);
1859
1860 DString fName = root->name;
1861 if (root->section.isPackageDoc())
1862 {
1863 fName=substitute(fName,".","::");
1864 }
1865
1866 DString fullName = stripAnonymousNamespaceScope(fName);
1867 if (!fullName.empty())
1868 {
1869 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1871 if (ndi) // existing namespace
1872 {
1874 if (nd) // non-inline namespace
1875 {
1876 AUTO_TRACE_ADD("Existing namespace");
1877 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1878 nd->setName(fullName); // change name to match docs
1880 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1881 if (nd->getLanguage()==SrcLangExt::Unknown)
1882 {
1883 nd->setLanguage(root->lang);
1884 }
1885 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
1886 // if we previously found namespace nd in a tag file and now we find a
1887 // documented namespace with the same name in the project, then remove
1888 // the tag file reference
1889 {
1890 nd->setReference("");
1891 nd->setFileName(fullName);
1892 }
1893 nd->setMetaData(root->metaData);
1894
1895 // file definition containing the namespace nd
1896 FileDef *fd=root->fileDef();
1897 if (nd->isArtificial())
1898 {
1899 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
1900 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1901 }
1902 // insert the namespace in the file definition
1903 if (fd) fd->insertNamespace(nd);
1904 addNamespaceToGroups(root,nd);
1905 nd->setRefItems(root->sli);
1906 nd->setRequirementReferences(root->rqli);
1907 addIncludeFile(nd,fd,root);
1908 }
1909 }
1910 else // fresh namespace
1911 {
1912 DString tagName;
1913 DString tagFileName;
1914 const TagInfo *tagInfo = root->tagInfo();
1915 if (tagInfo)
1916 {
1917 tagName = tagInfo->tagName;
1918 tagFileName = tagInfo->fileName;
1919 }
1920 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,root->lang,tagName);
1921 // add namespace to the list
1923 Doxygen::namespaceLinkedMap->add(fullName,
1924 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1925 root->startColumn,fullName,tagName,tagFileName,
1926 root->type,root->spec.isPublished())));
1927 if (nd)
1928 {
1929 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1930 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1932 nd->setHidden(root->hidden);
1933 nd->setArtificial(root->artificial);
1934 nd->setLanguage(root->lang);
1935 nd->setId(root->id);
1936 nd->setMetaData(root->metaData);
1937 nd->setInline(root->spec.isInline());
1938 nd->setExported(root->exported);
1939
1940 addNamespaceToGroups(root,nd);
1941 nd->setRefItems(root->sli);
1942 nd->setRequirementReferences(root->rqli);
1943
1944 // file definition containing the namespace nd
1945 FileDef *fd=root->fileDef();
1946 // insert the namespace in the file definition
1947 if (fd) fd->insertNamespace(nd);
1948
1949 // the empty string test is needed for extract all case
1950 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1951 nd->insertUsedFile(fd);
1952 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1953 nd->setBodyDef(fd);
1954 addIncludeFile(nd,fd,root);
1955
1956 // also add namespace to the correct structural context
1957 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1958 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
1959 if (d==nullptr) // we didn't find anything, create the scope artificially
1960 // anyway, so we can at least relate scopes properly.
1961 {
1962 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1964 if (dm)
1965 {
1966 dm->addInnerCompound(nd);
1967 }
1968 nd->setOuterScope(d);
1969 // TODO: Due to the order in which the tag file is written
1970 // a nested class can be found before its parent!
1971 }
1972 else
1973 {
1975 if (dm)
1976 {
1977 dm->addInnerCompound(nd);
1978 }
1979 nd->setOuterScope(d);
1980 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1982 {
1983 NamespaceDef *pnd = toNamespaceDef(d);
1984 if (pnd && pnd->isInline())
1985 {
1986 d = d->getOuterScope();
1987 if (d)
1988 {
1989 dm = toDefinitionMutable(d);
1990 if (dm)
1991 {
1992 auto aliasNd = createNamespaceDefAlias(d,nd);
1993 dm->addInnerCompound(aliasNd.get());
1994 DString aliasName = aliasNd->name();
1995 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1996 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1997 }
1998 }
1999 else
2000 {
2001 break;
2002 }
2003 }
2004 else
2005 {
2006 break;
2007 }
2008 }
2009 }
2010 }
2011 }
2012 }
2013 }
2014 for (const auto &e : root->children()) buildNamespaceList(e.get());
2015}
2016
2017//----------------------------------------------------------------------
2018
2020 const DString &name)
2021{
2022 NamespaceDef *usingNd =nullptr;
2023 for (auto &und : unl)
2024 {
2025 DString uScope=und->name()+"::";
2026 usingNd = getResolvedNamespace(uScope+name);
2027 if (usingNd!=nullptr) break;
2028 }
2029 return usingNd;
2030}
2031
2032static void findUsingDirectives(const Entry *root)
2033{
2034 if (root->section.isUsingDir())
2035 {
2036 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2037 DString name=substitute(root->name,".","::");
2038 if (name.endsWith("::"))
2039 {
2040 name=name.left(name.length()-2);
2041 }
2042 if (!name.empty())
2043 {
2044 NamespaceDef *usingNd = nullptr;
2045 NamespaceDefMutable *nd = nullptr;
2046 FileDef *fd = root->fileDef();
2047 DString nsName;
2048
2049 // see if the using statement was found inside a namespace or inside
2050 // the global file scope.
2051 if (root->parent() && root->parent()->section.isNamespace() &&
2052 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2053 )
2054 {
2055 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2056 if (!nsName.empty())
2057 {
2058 nd = getResolvedNamespaceMutable(nsName);
2059 }
2060 }
2061
2062 // find the scope in which the 'using' namespace is defined by prepending
2063 // the possible scopes in which the using statement was found, starting
2064 // with the most inner scope and going to the most outer scope (i.e.
2065 // file scope).
2066 int scopeOffset = static_cast<int>(nsName.length());
2067 do
2068 {
2069 DString scope=scopeOffset>0 ?
2070 nsName.left(scopeOffset)+"::" : DString();
2071 usingNd = getResolvedNamespace(scope+name);
2072 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2073 if (scopeOffset==0)
2074 {
2075 scopeOffset=-1;
2076 }
2077 else
2078 {
2079 size_t o = nsName.rfind("::",scopeOffset-1);
2080 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2081 }
2082 } while (scopeOffset>=0 && usingNd==nullptr);
2083
2084 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2085 // or in one of the parent namespace scopes
2086 {
2087 const NamespaceDefMutable *pnd = nd;
2088 while (pnd && usingNd==nullptr)
2089 {
2090 // also try with one of the used namespaces found earlier
2092
2093 // goto the parent
2094 Definition *s = pnd->getOuterScope();
2096 {
2098 }
2099 else
2100 {
2101 pnd = nullptr;
2102 }
2103 }
2104 }
2105 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2106 // global scope
2107 {
2108 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2109 }
2110
2111 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2112
2113 // add the namespace the correct scope
2114 if (usingNd)
2115 {
2116 //printf("using fd=%p nd=%p\n",fd,nd);
2117 if (nd)
2118 {
2119 //printf("Inside namespace %s\n",qPrint(nd->name()));
2120 nd->addUsingDirective(usingNd);
2121 }
2122 else if (fd)
2123 {
2124 //printf("Inside file %s\n",qPrint(fd->name()));
2125 fd->addUsingDirective(usingNd);
2126 }
2127 }
2128 else // unknown namespace, but add it anyway.
2129 {
2130 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,root->lang,root->hidden);
2131 // add namespace to the list
2134 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2135 if (nd)
2136 {
2137 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2138 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2140 nd->setHidden(root->hidden);
2141 nd->setArtificial(true);
2142 nd->setLanguage(root->lang);
2143 nd->setId(root->id);
2144 nd->setMetaData(root->metaData);
2145 nd->setInline(root->spec.isInline());
2146 nd->setExported(root->exported);
2147
2148 for (const Grouping &g : root->groups)
2149 {
2150 GroupDef *gd=nullptr;
2152 gd->addNamespace(nd);
2153 }
2154
2155 // insert the namespace in the file definition
2156 if (fd)
2157 {
2158 fd->insertNamespace(nd);
2159 fd->addUsingDirective(nd);
2160 }
2161
2162 // the empty string test is needed for extract all case
2163 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2164 nd->insertUsedFile(fd);
2165 nd->setRefItems(root->sli);
2166 nd->setRequirementReferences(root->rqli);
2167 }
2168 }
2169 }
2170 }
2171 for (const auto &e : root->children()) findUsingDirectives(e.get());
2172}
2173
2174//----------------------------------------------------------------------
2175
2176static void buildListOfUsingDecls(const Entry *root)
2177{
2178 if (root->section.isUsingDecl() &&
2179 !root->parent()->section.isCompound() // not a class/struct member
2180 )
2181 {
2182 DString name = substitute(root->name,".","::");
2183 g_usingDeclarations.insert(name.str());
2184 }
2185 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2186}
2187
2188
2189static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2190{
2191 if (root->section.isUsingDecl() &&
2192 !root->parent()->section.isCompound() && // not a class/struct member
2193 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2194 )
2195 {
2196 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2197 root->name,root->startLine,root->fileName,root->parent()->section);
2198 if (!root->name.empty())
2199 {
2200 const Definition *usingDef = nullptr;
2201 NamespaceDefMutable *nd = nullptr;
2202 FileDef *fd = root->fileDef();
2203 DString scName;
2204
2205 // see if the using statement was found inside a namespace or inside
2206 // the global file scope.
2207 if (root->parent()->section.isNamespace())
2208 {
2209 scName=root->parent()->name;
2210 if (!scName.empty())
2211 {
2212 nd = getResolvedNamespaceMutable(scName);
2213 }
2214 }
2215
2216 // Assume the using statement was used to import a class.
2217 // Find the scope in which the 'using' namespace is defined by prepending
2218 // the possible scopes in which the using statement was found, starting
2219 // with the most inner scope and going to the most outer scope (i.e.
2220 // file scope).
2221
2222 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2223
2224 SymbolResolver resolver;
2225 const Definition *scope = nd;
2226 if (nd==nullptr) scope = fd;
2227 usingDef = resolver.resolveSymbol(scope,name);
2228
2229 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2230
2231 if (!usingDef)
2232 {
2233 usingDef = getClass(name); // try direct lookup, this is needed to get
2234 // builtin STL classes to properly resolve, e.g.
2235 // vector -> std::vector
2236 }
2237 if (!usingDef)
2238 {
2239 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2240 }
2241#if 0
2242 if (!usingDef)
2243 {
2244 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2245 name,root->section,root->tArgLists.size());
2248 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2249 if (usingCd)
2250 {
2251 usingCd->setArtificial(true);
2252 usingCd->setLanguage(root->lang);
2253 usingDef = usingCd;
2254 }
2255 }
2256#endif
2257 else
2258 {
2259 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2260 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2261 }
2262
2263 if (usingDef)
2264 {
2265 if (nd)
2266 {
2267 nd->addUsingDeclaration(usingDef);
2268 }
2269 else if (fd)
2270 {
2271 fd->addUsingDeclaration(usingDef);
2272 }
2273 }
2274 }
2275 }
2276 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2277}
2278
2279//----------------------------------------------------------------------
2280
2282{
2283 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2284 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2285 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2286 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2287 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2288 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2289}
2290
2291//----------------------------------------------------------------------
2292
2294 const DString &fileName,const DString &memName)
2295{
2296 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2297 const ArgumentList &templAl = md->templateArguments();
2298 const ArgumentList &al = md->argumentList();
2299 auto newMd = createMemberDef(
2300 fileName,root->startLine,root->startColumn,
2301 md->typeString(),memName,md->argsString(),
2302 md->excpString(),root->protection,root->virt,
2303 md->isStatic(),Relationship::Member,md->memberType(),
2304 templAl,al,root->metaData
2305 );
2306 auto newMmd = toMemberDefMutable(newMd.get());
2307 newMmd->setMemberClass(cd);
2308 cd->insertMember(newMd.get());
2309 if (!root->doc.empty() || !root->brief.empty())
2310 {
2311 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2312 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2313 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2314 }
2315 else
2316 {
2317 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2318 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2319 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2320 }
2321 newMmd->setDefinition(md->definition());
2322 applyMemberOverrideOptions(root,newMmd);
2323 newMmd->addQualifiers(root->qualifiers);
2324 newMmd->setBitfields(md->bitfieldString());
2325 newMmd->addSectionsToDefinition(root->anchors);
2326 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2327 newMmd->setBodyDef(md->getBodyDef());
2328 newMmd->setInitializer(md->initializer());
2329 newMmd->setRequiresClause(md->requiresClause());
2330 newMmd->setMaxInitLines(md->initializerLines());
2331 newMmd->setMemberGroupId(root->mGrpId);
2332 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2333 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2334 newMmd->setLanguage(root->lang);
2335 newMmd->setId(root->id);
2337 mn->push_back(std::move(newMd));
2338}
2339
2340static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2341
2342static void findUsingDeclImports(const Entry *root)
2343{
2344 if (root->section.isUsingDecl() &&
2345 root->parent()->section.isCompound() // in a class/struct member
2346 )
2347 {
2348 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2349 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2350 fullName=stripAnonymousNamespaceScope(fullName);
2351 fullName=stripTemplateSpecifiersFromScope(fullName);
2352 ClassDefMutable *cd = getClassMutable(fullName);
2353 if (cd)
2354 {
2355 AUTO_TRACE_ADD("found class '{}'",cd->name());
2356 size_t i=root->name.rfind("::");
2357 if (i!=DString::npos)
2358 {
2359 DString scope=root->name.left(i);
2360 DString memName=root->name.mid(i+2);
2361 SymbolResolver resolver;
2362 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2363 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2364 if (bcd && bcd!=cd)
2365 {
2366 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2368 const MemberNameInfo *mni = mnlm.find(memName);
2369 if (mni)
2370 {
2371 for (auto &mi : *mni)
2372 {
2373 const MemberDef *md = mi->memberDef();
2374 if (md && md->protection()!=Protection::Private)
2375 {
2376 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2377 DString fileName = root->fileName;
2378 if (fileName.empty() && root->tagInfo())
2379 {
2380 fileName = root->tagInfo()->tagName;
2381 }
2382 if (!cd->containsOverload(md))
2383 {
2384 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2385 // also insert the member into copies of the class
2386 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2387 if (it != g_usingClassMap.end())
2388 {
2389 for (const auto &copyCd : it->second)
2390 {
2391 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2392 }
2393 }
2394 }
2395 }
2396 }
2397 }
2398 }
2399 }
2400 }
2401 }
2402 else if (root->section.isUsingDecl() &&
2403 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2404 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2405 )
2406 {
2407 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2408 Definition *scope = nullptr;
2409 NamespaceDefMutable *nd = nullptr;
2410 FileDef *fd = root->parent()->fileDef();
2411 if (!root->parent()->name.empty())
2412 {
2413 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2414 fullName=stripAnonymousNamespaceScope(fullName);
2416 scope = nd;
2417 }
2418 else
2419 {
2420 scope = fd;
2421 }
2422 if (scope)
2423 {
2424 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2425 SymbolResolver resolver;
2426 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2427 if (def && def->definitionType()==Definition::TypeMember)
2428 {
2429 size_t i=root->name.rfind("::");
2430 DString memName;
2431 if (i!=DString::npos)
2432 {
2433 memName = root->name.right(root->name.length()-i-2);
2434 }
2435 else
2436 {
2437 memName = root->name;
2438 }
2439 const MemberDef *md = toMemberDef(def);
2440 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2441 DString fileName = root->fileName;
2442 if (fileName.empty() && root->tagInfo())
2443 {
2444 fileName = root->tagInfo()->tagName;
2445 }
2446 const ArgumentList &templAl = md->templateArguments();
2447 const ArgumentList &al = md->argumentList();
2448
2449 auto newMd = createMemberDef(
2450 fileName,root->startLine,root->startColumn,
2451 md->typeString(),memName,md->argsString(),
2452 md->excpString(),root->protection,root->virt,
2453 md->isStatic(),Relationship::Member,md->memberType(),
2454 templAl,al,root->metaData
2455 );
2456 auto newMmd = toMemberDefMutable(newMd.get());
2457 if (nd)
2458 {
2459 newMmd->setNamespace(nd);
2460 nd->insertMember(newMd.get());
2461 }
2462 if (fd)
2463 {
2464 newMmd->setFileDef(fd);
2465 fd->insertMember(newMd.get());
2466 }
2467 if (!root->doc.empty() || !root->brief.empty())
2468 {
2469 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2470 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2471 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2472 }
2473 else
2474 {
2475 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2476 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2477 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2478 }
2479 newMmd->setDefinition(md->definition());
2480 applyMemberOverrideOptions(root,newMmd);
2481 newMmd->addQualifiers(root->qualifiers);
2482 newMmd->setBitfields(md->bitfieldString());
2483 newMmd->addSectionsToDefinition(root->anchors);
2484 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2485 newMmd->setBodyDef(md->getBodyDef());
2486 newMmd->setInitializer(md->initializer());
2487 newMmd->setRequiresClause(md->requiresClause());
2488 newMmd->setMaxInitLines(md->initializerLines());
2489 newMmd->setMemberGroupId(root->mGrpId);
2490 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2491 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2492 newMmd->setLanguage(root->lang);
2493 newMmd->setId(root->id);
2495 mn->push_back(std::move(newMd));
2496#if 0 // insert an alias instead of a copy
2497 const MemberDef *md = toMemberDef(def);
2498 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2499 auto aliasMd = createMemberDefAlias(nd,md);
2500 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2501 if (nd && aliasMd.get())
2502 {
2503 nd->insertMember(aliasMd.get());
2504 }
2505 if (fd && aliasMd.get())
2506 {
2507 fd->insertMember(aliasMd.get());
2508 }
2509 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2510 mn->push_back(std::move(aliasMd));
2511#endif
2512 }
2513 else if (def && def->definitionType()==Definition::TypeClass)
2514 {
2515 const ClassDef *cd = toClassDef(def);
2516 DString copyFullName;
2517 if (nd==nullptr)
2518 {
2519 copyFullName = cd->localName();
2520 }
2521 else
2522 {
2523 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2524 }
2525 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2526 {
2528 Doxygen::classLinkedMap->add(copyFullName,
2529 cd->deepCopy(copyFullName)));
2530 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2531 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2532 if (ncdm)
2533 {
2534 if (nd) ncdm->moveTo(nd);
2535 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2536 {
2537 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2538 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2539 }
2540 else // use docs from used class
2541 {
2542 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2544 }
2545 if (nd)
2546 {
2547 nd->addInnerCompound(ncdm);
2548 nd->addUsingDeclaration(ncdm);
2549 }
2550 if (fd)
2551 {
2552 if (ncdm) ncdm->setFileDef(fd);
2553 fd->insertClass(ncdm);
2554 fd->addUsingDeclaration(ncdm);
2555 }
2556 }
2557 }
2558#if 0 // insert an alias instead of a copy
2559 auto aliasCd = createClassDefAlias(nd,cd);
2560 DString aliasFullName;
2561 if (nd==nullptr)
2562 {
2563 aliasFullName = aliasCd->localName();
2564 }
2565 else
2566 {
2567 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2568 }
2569 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2570 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2571 if (nd && acd)
2572 {
2573 nd->addInnerCompound(acd);
2574 }
2575 if (fd && acd)
2576 {
2577 fd->insertClass(acd);
2578 }
2579#endif
2580 }
2581 else if (scope)
2582 {
2583 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2584 }
2585 }
2586 }
2587 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2588}
2589
2590//----------------------------------------------------------------------
2591
2593{
2594 FileDefSet visitedFiles;
2595 // then recursively add using directives found in #include files
2596 // to files that have not been visited.
2597 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2598 {
2599 for (const auto &fd : *fn)
2600 {
2601 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2602 fd->addIncludedUsingDirectives(visitedFiles);
2603 }
2604 }
2605}
2606
2607//----------------------------------------------------------------------
2608
2610 const Entry *root,
2611 ClassDefMutable *cd,
2612 MemberType mtype,
2613 const DString &type,
2614 const DString &name,
2615 const DString &args,
2616 Protection prot,
2617 Relationship related)
2618{
2620 DString scopeSeparator="::";
2621 SrcLangExt lang = cd->getLanguage();
2622 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2623 {
2624 qualScope = substitute(qualScope,"::",".");
2625 scopeSeparator=".";
2626 }
2627 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2628 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, root->initializer.str());
2629
2630 DString def;
2631 if (!type.empty())
2632 {
2633 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2634 {
2635 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2636 {
2637 if (lang==SrcLangExt::Python)
2638 {
2639 def="type "+name+args;
2640 }
2641 else
2642 {
2643 def="using "+name;
2644 }
2645 }
2646 else
2647 {
2648 def=type+" "+name+args;
2649 }
2650 }
2651 else
2652 {
2653 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2654 {
2655 if (lang==SrcLangExt::Python)
2656 {
2657 def="type "+qualScope+scopeSeparator+name+args;
2658 }
2659 else
2660 {
2661 def="using "+qualScope+scopeSeparator+name;
2662 }
2663 }
2664 else
2665 {
2666 def=type+" "+qualScope+scopeSeparator+name+args;
2667 }
2668 }
2669 }
2670 else
2671 {
2672 if (Config_getBool(HIDE_SCOPE_NAMES))
2673 {
2674 def=name+args;
2675 }
2676 else
2677 {
2678 def=qualScope+scopeSeparator+name+args;
2679 }
2680 }
2681 def.stripPrefix("static ");
2682
2683 // see if the member is already found in the same scope
2684 // (this may be the case for a static member that is initialized
2685 // outside the class)
2687 if (mn)
2688 {
2689 for (const auto &imd : *mn)
2690 {
2691 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2692 // md->getClassDef(),cd,qPrint(type),md->typeString());
2693 MemberDefMutable *md = toMemberDefMutable(imd.get());
2694 if (md &&
2695 md->getClassDef()==cd &&
2696 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2698 // member already in the scope
2699 {
2700
2701 if (root->lang==SrcLangExt::ObjC &&
2702 root->mtype==MethodTypes::Property &&
2703 md->memberType()==MemberType::Variable)
2704 { // Objective-C 2.0 property
2705 // turn variable into a property
2706 md->setProtection(root->protection);
2707 cd->reclassifyMember(md,MemberType::Property);
2708 }
2709 addMemberDocs(root,md,def,nullptr,false,root->spec);
2710 AUTO_TRACE_ADD("Member already found!");
2711 return md;
2712 }
2713 }
2714 }
2715
2716 DString fileName = root->fileName;
2717 if (fileName.empty() && root->tagInfo())
2718 {
2719 fileName = root->tagInfo()->tagName;
2720 }
2721
2722 // new member variable, typedef or enum value
2723 auto md = createMemberDef(
2724 fileName,root->startLine,root->startColumn,
2725 type,name,args,root->exception,
2726 prot,Specifier::Normal,root->isStatic,related,
2727 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2728 ArgumentList(), root->metaData);
2729 auto mmd = toMemberDefMutable(md.get());
2730 mmd->setTagInfo(root->tagInfo());
2731 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2732 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2733 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2734 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2735 mmd->setDefinition(def);
2736 mmd->setBitfields(root->bitfields);
2737 mmd->addSectionsToDefinition(root->anchors);
2738 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2739 mmd->setInitializer(root->initializer.str());
2740 mmd->setMaxInitLines(root->initLines);
2741 mmd->setMemberGroupId(root->mGrpId);
2742 mmd->setMemberSpecifiers(root->spec);
2743 mmd->setVhdlSpecifiers(root->vhdlSpec);
2744 mmd->setReadAccessor(root->read);
2745 mmd->setWriteAccessor(root->write);
2747 mmd->setHidden(root->hidden);
2748 mmd->setArtificial(root->artificial);
2749 mmd->setLanguage(root->lang);
2750 mmd->setId(root->id);
2751 addMemberToGroups(root,md.get());
2753 mmd->setBodyDef(root->fileDef());
2754 mmd->addQualifiers(root->qualifiers);
2755 addIncludeFile(mmd,root->fileDef(),root);
2756
2757 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2758 cd->insertMember(md.get());
2759 mmd->setRefItems(root->sli);
2760 mmd->setRequirementReferences(root->rqli);
2761
2762 cd->insertUsedFile(root->fileDef());
2763 root->markAsProcessed();
2764
2765 if (mtype==MemberType::Typedef)
2766 {
2767 resolveTemplateInstanceInType(root,cd,md.get());
2768 }
2769
2770 // add the member to the global list
2771 MemberDef *result = md.get();
2773 mn->push_back(std::move(md));
2774
2775 return result;
2776}
2777
2778//----------------------------------------------------------------------
2779
2781 const Entry *root,
2782 MemberType mtype,
2783 const DString &scope,
2784 const DString &type,
2785 const DString &name,
2786 const DString &args)
2787{
2788 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2789 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2790
2791 FileDef *fd = root->fileDef();
2792
2793 // see if we have a typedef that should hide a struct or union
2794 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2795 {
2796 DString ttype = type;
2797 ttype.stripPrefix("typedef ");
2798 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2799 {
2800 static const reg::Ex re(R"(\a\w*)");
2801 reg::Match match;
2802 const std::string &typ = ttype.str();
2803 if (reg::search(typ,match,re))
2804 {
2805 DString typeValue = match.str();
2806 ClassDefMutable *cd = getClassMutable(typeValue);
2807 if (cd)
2808 {
2809 // this typedef should hide compound name cd, so we
2810 // change the name that is displayed from cd.
2811 cd->setClassName(name);
2812 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2813 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2814 return nullptr;
2815 }
2816 }
2817 }
2818 }
2819
2820 // see if the function is inside a namespace
2821 NamespaceDefMutable *nd = nullptr;
2822 if (!scope.empty())
2823 {
2824 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2825 nd = getResolvedNamespaceMutable(scope);
2826 }
2827 DString def;
2828
2829 // determine the definition of the global variable
2830 if (nd && !nd->isAnonymous() &&
2831 !Config_getBool(HIDE_SCOPE_NAMES)
2832 )
2833 // variable is inside a namespace, so put the scope before the name
2834 {
2835 SrcLangExt lang = nd->getLanguage();
2837
2838 if (!type.empty())
2839 {
2840 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2841 {
2842 if (lang==SrcLangExt::Python)
2843 {
2844 def="type "+nd->name()+sep+name+args;
2845 }
2846 else
2847 {
2848 def="using "+nd->name()+sep+name;
2849 }
2850 }
2851 else // normal member
2852 {
2853 def=type+" "+nd->name()+sep+name+args;
2854 }
2855 }
2856 else
2857 {
2858 def=nd->name()+sep+name+args;
2859 }
2860 }
2861 else
2862 {
2863 if (!type.empty() && !root->name.empty())
2864 {
2865 if (name.at(0)=='@') // dummy variable representing anonymous union
2866 {
2867 def=type;
2868 }
2869 else
2870 {
2871 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2872 {
2873 if (root->lang==SrcLangExt::Python)
2874 {
2875 def="type "+root->name+args;
2876 }
2877 else
2878 {
2879 def="using "+root->name;
2880 }
2881 }
2882 else // normal member
2883 {
2884 def=type+" "+name+args;
2885 }
2886 }
2887 }
2888 else
2889 {
2890 def=name+args;
2891 }
2892 }
2893 def.stripPrefix("static ");
2894
2896 if (mn)
2897 {
2898 //DString nscope=removeAnonymousScopes(scope);
2899 //NamespaceDef *nd=nullptr;
2900 //if (!nscope.empty())
2901 if (!scope.empty())
2902 {
2903 nd = getResolvedNamespaceMutable(scope);
2904 }
2905 for (const auto &imd : *mn)
2906 {
2907 MemberDefMutable *md = toMemberDefMutable(imd.get());
2908 if (md &&
2909 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2910 root->fileName==md->getFileDef()->absFilePath()
2911 ) // both variable names in the same file
2912 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2913 )
2914 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2915 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2916 )
2917 // variable already in the scope
2918 {
2919 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2920 md->argsString()!=args &&
2921 args.find('[')!=DString::npos;
2922 bool staticsInDifferentFiles =
2923 root->isStatic && md->isStatic() &&
2924 root->fileName!=md->getDefFileName();
2925
2926 if (md->getFileDef() &&
2927 !isPHPArray && // not a php array
2928 !staticsInDifferentFiles
2929 )
2930 // not a php array variable
2931 {
2932 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2933 addMemberDocs(root,md,def,nullptr,false,root->spec);
2934 md->setRefItems(root->sli);
2935 md->setRequirementReferences(root->rqli);
2936 // if md is a variable forward declaration and root is the definition that
2937 // turn md into the definition
2938 if (!root->explicitExternal && md->isExternal())
2939 {
2940 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2941 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
2942 }
2943 // if md is the definition and root point at a declaration, then add the
2944 // declaration info
2945 else if (root->explicitExternal && !md->isExternal())
2946 {
2947 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2948 }
2949 return md;
2950 }
2951 }
2952 }
2953 }
2954
2955 DString fileName = root->fileName;
2956 if (fileName.empty() && root->tagInfo())
2957 {
2958 fileName = root->tagInfo()->tagName;
2959 }
2960
2961 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
2962 // new global variable, enum value or typedef
2963 auto md = createMemberDef(
2964 fileName,root->startLine,root->startColumn,
2965 type,name,args,DString(),
2966 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2967 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2968 root->argList, root->metaData);
2969 auto mmd = toMemberDefMutable(md.get());
2970 mmd->setTagInfo(root->tagInfo());
2971 mmd->setMemberSpecifiers(root->spec);
2972 mmd->setVhdlSpecifiers(root->vhdlSpec);
2973 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2974 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2975 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2976 mmd->addSectionsToDefinition(root->anchors);
2977 mmd->setInitializer(root->initializer.str());
2978 mmd->setMaxInitLines(root->initLines);
2979 mmd->setMemberGroupId(root->mGrpId);
2980 mmd->setDefinition(def);
2981 mmd->setLanguage(root->lang);
2982 mmd->setId(root->id);
2984 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2985 mmd->addQualifiers(root->qualifiers);
2986 //md->setOuterScope(fd);
2987 if (!root->explicitExternal)
2988 {
2989 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2990 mmd->setBodyDef(fd);
2991 }
2992 addMemberToGroups(root,md.get());
2993 addIncludeFile(mmd,fd,root);
2995
2996 mmd->setRefItems(root->sli);
2997 mmd->setRequirementReferences(root->rqli);
2998 if (nd && !nd->isAnonymous())
2999 {
3000 mmd->setNamespace(nd);
3001 nd->insertMember(md.get());
3002 }
3003
3004 // add member to the file (we do this even if we have already inserted
3005 // it into the namespace.
3006 if (fd)
3007 {
3008 mmd->setFileDef(fd);
3009 fd->insertMember(md.get());
3010 }
3011
3012 root->markAsProcessed();
3013
3014 if (mtype==MemberType::Typedef)
3015 {
3016 resolveTemplateInstanceInType(root,nd,md.get());
3017 }
3018
3019 // add member definition to the list of globals
3020 MemberDef *result = md.get();
3022 mn->push_back(std::move(md));
3023
3024
3025
3026 return result;
3027}
3028
3029/*! See if the return type string \a type is that of a function pointer
3030 * \returns -1 if this is not a function pointer variable or
3031 * the index at which the closing brace of (...*name) was found.
3032 */
3033static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3034{
3035 AUTO_TRACE("type='{}' lang={}",type,lang);
3036 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3037 {
3038 return -1; // Fortran and VHDL do not have function pointers
3039 }
3040
3041 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3042 reg::Match match;
3043 size_t i=std::string::npos;
3044 size_t l=0;
3045 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3046 {
3047 i = match.position();
3048 l = match.length();
3049 }
3050 if (i!=std::string::npos)
3051 {
3052 size_t di = type.find("decltype(");
3053 if (di!=std::string::npos && di<i)
3054 {
3055 i = std::string::npos;
3056 }
3057 }
3058 size_t bb=type.find('<');
3059 size_t be=type.rfind('>');
3060 bool templFp = false;
3061 if (be!=std::string::npos) {
3062 size_t cc_ast = type.find("::*");
3063 size_t cc_amp = type.find("::&");
3064 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>::*)'
3065 }
3066
3067 if (!type.empty() && // return type is non-empty
3068 i!=std::string::npos && // contains (...*...)
3069 type.find("operator")==std::string::npos && // not an operator
3070 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3071 // not a function pointer return type
3072 (!((bb!=std::string::npos && bb<i) && (be!=std::string::npos && i<be)) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
3073 )
3074 {
3075 if (pLength) *pLength=static_cast<int>(l);
3076 //printf("findFunctionPtr=%d\n",(int)i);
3077 AUTO_TRACE_EXIT("result={}",i);
3078 return static_cast<int>(i);
3079 }
3080 else
3081 {
3082 //printf("findFunctionPtr=%d\n",-1);
3083 AUTO_TRACE_EXIT("result=-1");
3084 return -1;
3085 }
3086}
3087
3088//--------------------------------------------------------------------------------------
3089
3090/*! Returns true iff \a type is a class within scope \a context.
3091 * Used to detect variable declarations that look like function prototypes.
3092 */
3093static bool isVarWithConstructor(const Entry *root)
3094{
3095 bool result = false;
3096 bool typeIsClass = false;
3097 bool typePtrType = false;
3098 DString type;
3099 Definition *ctx = nullptr;
3100 FileDef *fd = root->fileDef();
3101 SymbolResolver resolver(fd);
3102
3103 AUTO_TRACE("isVarWithConstructor({})",root->name);
3104 if (root->parent()->section.isCompound())
3105 { // inside a class
3106 result=false;
3107 AUTO_TRACE_EXIT("inside class: result={}",result);
3108 return result;
3109 }
3110 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3111 { // inside a .c file
3112 result=false;
3113 AUTO_TRACE_EXIT("inside C file: result={}",result);
3114 return result;
3115 }
3116 if (root->type.empty())
3117 {
3118 result=false;
3119 AUTO_TRACE_EXIT("no type: result={}",result);
3120 return result;
3121 }
3122 if (!root->parent()->name.empty())
3123 {
3125 }
3126 type = root->type;
3127 // remove qualifiers
3128 type.findAndRemoveWord("const");
3129 type.findAndRemoveWord("static");
3130 type.findAndRemoveWord("volatile");
3131 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3132 if (!typePtrType)
3133 {
3134 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3135 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3136 {
3137 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3138 }
3139 }
3140 if (typeIsClass) // now we still have to check if the arguments are
3141 // types or values. Since we do not have complete type info
3142 // we need to rely on heuristics :-(
3143 {
3144 if (root->argList.empty())
3145 {
3146 result=false; // empty arg list -> function prototype.
3147 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3148 return result;
3149 }
3150 for (const Argument &a : root->argList)
3151 {
3152 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3153 reg::Match match;
3154 if (!a.name.empty() || !a.defval.empty())
3155 {
3156 std::string name = a.name.str();
3157 if (reg::search(name,match,initChars) && match.position()==0)
3158 {
3159 result=true;
3160 }
3161 else
3162 {
3163 result=false; // arg has (type,name) pair -> function prototype
3164 }
3165 AUTO_TRACE_EXIT("function prototype: result={}",result);
3166 return result;
3167 }
3168 if (!a.type.empty() &&
3169 (a.type.at(a.type.length()-1)=='*' ||
3170 a.type.at(a.type.length()-1)=='&'))
3171 // type ends with * or & => pointer or reference
3172 {
3173 result=false;
3174 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3175 return result;
3176 }
3177 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3178 {
3179 result=false; // arg type is a known type
3180 AUTO_TRACE_EXIT("known type: result={}",result);
3181 return result;
3182 }
3183 if (checkIfTypedef(ctx,fd,a.type))
3184 {
3185 result=false; // argument is a typedef
3186 AUTO_TRACE_EXIT("typedef: result={}",result);
3187 return result;
3188 }
3189 std::string atype = a.type.str();
3190 if (reg::search(atype,match,initChars) && match.position()==0)
3191 {
3192 result=true; // argument type starts with typical initializer char
3193 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3194 return result;
3195 }
3196 std::string resType=resolveTypeDef(ctx,a.type).str();
3197 if (resType.empty()) resType=atype;
3198 static const reg::Ex idChars(R"(\a\w*)");
3199 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3200 {
3201 resType=match.str();
3202 if (resType=="int" || resType=="long" ||
3203 resType=="float" || resType=="double" ||
3204 resType=="char" || resType=="void" ||
3205 resType=="signed" || resType=="unsigned" ||
3206 resType=="const" || resType=="volatile" )
3207 {
3208 result=false; // type keyword -> function prototype
3209 AUTO_TRACE_EXIT("type keyword: result={}",result);
3210 return result;
3211 }
3212 }
3213 }
3214 result=true;
3215 }
3216
3217 AUTO_TRACE_EXIT("end: result={}",result);
3218 return result;
3219}
3220
3221//--------------------------------------------------------------------------------------
3222
3223/*! Searches for the end of a template in prototype \a s starting from
3224 * character position \a startPos. If the end was found the position
3225 * of the closing > is returned, otherwise -1 is returned.
3226 *
3227 * Handles exotic cases such as
3228 * \code
3229 * Class<(id<0)>
3230 * Class<bits<<2>
3231 * Class<"<">
3232 * Class<'<'>
3233 * Class<(")<")>
3234 * \endcode
3235 */
3236static int findEndOfTemplate(const DString &s,size_t startPos)
3237{
3238 // locate end of template
3239 size_t e=startPos;
3240 int brCount=1;
3241 int roundCount=0;
3242 size_t len = s.length();
3243 bool insideString=false;
3244 bool insideChar=false;
3245 char pc = 0;
3246 while (e<len && brCount!=0)
3247 {
3248 char c=s.at(e);
3249 switch(c)
3250 {
3251 case '<':
3252 if (!insideString && !insideChar)
3253 {
3254 if (e<len-1 && s.at(e+1)=='<')
3255 e++;
3256 else if (roundCount==0)
3257 brCount++;
3258 }
3259 break;
3260 case '>':
3261 if (!insideString && !insideChar)
3262 {
3263 if (e<len-1 && s.at(e+1)=='>')
3264 e++;
3265 else if (roundCount==0)
3266 brCount--;
3267 }
3268 break;
3269 case '(':
3270 if (!insideString && !insideChar)
3271 roundCount++;
3272 break;
3273 case ')':
3274 if (!insideString && !insideChar)
3275 roundCount--;
3276 break;
3277 case '"':
3278 if (!insideChar)
3279 {
3280 if (insideString && pc!='\\')
3281 insideString=false;
3282 else
3283 insideString=true;
3284 }
3285 break;
3286 case '\'':
3287 if (!insideString)
3288 {
3289 if (insideChar && pc!='\\')
3290 insideChar=false;
3291 else
3292 insideChar=true;
3293 }
3294 break;
3295 }
3296 pc = c;
3297 e++;
3298 }
3299 return brCount==0 ? static_cast<int>(e) : -1;
3300}
3301
3302//--------------------------------------------------------------------------------------
3303
3304static void addVariable(const Entry *root,int isFuncPtr=-1)
3305{
3306 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3307
3308 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3309 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3310 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3311
3312 DString type = root->type;
3313 DString name = root->name;
3314 DString args = root->args;
3315 if (type.empty() && name.find("operator")==DString::npos &&
3316 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3317 {
3318 // recover from parse error caused by redundant braces
3319 // like in "int *(var[10]);", which is parsed as
3320 // type="" name="int *" args="(var[10])"
3321
3322 type=name;
3323 std::string sargs = args.str();
3324 static const reg::Ex reName(R"(\a\w*)");
3325 reg::Match match;
3326 if (reg::search(sargs,match,reName))
3327 {
3328 name = match.str(); // e.g. 'var' in '(var[10])'
3329 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3330 size_t j = sargs.find(')');
3331 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3332 }
3333 }
3334 else
3335 {
3336 int i=isFuncPtr;
3337 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3338 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3339 if (i>=0) // function pointer
3340 {
3341 size_t ii = i;
3342 size_t ai = type.find('[',ii);
3343 if (ai!=DString::npos && ai>ii) // function pointer array
3344 {
3345 args.prepend(type.mid(ai));
3346 type=type.left(ai);
3347 }
3348 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3349 {
3350 type=type.left(type.length()-1);
3351 args.prepend(") ");
3352 }
3353 }
3354 }
3355 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3356
3357 DString scope;
3358 name=removeRedundantWhiteSpace(name);
3359
3360 // find the scope of this variable
3361 int index = computeQualifiedIndex(name);
3362 // grouped members are stored with full scope
3363 bool qualifiedGroupMember = index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo();
3364 // A specialization of a variable template that lives in a namespace, i.e.
3365 // `template<class T> constexpr bool std::ranges::enable_view<MyView<T>> = true;`.
3366 // Its scope has to be spelled out, so it must not be mistaken for the definition of a static
3367 // data member, which is declared in its class as well and is therefore picked up from there.
3368 bool variableTemplateSpecialization = index!=-1 && !root->tArgLists.empty() &&
3369 name.mid(index+2).find('<')!=DString::npos &&
3370 getClass(name.left(index))==nullptr;
3371 if (qualifiedGroupMember || variableTemplateSpecialization)
3372 {
3373 Definition *d = buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3374 if (variableTemplateSpecialization)
3375 // the scope may have been made up for this specialization, but it holds a documented member
3376 // now, which is only reachable through a page of its own
3377 {
3378 for (Definition *s=d; s && s!=Doxygen::globalScope; s=s->getOuterScope())
3379 {
3381 if (ndm && ndm->isArtificial()) ndm->setArtificial(false);
3382 }
3383 }
3384 scope=name.left(index);
3385 name=name.mid(index+2);
3386 }
3387 else
3388 {
3389 Entry *p = root->parent();
3390 while (p->section.isScope())
3391 {
3392 DString scopeName = p->name;
3393 if (!scopeName.empty())
3394 {
3395 scope.prepend(scopeName);
3396 break;
3397 }
3398 p=p->parent();
3399 }
3400 }
3401
3402 DString type_s = type;
3403 type=type.stripWhiteSpace();
3404 ClassDefMutable *cd=nullptr;
3405 bool isRelated=false;
3406 bool isMemberOf=false;
3407
3408 DString classScope=stripAnonymousNamespaceScope(scope);
3409 if (root->lang==SrcLangExt::CSharp)
3410 {
3411 classScope=mangleCSharpGenericName(classScope);
3412 }
3413 else
3414 {
3415 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3416 }
3417 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3418
3419
3420 // Look for last :: not part of template specifier
3421 int p=-1;
3422 for (size_t i=0;i<name.length()-1;i++)
3423 {
3424 if (name[i]==':' && name[i+1]==':')
3425 {
3426 p=static_cast<int>(i);
3427 }
3428 else if (name[i]=='<') // skip over template parts,
3429 // i.e. A::B<C::D> => p=1 and
3430 // A<B::C>::D => p=8
3431 {
3432 int e = findEndOfTemplate(name,i+1);
3433 if (e!=-1) i=static_cast<int>(e);
3434 }
3435 }
3436
3437 if (p!=-1) // found it
3438 {
3439 if (isTypeAClassFriend(type))
3440 {
3441 cd=getClassMutable(scope);
3442 if (cd)
3443 {
3444 addVariableToClass(root, // entry
3445 cd, // class to add member to
3446 MemberType::Friend, // type of member
3447 type, // type value as string
3448 name, // name of the member
3449 args, // arguments as string
3450 Protection::Public, // protection
3451 Relationship::Member // related to a class
3452 );
3453 }
3454 }
3455 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3456 {
3457 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3458 }
3459
3460
3461 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3462 return; /* skip this member, because it is a
3463 * static variable definition (always?), which will be
3464 * found in a class scope as well, but then we know the
3465 * correct protection level, so only then it will be
3466 * inserted in the correct list!
3467 */
3468 }
3469
3470 MemberType mtype = MemberType::Variable;
3471 if (type=="@")
3472 mtype=MemberType::EnumValue;
3473 else if (type_s.startsWith("typedef "))
3474 mtype=MemberType::Typedef;
3475 else if (type_s.startsWith("friend ") || type_s=="friend")
3476 mtype=MemberType::Friend;
3477 else if (root->mtype==MethodTypes::Property)
3478 mtype=MemberType::Property;
3479 else if (root->mtype==MethodTypes::Event)
3480 mtype=MemberType::Event;
3481 else if (type.find("sequence<") != DString::npos)
3482 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3483 else if (type.find("dictionary<") != DString::npos)
3484 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3485
3486 if (!root->relates.empty()) // related variable
3487 {
3488 isRelated=true;
3489 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3490 if (getClass(root->relates)==nullptr && !scope.empty())
3491 scope=mergeScopes(scope,root->relates);
3492 else
3493 scope=root->relates;
3494 }
3495
3496 cd=getClassMutable(scope);
3497 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3498 if (cd)
3499 {
3500 // if cd is an anonymous (=tag less) scope we insert the member
3501 // into a non-anonymous parent scope as well. This is needed to
3502 // be able to refer to it using \var or \fn
3503
3504 Relationship relationship = isMemberOf ? Relationship::Foreign :
3505 isRelated ? Relationship::Related :
3506 Relationship::Member ;
3507
3508 addVariableToClass(root, // entry
3509 cd, // class to add member to
3510 mtype, // member type
3511 type, // type value as string
3512 name, // name of the member
3513 args, // arguments as string
3514 root->protection,
3515 relationship
3516 );
3517 }
3518 else if (!name.empty()) // global variable
3519 {
3520 addVariableToFile(root,mtype,scope,type,name,args);
3521 }
3522
3523}
3524
3525//----------------------------------------------------------------------
3526// Searches the Entry tree for typedef documentation sections.
3527// If found they are stored in their class or in the global list.
3528static void buildTypedefList(const Entry *root)
3529{
3530 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3531 if (!root->name.empty() &&
3532 root->section.isVariable() &&
3533 root->type.find("typedef ")!=DString::npos // its a typedef
3534 )
3535 {
3536 AUTO_TRACE();
3537 DString rname = removeRedundantWhiteSpace(root->name);
3538 DString scope;
3539 int index = computeQualifiedIndex(rname);
3540 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3541 // grouped members are stored with full scope
3542 {
3543 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3544 scope=rname.left(index);
3545 rname=rname.mid(index+2);
3546 }
3547 else
3548 {
3549 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3550 }
3554 bool found=false;
3555 if (mn) // symbol with the same name already found
3556 {
3557 for (auto &imd : *mn)
3558 {
3559 if (!imd->isTypedef())
3560 continue;
3561
3562 DString rtype = root->type;
3563 rtype.stripPrefix("typedef ");
3564
3565 // merge the typedefs only if they're not both grouped, and both are
3566 // either part of the same class, part of the same namespace, or both
3567 // are global (i.e., neither in a class or a namespace)
3568 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3569 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3570 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3571 // qPrint(imd->typeString()),qPrint(root->type));
3572 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3573 {
3574 MemberDefMutable *md = toMemberDefMutable(imd.get());
3575 if (md)
3576 {
3577 md->setDocumentation(root->doc,root->docFile,root->docLine);
3579 md->setDocsForDefinition(!root->proto);
3580 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3582 md->setRefItems(root->sli);
3583 md->setRequirementReferences(root->rqli);
3584 md->addQualifiers(root->qualifiers);
3585
3586 // merge ingroup specifiers
3587 if (md->getGroupDef()==nullptr && !root->groups.empty())
3588 {
3589 addMemberToGroups(root,md);
3590 }
3591 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3592 {
3593 //printf("existing member is grouped, new member not\n");
3594 }
3595 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3596 {
3597 //printf("both members are grouped\n");
3598 }
3599 found=true;
3600 break;
3601 }
3602 }
3603 }
3604 }
3605 if (found)
3606 {
3607 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3608 // mark the entry as processed, as we copied everything from it elsewhere
3609 // also, otherwise, due to containing `typedef` it may later get treated
3610 // as a function typedef in filterMemberDocumentation, which is incorrect
3611 root->markAsProcessed();
3612 }
3613 else
3614 {
3615 AUTO_TRACE_ADD("new typedef '{}'",rname);
3616 addVariable(root);
3617 }
3618
3619 }
3620 for (const auto &e : root->children())
3621 if (!e->section.isEnum())
3622 buildTypedefList(e.get());
3623}
3624
3625//----------------------------------------------------------------------
3626// Searches the Entry tree for sequence documentation sections.
3627// If found they are stored in the global list.
3628static void buildSequenceList(const Entry *root)
3629{
3630 if (!root->name.empty() &&
3631 root->section.isVariable() &&
3632 root->type.find("sequence<")!=DString::npos // it's a sequence
3633 )
3634 {
3635 AUTO_TRACE();
3636 addVariable(root);
3637 }
3638 for (const auto &e : root->children())
3639 if (!e->section.isEnum())
3640 buildSequenceList(e.get());
3641}
3642
3643//----------------------------------------------------------------------
3644// Searches the Entry tree for dictionary documentation sections.
3645// If found they are stored in the global list.
3646static void buildDictionaryList(const Entry *root)
3647{
3648 if (!root->name.empty() &&
3649 root->section.isVariable() &&
3650 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3651 )
3652 {
3653 AUTO_TRACE();
3654 addVariable(root);
3655 }
3656 for (const auto &e : root->children())
3657 if (!e->section.isEnum())
3658 buildDictionaryList(e.get());
3659}
3660
3661//----------------------------------------------------------------------
3662// Searches the Entry tree for Variable documentation sections.
3663// If found they are stored in their class or in the global list.
3664
3665static void buildVarList(const Entry *root)
3666{
3667 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3668 int isFuncPtr=-1;
3669 if (!root->name.empty() &&
3670 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3671 (
3672 (root->section.isVariable() && // it's a variable
3673 root->type.find("typedef ")==DString::npos // and not a typedef
3674 ) ||
3675 (root->section.isFunction() && // or maybe a function pointer variable
3676 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3677 ) ||
3678 (root->section.isFunction() && // class variable initialized by constructor
3680 )
3681 )
3682 ) // documented variable
3683 {
3684 AUTO_TRACE();
3685 addVariable(root,isFuncPtr);
3686 }
3687 for (const auto &e : root->children())
3688 if (!e->section.isEnum())
3689 buildVarList(e.get());
3690}
3691
3692//----------------------------------------------------------------------
3693// Searches the Entry tree for Interface sections (UNO IDL only).
3694// If found they are stored in their service or in the global list.
3695//
3696
3698 const Entry *root,
3699 ClassDefMutable *cd,
3700 DString const& rname)
3701{
3702 FileDef *fd = root->fileDef();
3703 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3704 DString fileName = root->fileName;
3705 if (fileName.empty() && root->tagInfo())
3706 {
3707 fileName = root->tagInfo()->tagName;
3708 }
3709 auto md = createMemberDef(
3710 fileName, root->startLine, root->startColumn, root->type, rname,
3711 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3712 type, ArgumentList(), root->argList, root->metaData);
3713 auto mmd = toMemberDefMutable(md.get());
3714 mmd->setTagInfo(root->tagInfo());
3715 mmd->setMemberClass(cd);
3716 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3717 mmd->setDocsForDefinition(false);
3718 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3719 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3720 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3721 mmd->setMemberSpecifiers(root->spec);
3722 mmd->setVhdlSpecifiers(root->vhdlSpec);
3723 mmd->setMemberGroupId(root->mGrpId);
3724 mmd->setTypeConstraints(root->typeConstr);
3725 mmd->setLanguage(root->lang);
3726 mmd->setBodyDef(fd);
3727 mmd->setFileDef(fd);
3728 mmd->addSectionsToDefinition(root->anchors);
3729 DString const def = root->type + " " + rname;
3730 mmd->setDefinition(def);
3732 mmd->addQualifiers(root->qualifiers);
3733 addIncludeFile(mmd,fd,root);
3734
3735 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3736 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3737
3738 // add member to the class cd
3739 cd->insertMember(md.get());
3740 // also add the member as a "base" (to get nicer diagrams)
3741 // "optional" interface/service get Protected which turns into dashed line
3742 BaseInfo base(rname,
3743 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3744 TemplateNameMap templateNames;
3745 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3746 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3747 // add file to list of used files
3748 cd->insertUsedFile(fd);
3749
3750 addMemberToGroups(root,md.get());
3752 root->markAsProcessed();
3753 mmd->setRefItems(root->sli);
3754 mmd->setRequirementReferences(root->rqli);
3755
3756 // add member to the global list of all members
3758 mn->push_back(std::move(md));
3759}
3760
3761static void buildInterfaceAndServiceList(const Entry *root)
3762{
3763 if (root->section.isExportedInterface() || root->section.isIncludedService())
3764 {
3765 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3766 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3767 " mGrpId={} spec={} proto={} docFile='{}'",
3768 root->type, root->parent()->name, root->name, root->args,
3769 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3770 root->mGrpId, root->spec, root->proto, root->docFile);
3771
3772 DString rname = removeRedundantWhiteSpace(root->name);
3773
3774 if (!rname.empty())
3775 {
3776 DString scope = root->parent()->name;
3777 ClassDefMutable *cd = getClassMutable(scope);
3778 ASSERT(cd);
3779 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3780 (ClassDef::Service == cd->compoundType()) ||
3782 {
3784 }
3785 else
3786 {
3787 ASSERT(false); // was checked by scanner.l
3788 }
3789 }
3790 else if (rname.empty())
3791 {
3792 warn(root->fileName,root->startLine,
3793 "Illegal member name found.");
3794 }
3795 }
3796 // can only have these in IDL anyway
3797 switch (root->lang)
3798 {
3799 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3800 case SrcLangExt::IDL:
3801 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3802 break;
3803 default:
3804 return; // nothing to do here
3805 }
3806}
3807
3808
3809//----------------------------------------------------------------------
3810// Searches the Entry tree for Function sections.
3811// If found they are stored in their class or in the global list.
3812
3813static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3814 const DString &rtype,const DString &rname,const DString &rargs,
3815 bool isFriend,
3816 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3817 const DString &relates
3818 )
3819{
3820 FileDef *fd=root->fileDef();
3821
3822 DString type = rtype;
3823 DString args = rargs;
3824
3826 name.stripPrefix("::");
3827
3828 MemberType mtype = MemberType::Function;
3829 if (isFriend) mtype=MemberType::Friend;
3830 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3831 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3832 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3833
3834 // strip redundant template specifier for constructors
3835 size_t i = DString::npos;
3836 size_t j = DString::npos;
3837 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3838 !name.startsWith("operator ") && // not operator
3839 (i=name.find('<'))!=DString::npos && // containing <
3840 (j=name.find('>'))!=DString::npos && // or >
3841 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3842 )
3843 {
3844 name=name.left(i);
3845 }
3846
3847 DString fileName = root->fileName;
3848 if (fileName.empty() && root->tagInfo())
3849 {
3850 fileName = root->tagInfo()->tagName;
3851 }
3852
3853 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3854 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3855 // );
3856
3857 // adding class member
3858 Relationship relationship = relates.empty() ? Relationship::Member :
3859 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3860 Relationship::Related ;
3861 auto md = createMemberDef(
3862 fileName,root->startLine,root->startColumn,
3863 type,name,args,root->exception,
3864 protection,virt,
3865 stat && root->relatesType!=RelatesType::MemberOf,
3866 relationship,
3867 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3868 root->argList, root->metaData);
3869 auto mmd = toMemberDefMutable(md.get());
3870 mmd->setTagInfo(root->tagInfo());
3871 mmd->setMemberClass(cd);
3872 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3873 mmd->setDocsForDefinition(!root->proto);
3874 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3875 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3876 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3877 mmd->setMemberSpecifiers(spec);
3878 mmd->setVhdlSpecifiers(root->vhdlSpec);
3879 mmd->setMemberGroupId(root->mGrpId);
3880 mmd->setTypeConstraints(root->typeConstr);
3881 mmd->setLanguage(root->lang);
3882 mmd->setRequiresClause(root->req);
3883 mmd->setId(root->id);
3884 mmd->setBodyDef(fd);
3885 mmd->setFileDef(fd);
3886 mmd->addSectionsToDefinition(root->anchors);
3887 addIncludeFile(mmd,fd,root);
3888 DString def;
3890 SrcLangExt lang = cd->getLanguage();
3891 DString scopeSeparator=getLanguageSpecificSeparator(lang);
3892 if (scopeSeparator!="::")
3893 {
3894 qualScope = substitute(qualScope,"::",scopeSeparator);
3895 }
3896 if (lang==SrcLangExt::PHP)
3897 {
3898 // for PHP we use Class::method and Namespace\method
3899 scopeSeparator="::";
3900 }
3901 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3902 {
3903 if (!type.empty())
3904 {
3905 def=type+" "+name; //+optArgs;
3906 }
3907 else
3908 {
3909 def=name; //+optArgs;
3910 }
3911 }
3912 else
3913 {
3914 if (!type.empty())
3915 {
3916 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3917 }
3918 else
3919 {
3920 def=qualScope+scopeSeparator+name; //+optArgs;
3921 }
3922 }
3923 def.stripPrefix("friend ");
3924 mmd->setDefinition(def);
3926 mmd->addQualifiers(root->qualifiers);
3927
3928 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3929 type, qualScope, rname, args, root->proto, def);
3930
3931 // add member to the class cd
3932 cd->insertMember(md.get());
3933 // add file to list of used files
3934 cd->insertUsedFile(fd);
3935
3936 addMemberToGroups(root,md.get());
3938 root->markAsProcessed();
3939 mmd->setRefItems(root->sli);
3940 mmd->setRequirementReferences(root->rqli);
3941
3942 // add member to the global list of all members
3943 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3945 mn->push_back(std::move(md));
3946}
3947
3948//------------------------------------------------------------------------------------------
3949
3950static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
3951{
3952 DString scope = sc;
3953
3954 // new global function
3956 auto md = createMemberDef(
3957 root->fileName,root->startLine,root->startColumn,
3958 root->type,name,root->args,root->exception,
3959 root->protection,root->virt,root->isStatic,Relationship::Member,
3960 MemberType::Function,
3961 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3962 root->argList,root->metaData);
3963 auto mmd = toMemberDefMutable(md.get());
3964 mmd->setTagInfo(root->tagInfo());
3965 mmd->setLanguage(root->lang);
3966 mmd->setId(root->id);
3967 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3968 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3969 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3970 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3971 mmd->setDocsForDefinition(!root->proto);
3972 mmd->setTypeConstraints(root->typeConstr);
3973 //md->setBody(root->body);
3974 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3975 FileDef *fd=root->fileDef();
3976 mmd->setBodyDef(fd);
3977 mmd->addSectionsToDefinition(root->anchors);
3978 mmd->setMemberSpecifiers(root->spec);
3979 mmd->setVhdlSpecifiers(root->vhdlSpec);
3980 mmd->setMemberGroupId(root->mGrpId);
3981 mmd->setRequiresClause(root->req);
3982 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3983 addIncludeFile(mmd,fd,root);
3984
3985 NamespaceDefMutable *nd = nullptr;
3986 // see if the function is inside a namespace that was not part of
3987 // the name already (in that case nd should be non-zero already)
3988 if (root->parent()->section.isNamespace())
3989 {
3990 //DString nscope=removeAnonymousScopes(root->parent()->name);
3991 DString nscope=root->parent()->name;
3992 if (!nscope.empty())
3993 {
3994 nd = getResolvedNamespaceMutable(nscope);
3995 }
3996 }
3997 else if (root->parent()->section.isGroupDoc() && !scope.empty())
3998 {
4000 }
4001
4002 if (!scope.empty())
4003 {
4005 if (sep!="::")
4006 {
4007 scope = substitute(scope,"::",sep);
4008 }
4009 scope+=sep;
4010 }
4011
4012 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
4013 DString def;
4014 //DString optArgs = root->argList.empty() ? DString() : root->args;
4015 if (!root->type.empty())
4016 {
4017 def=root->type+" "+scope+name; //+optArgs;
4018 }
4019 else
4020 {
4021 def=scope+name; //+optArgs;
4022 }
4023 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4024 root->type,scope,rname,root->args,root->proto,def);
4025 mmd->setDefinition(def);
4027 mmd->addQualifiers(root->qualifiers);
4028
4029 mmd->setRefItems(root->sli);
4030 mmd->setRequirementReferences(root->rqli);
4031 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
4032 {
4033 // add member to namespace
4034 mmd->setNamespace(nd);
4035 nd->insertMember(md.get());
4036 }
4037 if (fd)
4038 {
4039 // add member to the file (we do this even if we have already
4040 // inserted it into the namespace)
4041 mmd->setFileDef(fd);
4042 fd->insertMember(md.get());
4043 }
4044
4045 addMemberToGroups(root,md.get());
4047 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4048 // allow find Member to pick it up
4049 {
4050 root->markAsProcessed(); // Otherwise we have finished with this entry.
4051 }
4052
4053 // add member to the list of file members
4055 mn->push_back(std::move(md));
4056}
4057
4058//------------------------------------------------------------------------------------------
4059
4060static void buildFunctionList(const Entry *root)
4061{
4062 if (root->section.isFunction())
4063 {
4064 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4065 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4066 " spec={} proto={} docFile='{}'",
4067 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4068 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4069 root->spec, root->proto, root->docFile);
4070
4071 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4072 DString rname = removeRedundantWhiteSpace(root->name);
4073 //printf("rname=%s\n",qPrint(rname));
4074
4075 DString scope;
4076 int index = computeQualifiedIndex(rname);
4077 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4078 // grouped members are stored with full scope
4079 {
4080 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4081 scope=rname.left(index);
4082 rname=rname.mid(index+2);
4083 }
4084 else
4085 {
4086 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4087 }
4088 if (!rname.empty() && scope.find('@')==DString::npos)
4089 {
4090 // check if this function's parent is a class
4091 if (root->lang==SrcLangExt::CSharp)
4092 {
4093 scope=mangleCSharpGenericName(scope);
4094 }
4095 else
4096 {
4097 scope=stripTemplateSpecifiersFromScope(scope,false);
4098 }
4099
4100 FileDef *rfd=root->fileDef();
4101
4102 size_t memIndex=rname.rfind("::");
4103
4105 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4106 {
4107 // strip scope from name
4108 rname=rname.mid(root->parent()->name.length()+2);
4109 }
4110
4111 bool isMember=false;
4112 if (memIndex!=DString::npos)
4113 {
4114 size_t ts=rname.find('<');
4115 size_t te=rname.find('>');
4116 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4117 {
4118 // note: the following code was replaced by inMember=true to deal with a
4119 // function rname='X::foo' of class X inside a namespace also called X...
4120 // bug id 548175
4121 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4122 //isMember = nd==nullptr;
4123 //if (nd)
4124 //{
4125 // // strip namespace scope from name
4126 // scope=rname.left(memIndex);
4127 // rname=rname.mid(memIndex+2);
4128 //}
4129 isMember = true;
4130 }
4131 else
4132 {
4133 isMember=memIndex<ts || memIndex>te;
4134 }
4135 }
4136
4137 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4138 {
4139 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4140 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4141 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4142 }
4143 else if (root->parent()->section.isObjcImpl() && cd)
4144 {
4145 const MemberDef *md = cd->getMemberByName(rname);
4146 if (md)
4147 {
4148 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4149 if (mdm)
4150 {
4151 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4152 mdm->setBodyDef(root->fileDef());
4153 }
4154 }
4155 }
4156 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4157 !isMember &&
4158 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4159 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4160 )
4161 // no member => unrelated function
4162 {
4163 /* check the uniqueness of the function name in the file.
4164 * A file could contain a function prototype and a function definition
4165 * or even multiple function prototypes.
4166 */
4167 bool found=false;
4168 MemberDef *md_found=nullptr;
4170 if (mn)
4171 {
4172 AUTO_TRACE_ADD("function '{}' already found",rname);
4173 for (const auto &imd : *mn)
4174 {
4175 MemberDefMutable *md = toMemberDefMutable(imd.get());
4176 if (md)
4177 {
4178 const NamespaceDef *mnd = md->getNamespaceDef();
4179 NamespaceDef *rnd = nullptr;
4180 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4181 DString fullScope = scope;
4182 DString parentScope = root->parent()->name;
4183 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4184 {
4185 if (!scope.empty()) fullScope.prepend("::");
4186 fullScope.prepend(parentScope);
4187 }
4188 //printf("fullScope=%s\n",qPrint(fullScope));
4189 rnd = getResolvedNamespace(fullScope);
4190 const FileDef *mfd = md->getFileDef();
4191 DString nsName,rnsName;
4192 if (mnd) nsName = mnd->name();
4193 if (rnd) rnsName = rnd->name();
4194 //printf("matching arguments for %s%s %s%s\n",
4195 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4196 const ArgumentList &mdAl = md->argumentList();
4197 const ArgumentList &mdTempl = md->templateArguments();
4198
4199 // in case of template functions, we need to check if the
4200 // functions have the same number of template parameters
4201 bool sameTemplateArgs = true;
4202 bool matchingReturnTypes = true;
4203 bool sameRequiresClause = true;
4204 if (!mdTempl.empty() && !root->tArgLists.empty())
4205 {
4206 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4207 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4208 {
4209 matchingReturnTypes = false;
4210 }
4211 if (md->requiresClause()!=root->req)
4212 {
4213 sameRequiresClause = false;
4214 }
4215 }
4216 else if (!mdTempl.empty() || !root->tArgLists.empty())
4217 { // if one has template parameters and the other doesn't then that also counts as a
4218 // difference
4219 sameTemplateArgs = false;
4220 }
4221
4222 bool staticsInDifferentFiles =
4223 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4224
4225 if (sameTemplateArgs &&
4226 matchingReturnTypes &&
4227 sameRequiresClause &&
4228 !staticsInDifferentFiles &&
4229 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4230 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4231 false,root->lang)
4232 )
4233 {
4234 GroupDef *gd=nullptr;
4235 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4236 {
4237 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4238 }
4239 //printf("match!\n");
4240 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4241 // see if we need to create a new member
4242 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4243 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4244 mfd->absFilePath()==root->fileName // prototype in the same file
4245 )
4246 );
4247 // otherwise, allow a duplicate global member with the same argument list
4248 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4249 {
4250 // member is already in the group, so we don't want to add it again.
4251 found=true;
4252 }
4253
4254 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4255
4256 if (found)
4257 {
4258 // merge argument lists
4259 ArgumentList mergedArgList = root->argList;
4260 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4261 // merge documentation
4262 if (md->documentation().empty() && !root->doc.empty())
4263 {
4264 if (root->proto)
4265 {
4267 }
4268 else
4269 {
4271 }
4272 }
4273
4274 md->setDocumentation(root->doc,root->docFile,root->docLine);
4276 md->setDocsForDefinition(!root->proto);
4277 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4278 {
4279 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4280 md->setBodyDef(rfd);
4281 }
4282
4283 if (md->briefDescription().empty() && !root->brief.empty())
4284 {
4285 md->setArgsString(root->args);
4286 }
4287 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4288
4290
4292 md->addQualifiers(root->qualifiers);
4293
4294 // merge ingroup specifiers
4295 if (md->getGroupDef()==nullptr && !root->groups.empty())
4296 {
4297 addMemberToGroups(root,md);
4298 }
4299 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4300 {
4301 //printf("existing member is grouped, new member not\n");
4302 }
4303 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4304 {
4305 //printf("both members are grouped\n");
4306 }
4308
4309 // if md is a declaration and root is the corresponding
4310 // definition, then turn md into a definition.
4311 if (md->isPrototype() && !root->proto)
4312 {
4313 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4314 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4315 }
4316 // if md is already the definition, then add the declaration info
4317 else if (!md->isPrototype() && root->proto)
4318 {
4319 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4320 }
4321 }
4322 }
4323 }
4324 if (found)
4325 {
4326 md_found = md;
4327 break;
4328 }
4329 }
4330 }
4331 if (!found) /* global function is unique with respect to the file */
4332 {
4333 addGlobalFunction(root,rname,scope);
4334 }
4335 else
4336 {
4337 FileDef *fd=root->fileDef();
4338 if (fd)
4339 {
4340 // add member to the file (we do this even if we have already
4341 // inserted it into the namespace)
4342 fd->insertMember(md_found);
4343 }
4344 }
4345
4346 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4347 }
4348 else
4349 {
4350 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4351 }
4352 }
4353 else if (rname.empty())
4354 {
4355 warn(root->fileName,root->startLine,
4356 "Illegal member name found."
4357 );
4358 }
4359 }
4360 for (const auto &e : root->children()) buildFunctionList(e.get());
4361}
4362
4363//----------------------------------------------------------------------
4364
4365static void findFriends()
4366{
4367 AUTO_TRACE();
4368 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4369 {
4370 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4371 if (mn)
4372 { // there are members with the same name
4373 // for each function with that name
4374 for (const auto &ifmd : *fn)
4375 {
4376 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4377 // for each member with that name
4378 for (const auto &immd : *mn)
4379 {
4380 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4381 //printf("Checking for matching arguments
4382 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4383 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4384 if (fmd && mmd &&
4385 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4386 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4387 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4388 true,mmd->getLanguage()
4389 )
4390
4391 ) // if the member is related and the arguments match then the
4392 // function is actually a friend.
4393 {
4394 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4395 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4396 const ArgumentList &mmdAl = mmd->argumentList();
4397 const ArgumentList &fmdAl = fmd->argumentList();
4398 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4399
4400 // reset argument lists to add missing default parameters
4401 DString mmdAlStr = argListToString(mmdAl);
4402 DString fmdAlStr = argListToString(fmdAl);
4403 mmd->setArgsString(mmdAlStr);
4404 fmd->setArgsString(fmdAlStr);
4405 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4406 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4407 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4408
4409 if (!fmd->documentation().empty())
4410 {
4411 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4412 }
4413 else if (!mmd->documentation().empty())
4414 {
4415 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4416 }
4417 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4418 {
4419 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4420 }
4421 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4422 {
4423 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4424 }
4425 if (!fmd->inbodyDocumentation().empty())
4426 {
4428 }
4429 else if (!mmd->inbodyDocumentation().empty())
4430 {
4432 }
4433 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4434 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4435 {
4436 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4437 mmd->setBodyDef(fmd->getBodyDef());
4438 //mmd->setBodyMember(fmd);
4439 }
4440 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4441 {
4442 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4443 fmd->setBodyDef(mmd->getBodyDef());
4444 //fmd->setBodyMember(mmd);
4445 }
4447
4449
4450 mmd->addQualifiers(fmd->getQualifiers());
4451 fmd->addQualifiers(mmd->getQualifiers());
4452
4453 }
4454 }
4455 }
4456 }
4457 }
4458}
4459
4460//----------------------------------------------------------------------
4461
4463{
4464 AUTO_TRACE();
4465
4466 // find matching function declaration and definitions.
4467 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4468 {
4469 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4470 /* find a matching function declaration and definition for this function */
4471 for (const auto &imdec : *mn)
4472 {
4473 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4474 if (mdec &&
4475 (mdec->isPrototype() ||
4476 (mdec->isVariable() && mdec->isExternal())
4477 ))
4478 {
4479 for (const auto &imdef : *mn)
4480 {
4481 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4482 if (mdef && mdec!=mdef &&
4483 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4484 {
4486 }
4487 }
4488 }
4489 }
4490 }
4491}
4492
4493//----------------------------------------------------------------------
4494
4496{
4497 AUTO_TRACE();
4498 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4499 {
4500 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4501 /* find a matching function declaration and definition for this function */
4502 for (const auto &imd : *mn)
4503 {
4504 MemberDefMutable *md = toMemberDefMutable(imd.get());
4505 if (md)
4506 {
4507 if (md->isPrototype())
4508 mdec=md;
4509 else if (md->isVariable() && md->isExternal())
4510 mdec=md;
4511
4512 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4513 mdef=md;
4514 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4515 mdef=md;
4516 }
4517
4518 if (mdef && mdec) break;
4519 }
4520 if (mdef && mdec)
4521 {
4522 const ArgumentList &mdefAl = mdef->argumentList();
4523 const ArgumentList &mdecAl = mdec->argumentList();
4524 if (
4525 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4526 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4527 true,mdef->getLanguage()
4528 )
4529 ) /* match found */
4530 {
4531 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4532 mdef->mergeReferences(mdec);
4533 mdec->mergeReferences(mdef);
4534 mdef->mergeReferencedBy(mdec);
4535 mdec->mergeReferencedBy(mdef);
4536 }
4537 }
4538 }
4539}
4540
4541//----------------------------------------------------------------------
4542
4544{
4545 AUTO_TRACE();
4546 // find match between function declaration and definition for
4547 // related functions
4548 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4549 {
4550 /* find a matching function declaration and definition for this function */
4551 // for each global function
4552 for (const auto &imd : *mn)
4553 {
4554 MemberDefMutable *md = toMemberDefMutable(imd.get());
4555 if (md)
4556 {
4557 //printf(" Function '%s'\n",qPrint(md->name()));
4559 if (rmn) // check if there is a member with the same name
4560 {
4561 //printf(" Member name found\n");
4562 // for each member with the same name
4563 for (const auto &irmd : *rmn)
4564 {
4565 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4566 //printf(" Member found: related='%d'\n",rmd->isRelated());
4567 if (rmd &&
4568 (rmd->isRelated() || rmd->isForeign()) && // related function
4569 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4570 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4571 true,md->getLanguage()
4572 )
4573 )
4574 {
4575 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4576 if (rmd->relatedAlso())
4577 md->setRelatedAlso(rmd->relatedAlso());
4578 else if (rmd->isForeign())
4579 md->makeForeign();
4580 else
4581 md->makeRelated();
4582 }
4583 }
4584 }
4585 }
4586 }
4587 }
4588}
4589
4590//----------------------------------------------------------------------
4591
4593{
4594 AUTO_TRACE();
4595 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4596 {
4597 size_t i=qualifiedName.rfind("::");
4598 if (i!=std::string::npos)
4599 {
4600 DString scope = qualifiedName.substr(0,i);
4601 DString name = qualifiedName.substr(i+2);
4603 if (mn)
4604 {
4605 for (const auto &imd : *mn)
4606 {
4607 MemberDefMutable *md = toMemberDefMutable(imd.get());
4608 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4609 {
4610 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4611 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4612 md->setBodySegment(bodyInfo.defLine,
4613 bodyInfo.startLine,
4614 bodyInfo.endLine);
4615 }
4616 }
4617 }
4618 }
4619 }
4620}
4621
4622//----------------------------------------------------------------------
4623
4624/*! make a dictionary of all template arguments of class cd
4625 * that are part of the base class name.
4626 * Example: A template class A with template arguments <R,S,T>
4627 * that inherits from B<T,T,S> will have T and S in the dictionary.
4628 */
4629static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4630{
4631 std::map<std::string,int> templateNames;
4632 int count=0;
4633 for (const Argument &arg : templateArguments)
4634 {
4635 static const reg::Ex re(R"(\a[\w:]*)");
4636 reg::Iterator it(name,re);
4638 for (; it!=end ; ++it)
4639 {
4640 const auto &match = *it;
4641 std::string n = match.str();
4642 if (n==arg.name.str())
4643 {
4644 if (templateNames.find(n)==templateNames.end())
4645 {
4646 templateNames.emplace(n,count);
4647 }
4648 }
4649 }
4650 }
4651 return templateNames;
4652}
4653
4654/*! Searches a class from within \a context and \a cd and returns its
4655 * definition if found (otherwise nullptr is returned).
4656 */
4658{
4659 ClassDef *result=nullptr;
4660 if (cd==nullptr)
4661 {
4662 return result;
4663 }
4664 FileDef *fd=cd->getFileDef();
4665 SymbolResolver resolver(fd);
4666 if (context && cd!=context)
4667 {
4668 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4669 }
4670 //printf("1. result=%p\n",result);
4671 if (result==nullptr)
4672 {
4673 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4674 }
4675 //printf("2. result=%p\n",result);
4676 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4677 {
4678 result = getClass(name);
4679 }
4680 //printf("3. result=%p\n",result);
4681 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4682 // qPrint(name),
4683 // context ? qPrint(context->name()) : "<none>",
4684 // cd ? qPrint(cd->name()) : "<none>",
4685 // result ? qPrint(result->name()) : "<none>",
4686 // Doxygen::classLinkedMap->find(name)
4687 // );
4688 return result;
4689}
4690
4691
4692static void findUsedClassesForClass(const Entry *root,
4693 Definition *context,
4694 ClassDefMutable *masterCd,
4695 ClassDefMutable *instanceCd,
4696 bool isArtificial,
4697 const ArgumentList *actualArgs = nullptr,
4698 const TemplateNameMap &templateNames = TemplateNameMap()
4699 )
4700{
4701 AUTO_TRACE();
4702 const ArgumentList &formalArgs = masterCd->templateArguments();
4703 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4704 {
4705 for (auto &mi : *mni)
4706 {
4707 const MemberDef *md=mi->memberDef();
4708 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4709 {
4710 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4711 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4712 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4713 if (!typedefValue.empty())
4714 {
4715 type = typedefValue;
4716 }
4717 int pos=0;
4718 DString usedClassName;
4719 DString templSpec;
4720 bool found=false;
4721 // the type can contain template variables, replace them if present
4722 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4723
4724 //printf(" template substitution gives=%s\n",qPrint(type));
4725 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4726 {
4727 // find the type (if any) that matches usedClassName
4728 SymbolResolver resolver(masterCd->getFileDef());
4729 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4730 //printf("====> usedClassName=%s -> typeCd=%s\n",
4731 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4732 if (typeCd)
4733 {
4734 usedClassName = typeCd->name();
4735 }
4736
4737 // replace any namespace aliases
4738 replaceNamespaceAliases(usedClassName);
4739 // add any template arguments to the class
4740 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4741 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4742
4743 TemplateNameMap formTemplateNames;
4744 if (templateNames.empty())
4745 {
4746 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4747 }
4748 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4749 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4750
4751 for (const Argument &arg : masterCd->templateArguments())
4752 {
4753 if (arg.name==usedName) // type is a template argument
4754 {
4755 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4756 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4757 if (usedCd==nullptr)
4758 {
4759 usedCdm = toClassDefMutable(
4760 Doxygen::hiddenClassLinkedMap->add(usedName,
4762 masterCd->getDefFileName(),masterCd->getDefLine(),
4763 masterCd->getDefColumn(),
4764 usedName,
4765 ClassDef::Class)));
4766 if (usedCdm)
4767 {
4768 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4769 usedCdm->makeTemplateArgument();
4770 usedCdm->setUsedOnly(true);
4771 usedCdm->setLanguage(masterCd->getLanguage());
4772 usedCd = usedCdm;
4773 }
4774 }
4775 if (usedCd)
4776 {
4777 found=true;
4778 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4779 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4780 if (usedCdm)
4781 {
4782 if (isArtificial) usedCdm->setArtificial(true);
4783 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4784 }
4785 }
4786 }
4787 }
4788
4789 if (!found)
4790 {
4791 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4792 //printf("Looking for used class %s: result=%s master=%s\n",
4793 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4794
4795 if (usedCd)
4796 {
4797 found=true;
4798 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4799 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4800 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4801 if (usedCdm)
4802 {
4803 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4804 }
4805 }
4806 }
4807 }
4808 if (!found && !type.empty()) // used class is not documented in any scope
4809 {
4811 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4812 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4813 {
4814 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4815 {
4816 type+=md->argsString();
4817 }
4818 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4819 usedCdm = toClassDefMutable(
4822 masterCd->getDefFileName(),masterCd->getDefLine(),
4823 masterCd->getDefColumn(),
4824 type,ClassDef::Class)));
4825 if (usedCdm)
4826 {
4827 usedCdm->setUsedOnly(true);
4828 usedCdm->setLanguage(masterCd->getLanguage());
4829 usedCd = usedCdm;
4830 }
4831 }
4832 if (usedCd)
4833 {
4834 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4835 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4836 if (usedCdm)
4837 {
4838 if (isArtificial) usedCdm->setArtificial(true);
4839 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4840 }
4841 }
4842 }
4843 }
4844 }
4845 }
4846}
4847
4849 const Entry *root,
4850 Definition *context,
4851 ClassDefMutable *masterCd,
4852 ClassDefMutable *instanceCd,
4854 bool isArtificial,
4855 const ArgumentList *actualArgs = nullptr,
4856 const TemplateNameMap &templateNames=TemplateNameMap()
4857 )
4858{
4859 AUTO_TRACE("name={}",root->name);
4860 // The base class could ofcouse also be a non-nested class
4861 const ArgumentList &formalArgs = masterCd->templateArguments();
4862 for (const BaseInfo &bi : root->extends)
4863 {
4864 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4865 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4866 TemplateNameMap formTemplateNames;
4867 if (templateNames.empty())
4868 {
4869 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4870 }
4871 BaseInfo tbi = bi;
4872 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4873 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4874
4875 if (mode==DocumentedOnly)
4876 {
4877 // find a documented base class in the correct scope
4878 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4879 {
4880 // 1.8.2: decided to show inheritance relations even if not documented,
4881 // we do make them artificial, so they do not appear in the index
4882 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4883 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
4884 //{
4885 // no documented base class -> try to find an undocumented one
4886 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4887 //}
4888 }
4889 }
4890 else if (mode==TemplateInstances)
4891 {
4892 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4893 }
4894 }
4895}
4896
4897//----------------------------------------------------------------------
4898
4899static void findTemplateInstanceRelation(const Entry *root,
4900 Definition *context,
4901 ClassDefMutable *templateClass,const DString &templSpec,
4902 const TemplateNameMap &templateNames,
4903 bool isArtificial)
4904{
4905 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4906 templateClass->name(),templSpec,isArtificial);
4907
4908 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4909 bool existingClass = templSpec==tempArgsStr;
4910 if (existingClass) return; // avoid recursion
4911
4912 bool freshInstance=false;
4913 ClassDefMutable *instanceClass = toClassDefMutable(
4914 templateClass->insertTemplateInstance(
4915 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4916 if (instanceClass)
4917 {
4918 if (freshInstance)
4919 {
4920 instanceClass->setArtificial(true);
4921 instanceClass->setLanguage(root->lang);
4922
4923 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4924 instanceClass->setTemplateBaseClassNames(templateNames);
4925
4926 // search for new template instances caused by base classes of
4927 // instanceClass
4928 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4929 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4930 {
4931 const Entry *templateRoot = it->second;
4932 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4933 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4934 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4935 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4936
4937 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4938 isArtificial,templArgs.get(),templateNames);
4939 }
4940 }
4941 else
4942 {
4943 AUTO_TRACE_ADD("instance already exists");
4944 }
4945 }
4946}
4947
4948//----------------------------------------------------------------------
4949
4950static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4951{
4952 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4953 // be used to match arguments (see issue #11111)
4954 AUTO_TRACE();
4955 DString ttype = md->typeString();
4956 ttype.stripPrefix("typedef ");
4957 if (size_t ti=ttype.find('<'); ti!=DString::npos)
4958 {
4959 DString templateClassName = ttype.left(ti);
4960 SymbolResolver resolver(root->fileDef());
4961 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4962 templateClassName, true, true);
4963 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4964 if (baseClass)
4965 {
4966 const ArgumentList &tl = baseClass->templateArguments();
4967 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4969 baseClass,
4970 ttype.mid(ti),
4971 templateNames,
4972 baseClass->isArtificial());
4973 }
4974 }
4975}
4976
4977//----------------------------------------------------------------------
4978
4979static bool isRecursiveBaseClass(const DString &scope,const DString &name)
4980{
4981 DString n=name;
4982 if (size_t index=n.find('<'); index!=DString::npos)
4983 {
4984 n=n.left(index);
4985 }
4986 bool result = rightScopeMatch(scope,n);
4987 return result;
4988}
4989
4991{
4992 if (name.empty()) return 0;
4993 int l = static_cast<int>(name.length());
4994 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4995 {
4996 int count=1;
4997 int i=l-2;
4998 char insideQuote=0;
4999 while (count>0 && i>=0)
5000 {
5001 char c = name[i--];
5002 switch (c)
5003 {
5004 case '>': if (!insideQuote) count++; break;
5005 case '<': if (!insideQuote) count--; break;
5006 case '\'': if (!insideQuote) insideQuote=c;
5007 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5008 break;
5009 case '"': if (!insideQuote) insideQuote=c;
5010 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5011 break;
5012 default: break;
5013 }
5014 }
5015 if (i>=0) l=i+1;
5016 }
5017 return l;
5018}
5019
5021 const Entry *root,
5022 Definition *context,
5023 ClassDefMutable *cd,
5024 const BaseInfo *bi,
5025 const TemplateNameMap &templateNames,
5027 bool isArtificial
5028 )
5029{
5030 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
5031
5032 DString biName=bi->name;
5033 bool explicitGlobalScope=false;
5034 if (biName.startsWith("::")) // explicit global scope
5035 {
5036 biName=biName.mid(2);
5037 explicitGlobalScope=true;
5038 }
5039
5040 Entry *parentNode=root->parent();
5041 bool lastParent=false;
5042 do // for each parent scope, starting with the largest scope
5043 // (in case of nested classes)
5044 {
5045 DString scopeName= parentNode ? parentNode->name : DString();
5046 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5047 do // try all parent scope prefixes, starting with the largest scope
5048 {
5049 //printf("scopePrefix='%s' biName='%s'\n",
5050 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5051
5052 DString baseClassName=biName;
5053 if (scopeOffset>0)
5054 {
5055 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5056 }
5057 if (root->lang==SrcLangExt::CSharp)
5058 {
5059 baseClassName = mangleCSharpGenericName(baseClassName);
5060 }
5061 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5062 SymbolResolver resolver(cd->getFileDef());
5063 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5064 baseClassName,
5065 mode==Undocumented,
5066 true
5067 );
5068 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5069 DString templSpec = resolver.getTemplateSpec();
5070 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5071 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5072 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5073 // cd ? qPrint(cd->name()):"<none>",
5074 // qPrint(baseClassName),
5075 // baseClass?qPrint(baseClass->name()):"<none>",
5076 // qPrint(templSpec)
5077 // );
5078 //if (baseClassName.left(root->name.length())!=root->name ||
5079 // baseClassName.at(root->name.length())!='<'
5080 // ) // Check for base class with the same name.
5081 // // If found then look in the outer scope for a match
5082 // // and prevent recursion.
5083 if (!isRecursiveBaseClass(root->name,baseClassName)
5084 || explicitGlobalScope
5085 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5086 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5087 || (root->lang==SrcLangExt::IDL &&
5088 (root->section.isExportedInterface() ||
5089 root->section.isIncludedService()))
5090 )
5091 {
5092 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5093 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5094
5095 int i=findTemplateSpecializationPosition(baseClassName);
5096 size_t si=baseClassName.rfind("::",i);
5097 if (si==DString::npos) si=0;
5098 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5099 // base class has template specifiers
5100 {
5101 // TODO: here we should try to find the correct template specialization
5102 // but for now, we only look for the unspecialized base class.
5103 int e=findEndOfTemplate(baseClassName,i+1);
5104 //printf("baseClass==0 i=%d e=%d\n",i,e);
5105 if (e!=-1) // end of template was found at e
5106 {
5107 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5108 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5109 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5110 baseClassName,
5111 mode==Undocumented,
5112 true
5113 );
5114 baseClassTypeDef = resolver.getTypedef();
5115 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5116 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5117 }
5118 }
5119 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5120 // know it is a template, so see if
5121 // we can also link to the explicit
5122 // instance (for instance if a class
5123 // derived from a template argument)
5124 {
5125 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5126 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5127 if (templClass)
5128 {
5129 // use the template instance instead of the template base.
5130 baseClass = templClass;
5131 templSpec.clear();
5132 }
5133 }
5134
5135 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5136 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5137 AUTO_TRACE_ADD("1. found={}",found);
5138 if (!found && si!=DString::npos)
5139 {
5140 // replace any namespace aliases
5141 replaceNamespaceAliases(baseClassName);
5142 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5143 baseClassName,
5144 mode==Undocumented,
5145 true
5146 );
5147 baseClassTypeDef = resolver.getTypedef();
5148 found=baseClass!=nullptr && baseClass!=cd;
5149 if (found) templSpec = resolver.getTemplateSpec();
5150 }
5151 AUTO_TRACE_ADD("2. found={}",found);
5152
5153 if (!found)
5154 {
5155 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5156 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5157 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5158 found = baseClass!=nullptr && baseClass!=cd;
5159
5160 }
5161 AUTO_TRACE_ADD("3. found={}",found);
5162 if (!found)
5163 {
5164 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5165 // the class name also in the alias mapping.
5166 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5167 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5168 {
5169 baseClass=getClassMutable(it->second.alias);
5170 found = baseClass!=nullptr && baseClass!=cd;
5171 }
5172 }
5173 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5174
5175 AUTO_TRACE_ADD("4. found={}",found);
5176 if (found)
5177 {
5178 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5179 // add base class to this class
5180
5181 // if templSpec is not empty then we should "instantiate"
5182 // the template baseClass. A new ClassDef should be created
5183 // to represent the instance. To be able to add the (instantiated)
5184 // members and documentation of a template class
5185 // (inserted in that template class at a later stage),
5186 // the template should know about its instances.
5187 // the instantiation process, should be done in a recursive way,
5188 // since instantiating a template may introduce new inheritance
5189 // relations.
5190 if (!templSpec.empty() && mode==TemplateInstances)
5191 {
5192 // if baseClass is actually a typedef then we should not
5193 // instantiate it, since typedefs are in a different namespace
5194 // see bug531637 for an example where this would otherwise hang
5195 // Doxygen
5196 if (baseClassTypeDef==nullptr)
5197 {
5198 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5199 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5200 }
5201 }
5202 else if (mode==DocumentedOnly || mode==Undocumented)
5203 {
5204 //printf(" => insert base class\n");
5205 DString usedName;
5206 if (baseClassTypeDef)
5207 {
5208 usedName=biName;
5209 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5210 }
5211 Protection prot = bi->prot;
5212 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5213 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5214 {
5215 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5216 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5217 // add this class as super class to the base class
5218 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5219 }
5220 else
5221 {
5222 warn(root->fileName,root->startLine,
5223 "Detected potential recursive class relation "
5224 "between class {} and base class {}!",
5225 cd->name(),baseClass->name()
5226 );
5227 }
5228 }
5229 return true;
5230 }
5231 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5232 {
5233 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5234 biName,baseClassName,templSpec,isArtificial);
5235 baseClass=nullptr;
5236 if (isATemplateArgument)
5237 {
5238 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5239 if (baseClass==nullptr) // not found (or alias)
5240 {
5241 baseClass= toClassDefMutable(
5242 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5243 createClassDef(root->fileName,root->startLine,root->startColumn,
5244 baseClassName,
5245 ClassDef::Class)));
5246 if (baseClass) // really added (not alias)
5247 {
5248 if (isArtificial) baseClass->setArtificial(true);
5249 baseClass->setLanguage(root->lang);
5250 }
5251 }
5252 }
5253 else
5254 {
5255 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5256 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5257 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5258 if (baseClass==nullptr) // not found (or alias)
5259 {
5260 baseClass = toClassDefMutable(
5261 Doxygen::classLinkedMap->add(baseClassName,
5262 createClassDef(root->fileName,root->startLine,root->startColumn,
5263 baseClassName,
5264 ClassDef::Class)));
5265 if (baseClass) // really added (not alias)
5266 {
5267 if (isArtificial) baseClass->setArtificial(true);
5268 baseClass->setLanguage(root->lang);
5269 si = baseClassName.rfind("::");
5270 if (si!=DString::npos) // class is nested
5271 {
5272 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5273 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5274 {
5275 baseClass->setArtificial(true); // see bug678139
5276 }
5277 }
5278 }
5279 }
5280 }
5281 if (baseClass)
5282 {
5283 if (biName.endsWith("-p"))
5284 {
5285 biName="<"+biName.left(biName.length()-2)+">";
5286 }
5287 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5288 {
5289 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5290 // add base class to this class
5291 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5292 // add this class as super class to the base class
5293 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5294 }
5295 // the undocumented base was found in this file
5296 baseClass->insertUsedFile(root->fileDef());
5297
5298 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5299 if (scope!=baseClass)
5300 {
5301 baseClass->setOuterScope(scope);
5302 }
5303
5304 if (baseClassName.endsWith("-p"))
5305 {
5307 }
5308 return true;
5309 }
5310 else
5311 {
5312 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5313 }
5314 }
5315 else
5316 {
5317 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5318 }
5319 }
5320 else
5321 {
5322 if (mode!=TemplateInstances)
5323 {
5324 warn(root->fileName,root->startLine,
5325 "Detected potential recursive class relation "
5326 "between class {} and base class {}!",
5327 root->name,baseClassName
5328 );
5329 }
5330 // for mode==TemplateInstance this case is quite common and
5331 // indicates a relation between a template class and a template
5332 // instance with the same name.
5333 }
5334 if (scopeOffset==0)
5335 {
5336 scopeOffset=-1;
5337 }
5338 else
5339 {
5340 size_t o = scopeName.rfind("::",scopeOffset-1);
5341 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5342 }
5343 //printf("new scopeOffset='%d'",scopeOffset);
5344 } while (scopeOffset>=0);
5345
5346 if (parentNode==nullptr)
5347 {
5348 lastParent=true;
5349 }
5350 else
5351 {
5352 parentNode=parentNode->parent();
5353 }
5354 } while (lastParent);
5355
5356 return false;
5357}
5358
5359//----------------------------------------------------------------------
5360// Computes the base and super classes for each class in the tree
5361
5362static bool isClassSection(const Entry *root)
5363{
5364 if ( !root->name.empty() )
5365 {
5366 if (root->section.isCompound())
5367 // is it a compound (class, struct, union, interface ...)
5368 {
5369 return true;
5370 }
5371 else if (root->section.isCompoundDoc())
5372 // is it a documentation block with inheritance info.
5373 {
5374 bool hasExtends = !root->extends.empty();
5375 if (hasExtends) return true;
5376 }
5377 }
5378 return false;
5379}
5380
5381
5382/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5383 */
5384static void findClassEntries(const Entry *root)
5385{
5386 if (isClassSection(root))
5387 {
5388 g_classEntries.emplace(root->name.str(),root);
5389 }
5390 for (const auto &e : root->children()) findClassEntries(e.get());
5391}
5392
5393static DString extractClassName(const Entry *root)
5394{
5395 // strip any anonymous scopes first
5398 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5399 {
5400 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5401 // template part before looking for matches
5402 if (root->lang==SrcLangExt::CSharp)
5403 {
5404 bName = mangleCSharpGenericName(root->name);
5405 }
5406 else
5407 {
5408 bName = bName.left(i);
5409 }
5410 }
5411 return bName;
5412}
5413
5414/*! Using the dictionary build by findClassEntries(), this
5415 * function will look for additional template specialization that
5416 * exists as inheritance relations only. These instances will be
5417 * added to the template they are derived from.
5418 */
5420{
5421 AUTO_TRACE();
5422 ClassDefSet visitedClasses;
5423 for (const auto &[name,root] : g_classEntries)
5424 {
5425 DString bName = extractClassName(root);
5426 ClassDefMutable *cdm = getClassMutable(bName);
5427 if (cdm)
5428 {
5429 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5430 }
5431 }
5432}
5433
5435{
5436 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5437 size_t i = root->name.find('<');
5438 size_t j = root->name.rfind('>');
5439 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5440 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5441 {
5442 ClassDefMutable *master = getClassMutable(root->name.left(i));
5443 if (master && master!=cd && !cd->templateMaster())
5444 {
5445 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5446 cd->setTemplateMaster(master);
5447 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5448 }
5449 }
5450}
5451
5453{
5454 AUTO_TRACE();
5455 for (const auto &[name,root] : g_classEntries)
5456 {
5457 DString bName = extractClassName(root);
5458 ClassDefMutable *cdm = getClassMutable(bName);
5459 if (cdm)
5460 {
5461 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5463 cdm->addTypeConstraints();
5464 }
5465 }
5466}
5467
5469{
5470 AUTO_TRACE();
5471 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5472 {
5473 if (!nd->hasDocumentation())
5474 {
5475 if ((EntryType::guessSection(nd->getDefFileName()).isHeader() ||
5476 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5477 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5478 )
5479 {
5480 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5481 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5482 nd->name());
5483 }
5484 }
5485 }
5486}
5487
5489{
5490 AUTO_TRACE();
5491 for (const auto &[name,root] : g_classEntries)
5492 {
5493 DString bName = extractClassName(root);
5494 ClassDefMutable *cd = getClassMutable(bName);
5495 if (cd)
5496 {
5497 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5498 }
5499 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5500 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5501 {
5502 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5503 (EntryType::guessSection(root->fileName).isHeader() ||
5504 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5505 protectionLevelVisible(root->protection) && // hidden by protection
5506 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5507 )
5508 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5509 }
5510 }
5511}
5512
5514{
5515 AUTO_TRACE();
5516 for (const auto &[name,root] : g_classEntries)
5517 {
5521 // strip any anonymous scopes first
5522 if (cd && !cd->getTemplateInstances().empty())
5523 {
5524 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5525 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5526 {
5527 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5528 if (tcd)
5529 {
5530 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5531 DString templSpec = ti.templSpec;
5532 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5533 for (const BaseInfo &bi : root->extends)
5534 {
5535 // check if the base class is a template argument
5536 BaseInfo tbi = bi;
5537 const ArgumentList &tl = cd->templateArguments();
5538 if (!tl.empty())
5539 {
5540 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5541 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5542 // for each template name that we inherit from we need to
5543 // substitute the formal with the actual arguments
5544 TemplateNameMap actualTemplateNames;
5545 for (const auto &tn_kv : templateNames)
5546 {
5547 size_t templIndex = tn_kv.second;
5548 Argument actArg;
5549 bool hasActArg=false;
5550 if (templIndex<templArgs->size())
5551 {
5552 actArg=templArgs->at(templIndex);
5553 hasActArg=true;
5554 }
5555 if (hasActArg &&
5556 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5557 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5558 )
5559 {
5560 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5561 }
5562 }
5563
5564 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5565 // find a documented base class in the correct scope
5566 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5567 {
5568 // no documented base class -> try to find an undocumented one
5569 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5570 }
5571 }
5572 }
5573 }
5574 }
5575 }
5576 }
5577}
5578
5579//-----------------------------------------------------------------------
5580// compute the references (anchors in HTML) for each function in the file
5581
5583{
5584 AUTO_TRACE();
5585 for (const auto &cd : *Doxygen::classLinkedMap)
5586 {
5587 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5588 if (cdm)
5589 {
5590 cdm->computeAnchors();
5591 }
5592 }
5593 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5594 {
5595 for (const auto &fd : *fn)
5596 {
5597 fd->computeAnchors();
5598 }
5599 }
5600 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5601 {
5603 if (ndm)
5604 {
5605 ndm->computeAnchors();
5606 }
5607 }
5608 for (const auto &gd : *Doxygen::groupLinkedMap)
5609 {
5610 gd->computeAnchors();
5611 }
5612}
5613
5614//----------------------------------------------------------------------
5615
5616
5617template<typename Func>
5618static void applyToAllDefinitions(Func func)
5619{
5620 for (const auto &cd : *Doxygen::classLinkedMap)
5621 {
5622 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5623 if (cdm)
5624 {
5625 func(cdm);
5626 }
5627 }
5628
5629 for (const auto &cd : *Doxygen::conceptLinkedMap)
5630 {
5631 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5632 if (cdm)
5633 {
5634 func(cdm);
5635 }
5636 }
5637
5638 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5639 {
5640 for (const auto &fd : *fn)
5641 {
5642 func(fd.get());
5643 }
5644 }
5645
5646 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5647 {
5649 if (ndm)
5650 {
5651 func(ndm);
5652 }
5653 }
5654
5655 for (const auto &gd : *Doxygen::groupLinkedMap)
5656 {
5657 func(gd.get());
5658 }
5659
5660 for (const auto &pd : *Doxygen::pageLinkedMap)
5661 {
5662 func(pd.get());
5663 }
5664
5665 for (const auto &dd : *Doxygen::dirLinkedMap)
5666 {
5667 func(dd.get());
5668 }
5669
5670 func(&ModuleManager::instance());
5671}
5672
5673//----------------------------------------------------------------------
5674
5676{
5677 AUTO_TRACE();
5678 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5679}
5680
5681//----------------------------------------------------------------------
5682
5684{
5685 AUTO_TRACE();
5686 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5687}
5688
5689
5690//----------------------------------------------------------------------
5691
5693{
5694 AUTO_TRACE();
5696 {
5697 rl->generatePage();
5698 }
5699}
5700
5701//----------------------------------------------------------------------
5702// Copy the documentation in entry 'root' to member definition 'md' and
5703// set the function declaration of the member to 'funcDecl'. If the boolean
5704// over_load is set the standard overload text is added.
5705
5706static void addMemberDocs(const Entry *root,
5707 MemberDefMutable *md, const DString &funcDecl,
5708 const ArgumentList *al,
5709 bool over_load,
5710 TypeSpecifier spec
5711 )
5712{
5713 if (md==nullptr) return;
5714 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5715 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5716 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5717 {
5718 DString fDecl=funcDecl;
5719 // strip extern specifier
5720 fDecl.stripPrefix("extern ");
5721 md->setDefinition(fDecl);
5722 }
5724 md->addQualifiers(root->qualifiers);
5726 const NamespaceDef *nd=md->getNamespaceDef();
5727 DString fullName;
5728 if (cd)
5729 fullName = cd->name();
5730 else if (nd)
5731 fullName = nd->name();
5732
5733 if (!fullName.empty()) fullName+="::";
5734 fullName+=md->name();
5735 FileDef *rfd=root->fileDef();
5736
5737 // TODO determine scope based on root not md
5738 Definition *rscope = md->getOuterScope();
5739
5740 const ArgumentList &mdAl = md->argumentList();
5741 if (al)
5742 {
5743 ArgumentList mergedAl = *al;
5744 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5745 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5746 }
5747 else
5748 {
5749 if (
5750 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5751 rscope,rfd,root->type,&root->argList,
5752 true, root->lang
5753 )
5754 )
5755 {
5756 //printf("merging arguments (2)\n");
5757 ArgumentList mergedArgList = root->argList;
5758 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5759 }
5760 }
5761 if (over_load) // the \overload keyword was used
5762 {
5764 if (!root->doc.empty())
5765 {
5766 doc+="<p>";
5767 doc+=root->doc;
5768 }
5769 md->setDocumentation(doc,root->docFile,root->docLine);
5771 md->setDocsForDefinition(!root->proto);
5772 }
5773 else
5774 {
5775 //printf("overwrite!\n");
5776 md->setDocumentation(root->doc,root->docFile,root->docLine);
5777 md->setDocsForDefinition(!root->proto);
5778
5779 //printf("overwrite!\n");
5780 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5781
5782 if (
5783 (md->inbodyDocumentation().empty() ||
5784 !root->parent()->name.empty()
5785 ) && !root->inbodyDocs.empty()
5786 )
5787 {
5789 }
5790 }
5791
5792 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5793 // qPrint(md->initializer()),md->initializer().empty(),
5794 // qPrint(root->initializer),root->initializer.empty()
5795 // );
5796 std::string rootInit = root->initializer.str();
5797 if (md->initializer().empty() && !rootInit.empty())
5798 {
5799 //printf("setInitializer\n");
5800 md->setInitializer(rootInit);
5801 }
5802 if (md->requiresClause().empty() && !root->req.empty())
5803 {
5804 md->setRequiresClause(root->req);
5805 }
5806
5807 md->setMaxInitLines(root->initLines);
5808
5809 if (rfd)
5810 {
5811 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5812 )
5813 {
5814 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5815 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5816 md->setBodyDef(rfd);
5817 }
5818
5819 md->setRefItems(root->sli);
5820 md->setRequirementReferences(root->rqli);
5821 }
5822
5824 md->addQualifiers(root->qualifiers);
5825
5826 md->mergeMemberSpecifiers(spec);
5828 addMemberToGroups(root,md);
5829 addIncludeFile(md,rfd,root);
5831 if (cd) cd->insertUsedFile(rfd);
5832 //printf("root->mGrpId=%d\n",root->mGrpId);
5833 if (root->mGrpId!=-1)
5834 {
5835 if (md->getMemberGroupId()!=-1)
5836 {
5837 if (md->getMemberGroupId()!=root->mGrpId)
5838 {
5839 warn(root->fileName,root->startLine,
5840 "member {} belongs to two different groups. The second one found here will be ignored.",
5841 md->name()
5842 );
5843 }
5844 }
5845 else // set group id
5846 {
5847 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5848 md->setMemberGroupId(root->mGrpId);
5849 }
5850 }
5851 md->addQualifiers(root->qualifiers);
5852}
5853
5854//----------------------------------------------------------------------
5855// find a class definition given the scope name and (optionally) a
5856// template list specifier
5857
5859 const DString &scopeName)
5860{
5861 SymbolResolver resolver(fd);
5862 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5863 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5864 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5865 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5866 return tcd;
5867}
5868
5869//----------------------------------------------------------------------------
5870// Returns true, if the entry belongs to the group of the member definition,
5871// otherwise false.
5872
5873static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5874{
5875 const GroupDef *gd = md->getGroupDef();
5876 if (!gd)
5877 {
5878 return allowNoGroup;
5879 }
5880
5881 for (const auto &g : root->groups)
5882 {
5883 if (g.groupname == gd->name())
5884 {
5885 return true; // matching group
5886 }
5887 }
5888
5889 return false;
5890}
5891
5892//----------------------------------------------------------------------
5893// Adds the documentation contained in 'root' to a global function
5894// with name 'name' and argument list 'args' (for overloading) and
5895// function declaration 'decl' to the corresponding member definition.
5896
5897static bool findGlobalMember(const Entry *root,
5898 const DString &namespaceName,
5899 const DString &type,
5900 const DString &name,
5901 const DString &tempArg,
5902 const DString &,
5903 const DString &decl,
5904 TypeSpecifier /* spec */)
5905{
5906 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5907 DString n=name;
5908 if (n.empty()) return false;
5909 if (n.find("::")!=DString::npos) return false; // skip undefined class members
5910 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5911 if (mn==nullptr)
5912 {
5913 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5914 }
5915 if (mn) // function name defined
5916 {
5917 AUTO_TRACE_ADD("Found symbol name");
5918 //int count=0;
5919 bool found=false;
5920 for (const auto &md : *mn)
5921 {
5922 // If the entry has groups, then restrict the search to members which are
5923 // in one of the groups of the entry. If md is not associated with a group yet,
5924 // allow this documentation entry to add the group info.
5925 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5926 {
5927 continue;
5928 }
5929
5930 const NamespaceDef *nd=nullptr;
5931 if (md->isAlias() && md->getOuterScope() &&
5932 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5933 {
5934 nd = toNamespaceDef(md->getOuterScope());
5935 }
5936 else
5937 {
5938 nd = md->getNamespaceDef();
5939 }
5940
5941 // special case for strong enums
5942 size_t enumNamePos=0;
5943 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
5944 { // md part of a strong enum in a namespace?
5945 DString enumName = namespaceName.mid(enumNamePos+2);
5946 if (namespaceName.left(enumNamePos)==nd->name())
5947 {
5949 if (enumMn)
5950 {
5951 for (const auto &emd : *enumMn)
5952 {
5953 found = emd->isStrong() && md->getEnumScope()==emd.get();
5954 if (found)
5955 {
5956 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5957 break;
5958 }
5959 }
5960 }
5961 }
5962 if (found)
5963 {
5964 break;
5965 }
5966 }
5967 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5968 {
5969 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5970 if (enumMn)
5971 {
5972 for (const auto &emd : *enumMn)
5973 {
5974 found = emd->isStrong() && md->getEnumScope()==emd.get();
5975 if (found)
5976 {
5977 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5978 break;
5979 }
5980 }
5981 }
5982 }
5983
5984 const FileDef *fd=root->fileDef();
5985 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5987 if (fd)
5988 {
5989 nl = fd->getUsedNamespaces();
5990 }
5991 //printf("NamespaceList %p\n",nl);
5992
5993 // search in the list of namespaces that are imported via a
5994 // using declaration
5995 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5996
5997 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
5998 (nd && nd->name()==namespaceName) || // or in the same namespace
5999 viaUsingDirective // member in 'using' namespace
6000 )
6001 {
6002 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
6003
6004 NamespaceDef *rnd = nullptr;
6005 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
6006
6007 const ArgumentList &mdAl = md.get()->argumentList();
6008 bool matching=
6009 (mdAl.empty() && root->argList.empty()) ||
6010 md->isVariable() || md->isTypedef() || /* in case of function pointers */
6011 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
6012 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
6013 false,root->lang);
6014
6015 // for template members we need to check if the number of
6016 // template arguments is the same, otherwise we are dealing with
6017 // different functions.
6018 if (matching && !root->tArgLists.empty())
6019 {
6020 const ArgumentList &mdTempl = md->templateArguments();
6021 if (root->tArgLists.back().size()!=mdTempl.size())
6022 {
6023 matching=false;
6024 }
6025 }
6026
6027 //printf("%s<->%s\n",
6028 // qPrint(argListToString(md->argumentList())),
6029 // qPrint(argListToString(root->argList)));
6030
6031 // For static members we also check if the comment block was found in
6032 // the same file. This is needed because static members with the same
6033 // name can be in different files. Thus it would be wrong to just
6034 // put the comment block at the first syntactically matching member. If
6035 // the comment block belongs to a group of the static member, then add
6036 // the documentation even if it is in a different file.
6037 if (matching && md->isStatic() &&
6038 md->getDefFileName()!=root->fileName &&
6039 mn->size()>1 &&
6040 !isEntryInGroupOfMember(root,md.get()))
6041 {
6042 matching = false;
6043 }
6044
6045 // for template member we also need to check the return type and requires
6046 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6047 {
6048 //printf("Comparing return types '%s'<->'%s'\n",
6049 // md->typeString(),type);
6050 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6051 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6052 md->typeString()!=type ||
6053 md->requiresClause()!=root->req)
6054 {
6055 //printf(" ---> no matching\n");
6056 matching = false;
6057 }
6058 }
6059
6060 if (matching) // add docs to the member
6061 {
6062 AUTO_TRACE_ADD("Match found");
6063 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6064 found=true;
6065 break;
6066 }
6067 }
6068 }
6069 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6070 {
6071 DString fullFuncDecl=decl;
6072 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6073 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6074 if (mn->size()>0)
6075 {
6076 warnMsg+="\nPossible candidates:";
6077 for (const auto &md : *mn)
6078 {
6079 warnMsg+="\n '";
6080 warnMsg+=replaceAnonymousScopes(md->declaration());
6081 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6082 }
6083 }
6084 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6085 }
6086 }
6087 else // got docs for an undefined member!
6088 {
6089 if (root->type!="friend class" &&
6090 root->type!="friend struct" &&
6091 root->type!="friend union" &&
6092 root->type!="friend" &&
6093 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6094 root->type.find("typedef ")==DString::npos)
6095 )
6096 {
6097 warn(root->fileName,root->startLine,
6098 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6099 );
6100 }
6101 }
6102 return true;
6103}
6104
6106 const ArgumentLists &srcTempArgLists,
6107 const ArgumentLists &dstTempArgLists
6108 )
6109{
6110 auto srcIt = srcTempArgLists.begin();
6111 auto dstIt = dstTempArgLists.begin();
6112 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6113 {
6114 if ((*srcIt).size()!=(*dstIt).size()) return true;
6115 ++srcIt;
6116 ++dstIt;
6117 }
6118 return false;
6119}
6120
6121static bool scopeIsTemplate(const Definition *d)
6122{
6123 bool result=false;
6124 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6126 {
6127 auto cd = toClassDef(d);
6128 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6130 }
6131 //printf("< scopeIsTemplate=%d\n",result);
6132 return result;
6133}
6134
6136 const ArgumentLists &srcTempArgLists,
6137 const ArgumentLists &dstTempArgLists,
6138 const std::string &src
6139 )
6140{
6141 std::string dst;
6142 static const reg::Ex re(R"(\a\w*)");
6143 reg::Iterator it(src,re);
6145 //printf("type=%s\n",qPrint(sa->type));
6146 size_t p=0;
6147 for (; it!=end ; ++it) // for each word in srcType
6148 {
6149 const auto &match = *it;
6150 size_t i = match.position();
6151 size_t l = match.length();
6152 bool found=false;
6153 dst+=src.substr(p,i-p);
6154 std::string name=match.str();
6155
6156 auto srcIt = srcTempArgLists.begin();
6157 auto dstIt = dstTempArgLists.begin();
6158 while (srcIt!=srcTempArgLists.end() && !found)
6159 {
6160 const ArgumentList *tdAli = nullptr;
6161 std::vector<Argument>::const_iterator tdaIt;
6162 if (dstIt!=dstTempArgLists.end())
6163 {
6164 tdAli = &(*dstIt);
6165 tdaIt = tdAli->begin();
6166 ++dstIt;
6167 }
6168
6169 const ArgumentList &tsaLi = *srcIt;
6170 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6171 {
6172 Argument tsa = *tsaIt;
6173 const Argument *tda = nullptr;
6174 if (tdAli && tdaIt!=tdAli->end())
6175 {
6176 tda = &(*tdaIt);
6177 ++tdaIt;
6178 }
6179 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6180 // qPrint(tsa.type),qPrint(tsa.name),
6181 // qPrint(tda->type),qPrint(tda->name));
6182 if (name==tsa.name.str())
6183 {
6184 if (tda && tda->name.empty())
6185 {
6186 DString tdaName = tda->name;
6187 DString tdaType = tda->type;
6188 int vc=0;
6189 if (tdaType.startsWith("class ")) vc=6;
6190 else if (tdaType.startsWith("typename ")) vc=9;
6191 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6192 {
6193 tdaName = tdaType.mid(vc);
6194 }
6195 if (!tdaName.empty())
6196 {
6197 name=tdaName.str(); // substitute
6198 found=true;
6199 }
6200 }
6201 }
6202 }
6203
6204 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6205 // qPrint(argListToString(srclali.current())),
6206 // qPrint(argListToString(dstlali.current())),
6207 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6208 ++srcIt;
6209 }
6210 dst+=name;
6211 p=i+l;
6212 }
6213 dst+=src.substr(p);
6214 //printf(" substituteTemplatesInString(%s)=%s\n",
6215 // qPrint(src),qPrint(dst));
6216 return dst;
6217}
6218
6220 const ArgumentLists &srcTempArgLists,
6221 const ArgumentLists &dstTempArgLists,
6222 const ArgumentList &src,
6223 ArgumentList &dst
6224 )
6225{
6226 auto dstIt = dst.begin();
6227 for (const Argument &sa : src)
6228 {
6229 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6230 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6231 if (dstIt == dst.end())
6232 {
6233 Argument da = sa;
6234 da.type = dstType;
6235 da.array = dstArray;
6236 dst.push_back(da);
6237 dstIt = dst.end();
6238 }
6239 else
6240 {
6241 Argument da = *dstIt;
6242 da.type = dstType;
6243 da.array = dstArray;
6244 ++dstIt;
6245 }
6246 }
6251 srcTempArgLists,dstTempArgLists,
6252 src.trailingReturnType().str()));
6253 dst.setIsDeleted(src.isDeleted());
6254 dst.setRefQualifier(src.refQualifier());
6255 dst.setNoParameters(src.noParameters());
6256 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6257 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6258 // );
6259}
6260
6261//-------------------------------------------------------------------------------------------
6262
6263static void addLocalObjCMethod(const Entry *root,
6264 const DString &scopeName,
6265 const DString &funcType,const DString &funcName,const DString &funcArgs,
6266 const DString &exceptions,const DString &funcDecl,
6267 TypeSpecifier spec)
6268{
6269 AUTO_TRACE();
6270 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6271 ClassDefMutable *cd=nullptr;
6272 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6273 {
6274 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6275 auto md = createMemberDef(
6276 root->fileName,root->startLine,root->startColumn,
6277 funcType,funcName,funcArgs,exceptions,
6278 root->protection,root->virt,root->isStatic,Relationship::Member,
6279 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6280 auto mmd = toMemberDefMutable(md.get());
6281 mmd->setTagInfo(root->tagInfo());
6282 mmd->setLanguage(root->lang);
6283 mmd->setId(root->id);
6284 mmd->makeImplementationDetail();
6285 mmd->setMemberClass(cd);
6286 mmd->setDefinition(funcDecl);
6288 mmd->addQualifiers(root->qualifiers);
6289 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6290 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6291 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6292 mmd->setDocsForDefinition(!root->proto);
6293 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6294 mmd->addSectionsToDefinition(root->anchors);
6295 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6296 FileDef *fd=root->fileDef();
6297 mmd->setBodyDef(fd);
6298 mmd->setMemberSpecifiers(spec);
6299 mmd->setVhdlSpecifiers(root->vhdlSpec);
6300 mmd->setMemberGroupId(root->mGrpId);
6301 cd->insertMember(md.get());
6302 cd->insertUsedFile(fd);
6303 mmd->setRefItems(root->sli);
6304 mmd->setRequirementReferences(root->rqli);
6305 addIncludeFile(mmd,fd,root);
6306
6308 mn->push_back(std::move(md));
6309 }
6310 else
6311 {
6312 // local objective C method found for class without interface
6313 }
6314}
6315
6316//-------------------------------------------------------------------------------------------
6317
6318static void addMemberFunction(const Entry *root,
6319 MemberName *mn,
6320 const DString &scopeName,
6321 const DString &namespaceName,
6322 const DString &className,
6323 const DString &funcTyp,
6324 const DString &funcName,
6325 const DString &funcArgs,
6326 const DString &funcTempList,
6327 const DString &exceptions,
6328 const DString &type,
6329 const DString &args,
6330 bool isFriend,
6331 TypeSpecifier spec,
6332 const DString &relates,
6333 const DString &funcDecl,
6334 bool overloaded,
6335 bool isFunc)
6336{
6337 AUTO_TRACE();
6338 DString funcType = funcTyp;
6339 int count=0;
6340 int noMatchCount=0;
6341 bool memFound=false;
6342 for (const auto &imd : *mn)
6343 {
6344 MemberDefMutable *md = toMemberDefMutable(imd.get());
6345 if (md==nullptr) continue;
6347 if (cd==nullptr) continue;
6348 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6349 // scopeName, cd->name(), md->argsString(), root->fileName);
6350 FileDef *fd=root->fileDef();
6351 NamespaceDef *nd=nullptr;
6352 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6353
6354 //printf("scopeName %s->%s\n",qPrint(scopeName),
6355 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6356
6357 // if the member we are searching for is an enum value that is part of
6358 // a "strong" enum, we need to look into the fields of the enum for a match
6359 size_t enumNamePos=0;
6360 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6361 {
6362 DString enumName = className.mid(enumNamePos+2);
6363 DString fullScope = className.left(enumNamePos);
6364 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6365 if (fullScope==cd->name())
6366 {
6367 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6368 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6369 if (enumMn)
6370 {
6371 for (const auto &emd : *enumMn)
6372 {
6373 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6374 if (memFound)
6375 {
6376 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6377 count++;
6378 }
6379 if (memFound) break;
6380 }
6381 }
6382 }
6383 }
6384 if (memFound) break;
6385
6386 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6387 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6388 {
6389 // don't be fooled by anonymous scopes
6390 tcd=cd;
6391 }
6392 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6393 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6394
6395 if (cd && tcd==cd) // member's classes match
6396 {
6397 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6398
6399 // get the template parameter lists found at the member declaration
6400 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6401 const ArgumentList &templAl = md->templateArguments();
6402 if (!templAl.empty())
6403 {
6404 declTemplArgs.push_back(templAl);
6405 }
6406
6407 // get the template parameter lists found at the member definition
6408 const ArgumentLists &defTemplArgs = root->tArgLists;
6409 //printf("defTemplArgs=%p\n",defTemplArgs);
6410
6411 // do we replace the decl argument lists with the def argument lists?
6412 bool substDone=false;
6413 ArgumentList argList;
6414
6415 /* substitute the occurrences of class template names in the
6416 * argument list before matching
6417 */
6418 const ArgumentList &mdAl = md->argumentList();
6419 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6420 {
6421 /* the function definition has template arguments
6422 * and the class definition also has template arguments, so
6423 * we must substitute the template names of the class by that
6424 * of the function definition before matching.
6425 */
6426 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6427
6428 substDone=true;
6429 }
6430 else /* no template arguments, compare argument lists directly */
6431 {
6432 argList = mdAl;
6433 }
6434
6435 bool matching=
6436 md->isVariable() || md->isTypedef() || // needed for function pointers
6438 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6439 cd,fd,root->type,&root->argList,
6440 true,root->lang);
6441
6442 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6443 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6444
6445 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6446 {
6447 matching = false; // don't match methods and attributes with the same name
6448 }
6449
6450 // for template member we also need to check the return type
6451 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6452 {
6453 DString memType = md->typeString();
6454 memType.stripPrefix("static "); // see bug700696
6455 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6456 className+"::",""); // see bug700693 & bug732594
6457 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6458 className+"::",""); // see bug758900
6459 if (memType=="auto" && !argList.trailingReturnType().empty())
6460 {
6461 memType = argList.trailingReturnType();
6462 memType.stripPrefix(" -> ");
6463 }
6464 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6465 {
6466 funcType = root->argList.trailingReturnType();
6467 funcType.stripPrefix(" -> ");
6469 substDone=true;
6470 }
6471 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6472 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6473 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6474 {
6475 //printf(" ---> no matching\n");
6476 matching = false;
6477 }
6478 }
6479 else if (defTemplArgs.size()>declTemplArgs.size())
6480 {
6481 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6482 // avoid matching a non-template function in a template class against a
6483 // template function with the same name and parameters, see issue #10184
6484 substDone = false;
6485 matching = false;
6486 }
6487 bool rootIsUserDoc = root->section.isMemberDoc();
6488 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6489 bool mdIsTemplate = md->templateArguments().hasParameters();
6490 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6491 bool rootIsTemplate = !root->tArgLists.empty();
6492 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6493 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6494 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6495 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6496 )
6497 {
6498 // Method with template return type does not match method without return type
6499 // even if the parameters are the same. See also bug709052
6500 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6501 matching = false;
6502 }
6503
6504 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6505
6506 if (substDone) // found a new argument list
6507 {
6508 if (matching) // replace member's argument list
6509 {
6511 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6512 }
6513 else // no match
6514 {
6515 if (!funcTempList.empty() &&
6516 isSpecialization(declTemplArgs,defTemplArgs))
6517 {
6518 // check if we are dealing with a partial template
6519 // specialization. In this case we add it to the class
6520 // even though the member arguments do not match.
6521
6522 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6523 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6524 return;
6525 }
6526 }
6527 }
6528 if (matching)
6529 {
6530 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6531 count++;
6532 memFound=true;
6533 }
6534 }
6535 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6536 // but in a different namespace
6537 {
6538 noMatchCount++;
6539 }
6540
6541 if (memFound) break;
6542 }
6543 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6544 {
6545 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6546 return;
6547 }
6548 if (count==0 && !(isFriend && funcType=="class"))
6549 {
6550 int candidates=0;
6551 const ClassDef *ecd = nullptr, *ucd = nullptr;
6552 MemberDef *emd = nullptr, *umd = nullptr;
6553 //printf("Assume template class\n");
6554 for (const auto &md : *mn)
6555 {
6556 MemberDef *cmd=md.get();
6558 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6559 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6560 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6561 {
6562 const ArgumentList &templAl = md->templateArguments();
6563 if (!root->tArgLists.empty() && !templAl.empty() &&
6564 root->tArgLists.back().size()<=templAl.size())
6565 {
6566 AUTO_TRACE_ADD("add template specialization");
6567 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6568 root->protection,root->isStatic,root->virt,spec,relates);
6569 return;
6570 }
6571 if (argListToString(md->argumentList(),false,false) ==
6572 argListToString(root->argList,false,false))
6573 { // exact argument list match -> remember
6574 ucd = ecd = ccd;
6575 umd = emd = cmd;
6576 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6577 className,ccd->name(),md->argsString());
6578 }
6579 else // arguments do not match, but member name and scope do -> remember
6580 {
6581 ucd = ccd;
6582 umd = cmd;
6583 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6584 className,ccd->name(),md->argsString());
6585 }
6586 candidates++;
6587 }
6588 }
6589 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6590 if (!strictProtoMatching)
6591 {
6592 if (candidates==1 && ucd && umd)
6593 {
6594 // we didn't find an actual match on argument lists, but there is only 1 member with this
6595 // name in the same scope, so that has to be the one.
6596 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6597 return;
6598 }
6599 else if (candidates>1 && ecd && emd)
6600 {
6601 // we didn't find a unique match using type resolution,
6602 // but one of the matches has the exact same signature so
6603 // we take that one.
6604 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6605 return;
6606 }
6607 }
6608
6609 DString warnMsg = "no ";
6610 if (noMatchCount>1) warnMsg+="uniquely ";
6611 warnMsg+="matching class member found for \n";
6612
6613 for (const ArgumentList &al : root->tArgLists)
6614 {
6615 warnMsg+=" template ";
6616 warnMsg+=tempArgListToString(al,root->lang);
6617 warnMsg+='\n';
6618 }
6619
6620 DString fullFuncDecl=funcDecl;
6621 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6622
6623 warnMsg+=" ";
6624 warnMsg+=fullFuncDecl;
6625
6626 if (candidates>0 || noMatchCount>=1)
6627 {
6628 warnMsg+="\nPossible candidates:";
6629
6630 NamespaceDef *nd=nullptr;
6631 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6632 FileDef *fd=root->fileDef();
6633
6634 for (const auto &md : *mn)
6635 {
6636 const ClassDef *cd=md->getClassDef();
6637 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6638 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6639 {
6640 // don't be fooled by anonymous scopes
6641 tcd=cd;
6642 }
6643 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6644 {
6645 warnMsg+='\n';
6646 const ArgumentList &templAl = md->templateArguments();
6647 warnMsg+=" '";
6648 if (templAl.hasParameters())
6649 {
6650 warnMsg+="template ";
6651 warnMsg+=tempArgListToString(templAl,root->lang);
6652 warnMsg+='\n';
6653 warnMsg+=" ";
6654 }
6655 if (!md->typeString().empty())
6656 {
6657 warnMsg+=md->typeString();
6658 warnMsg+=' ';
6659 }
6661 if (!qScope.empty())
6662 warnMsg+=qScope+"::"+md->name();
6663 warnMsg+=md->argsString();
6664 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6665 }
6666 }
6667 }
6668 warn(root->fileName,root->startLine,"{}",warnMsg);
6669 }
6670}
6671
6672//-------------------------------------------------------------------------------------------
6673
6674static void addMemberSpecialization(const Entry *root,
6675 MemberName *mn,
6676 ClassDefMutable *cd,
6677 const DString &funcType,
6678 const DString &funcName,
6679 const DString &funcArgs,
6680 const DString &funcDecl,
6681 const DString &exceptions,
6682 TypeSpecifier spec
6683 )
6684{
6685 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6686 MemberDef *declMd=nullptr;
6687 for (const auto &md : *mn)
6688 {
6689 if (md->getClassDef()==cd)
6690 {
6691 // TODO: we should probably also check for matching arguments
6692 declMd = md.get();
6693 break;
6694 }
6695 }
6696 MemberType mtype=MemberType::Function;
6697 ArgumentList tArgList;
6698 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6699 auto md = createMemberDef(
6700 root->fileName,root->startLine,root->startColumn,
6701 funcType,funcName,funcArgs,exceptions,
6702 declMd ? declMd->protection() : root->protection,
6703 root->virt,root->isStatic,Relationship::Member,
6704 mtype,tArgList,root->argList,root->metaData);
6705 auto mmd = toMemberDefMutable(md.get());
6706 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6707 mmd->setTagInfo(root->tagInfo());
6708 mmd->setLanguage(root->lang);
6709 mmd->setId(root->id);
6710 mmd->setMemberClass(cd);
6711 mmd->setTemplateSpecialization(true);
6712 mmd->setTypeConstraints(root->typeConstr);
6713 mmd->setDefinition(funcDecl);
6715 mmd->addQualifiers(root->qualifiers);
6716 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6717 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6718 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6719 mmd->setDocsForDefinition(!root->proto);
6720 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6721 mmd->addSectionsToDefinition(root->anchors);
6722 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6723 FileDef *fd=root->fileDef();
6724 mmd->setBodyDef(fd);
6725 mmd->setMemberSpecifiers(spec);
6726 mmd->setVhdlSpecifiers(root->vhdlSpec);
6727 mmd->setMemberGroupId(root->mGrpId);
6728 cd->insertMember(md.get());
6729 mmd->setRefItems(root->sli);
6730 mmd->setRequirementReferences(root->rqli);
6731 addIncludeFile(mmd,fd,root);
6732
6733 mn->push_back(std::move(md));
6734}
6735
6736//-------------------------------------------------------------------------------------------
6737
6738static void addOverloaded(const Entry *root,MemberName *mn,
6739 const DString &funcType,const DString &funcName,const DString &funcArgs,
6740 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6741{
6742 // for unique overloaded member we allow the class to be
6743 // omitted, this is to be Qt compatible. Using this should
6744 // however be avoided, because it is error prone
6745 bool sameClass=false;
6746 if (mn->size()>0)
6747 {
6748 // check if all members with the same name are also in the same class
6749 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6750 [](const auto &md1,const auto &md2)
6751 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6752 }
6753 if (sameClass)
6754 {
6755 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6756 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6757 if (cd==nullptr) return;
6758
6759 MemberType mtype = MemberType::Function;
6760 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6761 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6762 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6763
6764 // new overloaded member function
6765 std::unique_ptr<ArgumentList> tArgList =
6766 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6767 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6768 auto md = createMemberDef(
6769 root->fileName,root->startLine,root->startColumn,
6770 funcType,funcName,funcArgs,exceptions,
6771 root->protection,root->virt,root->isStatic,Relationship::Related,
6772 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6773 auto mmd = toMemberDefMutable(md.get());
6774 mmd->setTagInfo(root->tagInfo());
6775 mmd->setLanguage(root->lang);
6776 mmd->setId(root->id);
6777 mmd->setTypeConstraints(root->typeConstr);
6778 mmd->setMemberClass(cd);
6779 mmd->setDefinition(funcDecl);
6781 mmd->addQualifiers(root->qualifiers);
6783 doc+="<p>";
6784 doc+=root->doc;
6785 mmd->setDocumentation(doc,root->docFile,root->docLine);
6786 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6787 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6788 mmd->setDocsForDefinition(!root->proto);
6789 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6790 mmd->addSectionsToDefinition(root->anchors);
6791 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6792 FileDef *fd=root->fileDef();
6793 mmd->setBodyDef(fd);
6794 mmd->setMemberSpecifiers(spec);
6795 mmd->setVhdlSpecifiers(root->vhdlSpec);
6796 mmd->setMemberGroupId(root->mGrpId);
6797 cd->insertMember(md.get());
6798 cd->insertUsedFile(fd);
6799 mmd->setRefItems(root->sli);
6800 mmd->setRequirementReferences(root->rqli);
6801 addIncludeFile(mmd,fd,root);
6802
6803 mn->push_back(std::move(md));
6804 }
6805}
6806
6807static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6808{
6809 if (outerScope && outerScope!=Doxygen::globalScope)
6810 {
6811 auto aliasMd = createMemberDefAlias(outerScope,md);
6812 if (outerScope->definitionType()==Definition::TypeClass)
6813 {
6814 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6815 if (cdm)
6816 {
6817 cdm->insertMember(aliasMd.get());
6818 }
6819 }
6820 else if (outerScope->definitionType()==Definition::TypeNamespace)
6821 {
6822 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6823 if (ndm)
6824 {
6825 ndm->insertMember(aliasMd.get());
6826 }
6827 }
6828 else if (outerScope->definitionType()==Definition::TypeFile)
6829 {
6830 toFileDef(outerScope)->insertMember(aliasMd.get());
6831 }
6832 if (aliasMd)
6833 {
6834 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6835 }
6836 }
6837}
6838
6839//-------------------------------------------------------------------------------------------
6840
6841/*! This function tries to find a member (in a documented class/file/namespace)
6842 * that corresponds to the function/variable declaration given in \a funcDecl.
6843 *
6844 * The boolean \a overloaded is used to specify whether or not a standard
6845 * overload documentation line should be generated.
6846 *
6847 * The boolean \a isFunc is a hint that indicates that this is a function
6848 * instead of a variable or typedef.
6849 */
6850static void findMember(const Entry *root,
6851 const DString &relates,
6852 const DString &type,
6853 const DString &args,
6854 DString funcDecl,
6855 bool overloaded,
6856 bool isFunc
6857 )
6858{
6859 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6860 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6861 root->spec, root->lang);
6862
6863 DString scopeName;
6864 DString className;
6865 DString namespaceName;
6866 DString funcType;
6867 DString funcName;
6868 DString funcArgs;
6869 DString funcTempList;
6870 DString exceptions;
6871 DString funcSpec;
6872 bool isRelated=false;
6873 bool isMemberOf=false;
6874 bool isFriend=false;
6875 bool done=false;
6876 TypeSpecifier spec = root->spec;
6877 while (!done)
6878 {
6879 done=true;
6880 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6881 {
6882 isFriend=true;
6883 done=false;
6884 }
6885 if (funcDecl.stripPrefix("inline "))
6886 {
6887 spec.setInline(true);
6888 done=false;
6889 }
6890 if (funcDecl.stripPrefix("explicit "))
6891 {
6892 spec.setExplicit(true);
6893 done=false;
6894 }
6895 if (funcDecl.stripPrefix("mutable "))
6896 {
6897 spec.setMutable(true);
6898 done=false;
6899 }
6900 if (funcDecl.stripPrefix("thread_local "))
6901 {
6902 spec.setThreadLocal(true);
6903 done=false;
6904 }
6905 if (funcDecl.stripPrefix("virtual "))
6906 {
6907 done=false;
6908 }
6909 }
6910
6911 // delete any ; from the function declaration
6912 size_t sep=0;
6913 while ((sep=funcDecl.find(';'))!=DString::npos)
6914 {
6915 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
6916 }
6917
6918 // make sure the first character is a space to simplify searching.
6919 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6920
6921 // remove some superfluous spaces
6922 funcDecl= substitute(
6923 substitute(
6924 substitute(funcDecl,"~ ","~"),
6925 ":: ","::"
6926 ),
6927 " ::","::"
6928 ).stripWhiteSpace();
6929
6930 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6931 if (isFriend && funcDecl.startsWith("class "))
6932 {
6933 //printf("friend class\n");
6934 funcDecl=funcDecl.mid(6);
6935 funcName = funcDecl;
6936 }
6937 else if (isFriend && funcDecl.startsWith("struct "))
6938 {
6939 funcDecl=funcDecl.mid(7);
6940 funcName = funcDecl;
6941 }
6942 else
6943 {
6944 // extract information from the declarations
6945 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6946 funcArgs,funcTempList,exceptions
6947 );
6948 }
6949
6950 // the class name can also be a namespace name, we decide this later.
6951 // if a related class name is specified and the class name could
6952 // not be derived from the function declaration, then use the
6953 // related field.
6954 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6955 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6956 if (!relates.empty())
6957 { // related member, prefix user specified scope
6958 isRelated=true;
6959 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6960 if (getClass(relates)==nullptr && !scopeName.empty())
6961 {
6962 scopeName= mergeScopes(scopeName,relates);
6963 }
6964 else
6965 {
6966 scopeName = relates;
6967 }
6968 }
6969
6970 if (relates.empty() && root->parent() &&
6971 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6972 !root->parent()->name.empty()) // see if we can combine scopeName
6973 // with the scope in which it was found
6974 {
6975 DString joinedName = root->parent()->name+"::"+scopeName;
6976 if (!scopeName.empty() &&
6977 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6978 {
6979 scopeName = joinedName;
6980 }
6981 else
6982 {
6983 scopeName = mergeScopes(root->parent()->name,scopeName);
6984 }
6985 }
6986 else // see if we can prefix a namespace or class that is used from the file
6987 {
6988 FileDef *fd=root->fileDef();
6989 if (fd)
6990 {
6991 for (const auto &fnd : fd->getUsedNamespaces())
6992 {
6993 DString joinedName = fnd->name()+"::"+scopeName;
6994 if (Doxygen::namespaceLinkedMap->find(joinedName))
6995 {
6996 scopeName=joinedName;
6997 break;
6998 }
6999 }
7000 }
7001 }
7003 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
7004
7005 // funcSpec contains the last template specifiers of the given scope.
7006 // If this method does not have any template arguments or they are
7007 // empty while funcSpec is not empty we assume this is a
7008 // specialization of a method. If not, we clear the funcSpec and treat
7009 // this as a normal method of a template class.
7010 if (!(root->tArgLists.size()>0 &&
7011 root->tArgLists.front().size()==0
7012 )
7013 )
7014 {
7015 funcSpec.clear();
7016 }
7017
7018 //namespaceName=removeAnonymousScopes(namespaceName);
7019 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
7020
7021 // split scope into a namespace and a class part
7022 extractNamespaceName(scopeName,className,namespaceName,true);
7023 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
7024
7025 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
7026 // merge class and namespace scopes again
7027 scopeName.clear();
7028 if (!namespaceName.empty())
7029 {
7030 if (className.empty())
7031 {
7032 scopeName=namespaceName;
7033 }
7034 else if (!relates.empty() || // relates command with explicit scope
7035 !getClass(className)) // class name only exists in a namespace
7036 {
7037 scopeName=namespaceName+"::"+className;
7038 }
7039 else
7040 {
7041 scopeName=className;
7042 }
7043 }
7044 else if (!className.empty())
7045 {
7046 scopeName=className;
7047 }
7048 //printf("new scope='%s'\n",qPrint(scopeName));
7049
7050 DString tempScopeName=scopeName;
7051 ClassDefMutable *cd=getClassMutable(scopeName);
7052 if (cd)
7053 {
7054 if (funcSpec.empty())
7055 {
7056 uint32_t argListIndex=0;
7057 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7058 }
7059 else
7060 {
7061 tempScopeName=scopeName+funcSpec;
7062 }
7063 }
7064 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7065 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7066
7067 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7068 // rebuild the function declaration (needed to get the scope right).
7069 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7070 {
7071 if (!funcType.empty())
7072 {
7073 if (isFunc) // a function -> we use argList for the arguments
7074 {
7075 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7076 }
7077 else
7078 {
7079 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7080 }
7081 }
7082 else
7083 {
7084 if (isFunc) // a function => we use argList for the arguments
7085 {
7086 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7087 }
7088 else // variable => add 'argument' list
7089 {
7090 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7091 }
7092 }
7093 }
7094 else // build declaration without scope
7095 {
7096 if (!funcType.empty()) // but with a type
7097 {
7098 if (isFunc) // function => omit argument list
7099 {
7100 funcDecl=funcType+" "+funcName+funcTempList;
7101 }
7102 else // variable => add 'argument' list
7103 {
7104 funcDecl=funcType+" "+funcName+funcArgs;
7105 }
7106 }
7107 else // no type
7108 {
7109 if (isFunc)
7110 {
7111 funcDecl=funcName+funcTempList;
7112 }
7113 else
7114 {
7115 funcDecl=funcName+funcArgs;
7116 }
7117 }
7118 }
7119
7120 if (funcType=="template class" && !funcTempList.empty())
7121 return; // ignore explicit template instantiations
7122
7123 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7124 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7125 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7126 namespaceName, className, funcType, funcSpec,
7127 funcName, funcArgs, funcTempList, funcDecl, relates,
7128 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7129
7130 if (!funcName.empty()) // function name is valid
7131 {
7132 // check if 'className' is actually a scoped enum, in which case we need to
7133 // process it as a global, see issue #6471
7134 bool strongEnum = false;
7135 MemberName *mn=nullptr;
7136 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7137 {
7138 for (const auto &imd : *mn)
7139 {
7140 MemberDefMutable *md = toMemberDefMutable(imd.get());
7141 Definition *mdScope = nullptr;
7142 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7143 // need filter for the correct scope, see issue #9668
7144 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7145 {
7146 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7147 strongEnum = true;
7148 // pass the scope name name as a 'namespace' to the findGlobalMember function
7149 if (!namespaceName.empty())
7150 {
7151 namespaceName+="::"+className;
7152 }
7153 else
7154 {
7155 namespaceName=className;
7156 }
7157 }
7158 }
7159 }
7160
7161 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7162 {
7163 funcName = substitute(funcName,className+"::","");
7164 }
7165 mn = nullptr;
7166 if (!funcTempList.empty()) // try with member specialization
7167 {
7168 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7169 }
7170 if (mn==nullptr) // try without specialization
7171 {
7172 mn=Doxygen::memberNameLinkedMap->find(funcName);
7173 }
7174 if (!isRelated && !strongEnum && mn) // function name already found
7175 {
7176 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7177 if (!className.empty()) // class name is valid
7178 {
7179 if (funcSpec.empty()) // not a member specialization
7180 {
7181 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7182 funcArgs,funcTempList,exceptions,
7183 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7184 }
7185 else if (cd) // member specialization
7186 {
7187 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7188 }
7189 else
7190 {
7191 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7192 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7193 }
7194 }
7195 else if (overloaded) // check if the function belongs to only one class
7196 {
7197 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7198 }
7199 else // unrelated function with the same name as a member
7200 {
7201 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7202 {
7203 DString fullFuncDecl=funcDecl;
7204 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7205 warn(root->fileName,root->startLine,
7206 "Cannot determine class for function\n{}",
7207 fullFuncDecl
7208 );
7209 }
7210 }
7211 }
7212 else if (isRelated && !relates.empty())
7213 {
7214 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7215 if (className.empty()) className=relates;
7216 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7217 if ((cd=getClassMutable(scopeName)))
7218 {
7219 bool newMember=true; // assume we have a new member
7220 MemberDefMutable *mdDefine=nullptr;
7221 {
7222 mn = Doxygen::functionNameLinkedMap->find(funcName);
7223 if (mn)
7224 {
7225 for (const auto &imd : *mn)
7226 {
7227 MemberDefMutable *md = toMemberDefMutable(imd.get());
7228 if (md && md->isDefine())
7229 {
7230 mdDefine = md;
7231 break;
7232 }
7233 }
7234 }
7235 }
7236
7237 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7238 {
7239 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7240
7241 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7242 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7243 // insert it as a class member
7244 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7245 {
7246 mn=Doxygen::memberNameLinkedMap->add(funcName);
7247 }
7248
7249 if (mdDefine->getFileDef())
7250 {
7251 mdDefine->getFileDef()->removeMember(mdDefine);
7252 }
7253 mdDefine->makeRelated();
7254 mdDefine->setMemberClass(cd);
7255 mdDefine->moveTo(cd);
7256 cd->insertMember(mdDefine);
7257 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7258 insertMemberAlias(cd->getOuterScope(),mdDefine);
7259 mn->push_back(std::move(mdDefineTaken));
7260 }
7261 else // normal member, needs to be created and added to the class
7262 {
7263 FileDef *fd=root->fileDef();
7264
7265 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7266 {
7267 mn=Doxygen::memberNameLinkedMap->add(funcName);
7268 }
7269 else
7270 {
7271 // see if we got another member with matching arguments
7272 MemberDefMutable *rmd_found = nullptr;
7273 for (const auto &irmd : *mn)
7274 {
7275 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7276 if (rmd)
7277 {
7278 const ArgumentList &rmdAl = rmd->argumentList();
7279
7280 newMember=
7281 className!=rmd->getOuterScope()->name() ||
7282 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7283 cd,fd,root->type,&root->argList,
7284 true,root->lang);
7285 if (!newMember)
7286 {
7287 rmd_found = rmd;
7288 }
7289 }
7290 }
7291 if (rmd_found) // member already exists as rmd -> add docs
7292 {
7293 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7294 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7295 newMember=false;
7296 }
7297 }
7298
7299 if (newMember) // need to create a new member
7300 {
7301 MemberType mtype = MemberType::Function;
7302 switch (root->mtype)
7303 {
7304 case MethodTypes::Method: mtype = MemberType::Function; break;
7305 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7306 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7307 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7308 case MethodTypes::Property: mtype = MemberType::Property; break;
7309 case MethodTypes::Event: mtype = MemberType::Event; break;
7310 }
7311
7312 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7313 // root->argList ? (int)root->argList->count() : -1);
7314
7315 // first note that we pass:
7316 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7317 // for the template arguments for the new "member."
7318 // this accurately reflects the template arguments of
7319 // the related function, which don't have to do with
7320 // those of the related class.
7321 auto md = createMemberDef(
7322 root->fileName,root->startLine,root->startColumn,
7323 funcType,funcName,funcArgs,exceptions,
7324 root->protection,root->virt,
7325 root->isStatic,
7326 isMemberOf ? Relationship::Foreign : Relationship::Related,
7327 mtype,
7328 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7329 funcArgs.empty() ? ArgumentList() : root->argList,
7330 root->metaData);
7331 auto mmd = toMemberDefMutable(md.get());
7332
7333 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7334 insertMemberAlias(cd->getOuterScope(),md.get());
7335
7336 // we still have the problem that
7337 // MemberDef::writeDocumentation() in memberdef.cpp
7338 // writes the template argument list for the class,
7339 // as if this member is a member of the class.
7340 // fortunately, MemberDef::writeDocumentation() has
7341 // a special mechanism that allows us to totally
7342 // override the set of template argument lists that
7343 // are printed. We use that and set it to the
7344 // template argument lists of the related function.
7345 //
7346 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7347
7348 mmd->setTagInfo(root->tagInfo());
7349
7350 addIncludeFile(mmd,root->fileDef(),root);
7351 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7352 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7353
7354 // try to find the matching line number of the body from the
7355 // global function list
7356 bool found=false;
7357 if (root->bodyLine==-1)
7358 {
7360 if (rmn)
7361 {
7362 const MemberDefMutable *rmd_found=nullptr;
7363 for (const auto &irmd : *rmn)
7364 {
7365 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7366 if (rmd)
7367 {
7368 const ArgumentList &rmdAl = rmd->argumentList();
7369 // check for matching argument lists
7370 if (
7371 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7372 cd,fd,root->type,&root->argList,
7373 true,root->lang)
7374 )
7375 {
7376 found=true;
7377 rmd_found = rmd;
7378 break;
7379 }
7380 }
7381 }
7382 if (rmd_found) // member found -> copy line number info
7383 {
7384 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7385 mmd->setBodyDef(rmd_found->getBodyDef());
7386 //md->setBodyMember(rmd);
7387 }
7388 }
7389 }
7390 if (!found) // line number could not be found or is available in this
7391 // entry
7392 {
7393 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7394 mmd->setBodyDef(fd);
7395 }
7396
7397 //if (root->mGrpId!=-1)
7398 //{
7399 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7400 //}
7401 mmd->setMemberClass(cd);
7402 mmd->setMemberSpecifiers(spec);
7403 mmd->setVhdlSpecifiers(root->vhdlSpec);
7404 mmd->setDefinition(funcDecl);
7406 mmd->addQualifiers(root->qualifiers);
7407 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7408 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7409 mmd->setDocsForDefinition(!root->proto);
7410 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7411 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7412 mmd->addSectionsToDefinition(root->anchors);
7413 mmd->setMemberGroupId(root->mGrpId);
7414 mmd->setLanguage(root->lang);
7415 mmd->setId(root->id);
7416 //md->setMemberDefTemplateArguments(root->mtArgList);
7417 cd->insertMember(md.get());
7418 cd->insertUsedFile(fd);
7419 mmd->setRefItems(root->sli);
7420 mmd->setRequirementReferences(root->rqli);
7421 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7422 addMemberToGroups(root,md.get());
7424 //printf("Adding member=%s\n",qPrint(md->name()));
7425 mn->push_back(std::move(md));
7426 }
7427 if (root->relatesType==RelatesType::Duplicate)
7428 {
7429 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7430 {
7431 DString fullFuncDecl=funcDecl;
7432 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7433 warn(root->fileName,root->startLine,
7434 "Cannot determine file/namespace for relatedalso function\n{}",
7435 fullFuncDecl
7436 );
7437 }
7438 }
7439 }
7440 }
7441 else
7442 {
7443 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7444 }
7445 }
7446 else if (root->parent() && root->parent()->section.isObjcImpl())
7447 {
7448 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7449 }
7450 else // unrelated not overloaded member found
7451 {
7452 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7453 if (className.empty() && !globMem)
7454 {
7455 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7456 }
7457 else if (!className.empty() && !globMem)
7458 {
7459 warn(root->fileName,root->startLine,
7460 "member '{}' of class '{}' cannot be found",
7461 funcName,className);
7462 }
7463 }
7464 }
7465 else
7466 {
7467 // this should not be called
7468 warn(root->fileName,root->startLine,"member with no name found.");
7469 }
7470 return;
7471}
7472
7473//----------------------------------------------------------------------
7474// find the members corresponding to the different documentation blocks
7475// that are extracted from the sources.
7476
7477static void filterMemberDocumentation(const Entry *root,const DString &relates)
7478{
7479 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7480 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7481 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7482 bool isFunc=true;
7483
7484 DString type = root->type;
7485 DString args = root->args;
7486 int i=-1, l=0;
7487 if ( // detect func variable/typedef to func ptr
7488 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7489 )
7490 {
7491 //printf("Fixing function pointer!\n");
7492 // fix type and argument
7493 args.prepend(type.mid(i+l));
7494 type=type.left(i+l);
7495 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7496 isFunc=false;
7497 }
7498 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7499 // detect function types marked as functions
7500 {
7501 isFunc=false;
7502 }
7503
7504 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7505 if (root->section.isMemberDoc())
7506 {
7507 //printf("Documentation for inline member '%s' found args='%s'\n",
7508 // qPrint(root->name),qPrint(args));
7509 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7510 if (type.empty())
7511 {
7512 findMember(root,
7513 relates,
7514 type,
7515 args,
7516 root->name + args + root->exception,
7517 false,
7518 isFunc);
7519 }
7520 else
7521 {
7522 findMember(root,
7523 relates,
7524 type,
7525 args,
7526 type + " " + root->name + args + root->exception,
7527 false,
7528 isFunc);
7529 }
7530 }
7531 else if (root->section.isOverloadDoc())
7532 {
7533 //printf("Overloaded member %s found\n",qPrint(root->name));
7534 findMember(root,
7535 relates,
7536 type,
7537 args,
7538 root->name,
7539 true,
7540 isFunc);
7541 }
7542 else if
7543 ((root->section.isFunction() // function
7544 ||
7545 (root->section.isVariable() && // variable
7546 !type.empty() && // with a type
7547 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7548 // (to skip forward declaration of class etc.)
7549 )
7550 )
7551 )
7552 {
7553 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7554 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7555 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7556 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7557 if (isTypeAClassFriend(type))
7558 {
7559 findMember(root,
7560 relates,
7561 type,
7562 args,
7563 type+" "+root->name,
7564 false,false);
7565
7566 }
7567 else if (!type.empty())
7568 {
7569 findMember(root,
7570 relates,
7571 type,
7572 args,
7573 type+" "+ root->inside + root->name + args + root->exception,
7574 false,isFunc);
7575 }
7576 else
7577 {
7578 findMember(root,
7579 relates,
7580 type,
7581 args,
7582 root->inside + root->name + args + root->exception,
7583 false,isFunc);
7584 }
7585 }
7586 else if (root->section.isDefine() && !relates.empty())
7587 {
7588 findMember(root,
7589 relates,
7590 type,
7591 args,
7592 root->name + args,
7593 false,
7594 !args.empty());
7595 }
7596 else if (root->section.isVariableDoc())
7597 {
7598 //printf("Documentation for variable %s found\n",qPrint(root->name));
7599 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7600 findMember(root,
7601 relates,
7602 type,
7603 args,
7604 root->name,
7605 false,
7606 false);
7607 }
7608 else if (root->section.isExportedInterface() ||
7609 root->section.isIncludedService())
7610 {
7611 findMember(root,
7612 relates,
7613 type,
7614 args,
7615 type + " " + root->name,
7616 false,
7617 false);
7618 }
7619 else
7620 {
7621 // skip section
7622 //printf("skip section\n");
7623 }
7624}
7625
7626static void findMemberDocumentation(const Entry *root)
7627{
7628 if (root->section.isMemberDoc() ||
7629 root->section.isOverloadDoc() ||
7630 root->section.isFunction() ||
7631 root->section.isVariable() ||
7632 root->section.isVariableDoc() ||
7633 root->section.isDefine() ||
7634 root->section.isIncludedService() ||
7635 root->section.isExportedInterface()
7636 )
7637 {
7638 AUTO_TRACE();
7639 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7640 {
7642 }
7644 }
7645 for (const auto &e : root->children())
7646 {
7647 if (!e->section.isEnum())
7648 {
7649 findMemberDocumentation(e.get());
7650 }
7651 }
7652}
7653
7654//----------------------------------------------------------------------
7655
7656static void findObjCMethodDefinitions(const Entry *root)
7657{
7658 AUTO_TRACE();
7659 for (const auto &objCImpl : root->children())
7660 {
7661 if (objCImpl->section.isObjcImpl())
7662 {
7663 for (const auto &objCMethod : objCImpl->children())
7664 {
7665 if (objCMethod->section.isFunction())
7666 {
7667 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7668 findMember(objCMethod.get(),
7669 objCMethod->relates,
7670 objCMethod->type,
7671 objCMethod->args,
7672 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7673 false,true);
7674 objCMethod->section=EntryType::makeEmpty();
7675 }
7676 }
7677 }
7678 }
7679}
7680
7681//----------------------------------------------------------------------
7682// find and add the enumeration to their classes, namespaces or files
7683
7684static void findEnums(const Entry *root)
7685{
7686 if (root->section.isEnum())
7687 {
7688 AUTO_TRACE("name={}",root->name);
7689 ClassDefMutable *cd = nullptr;
7690 FileDef *fd = nullptr;
7691 NamespaceDefMutable *nd = nullptr;
7692 MemberNameLinkedMap *mnsd = nullptr;
7693 bool isGlobal = false;
7694 bool isRelated = false;
7695 bool isMemberOf = false;
7696 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7697
7698 DString name;
7699 DString scope;
7700
7701 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7702 {
7703 scope=root->name.left(i); // extract scope
7704 if (root->lang==SrcLangExt::CSharp)
7705 {
7706 scope = mangleCSharpGenericName(scope);
7707 }
7708 name=root->name.right(root->name.length()-i-2); // extract name
7709 if ((cd=getClassMutable(scope))==nullptr)
7710 {
7712 }
7713 }
7714 else // no scope, check the scope in which the docs where found
7715 {
7716 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7717 {
7718 scope=root->parent()->name;
7719 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7720 }
7721 name=root->name;
7722 }
7723
7724 if (!root->relates.empty())
7725 { // related member, prefix user specified scope
7726 isRelated=true;
7727 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7728 if (getClass(root->relates)==nullptr && !scope.empty())
7729 scope=mergeScopes(scope,root->relates);
7730 else
7731 scope=root->relates;
7732 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7733 }
7734
7735 if (cd && !name.empty()) // found a enum inside a compound
7736 {
7737 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7738 fd=nullptr;
7740 isGlobal=false;
7741 }
7742 else if (nd) // found enum inside namespace
7743 {
7745 isGlobal=true;
7746 }
7747 else // found a global enum
7748 {
7749 fd=root->fileDef();
7751 isGlobal=true;
7752 }
7753
7754 if (!name.empty())
7755 {
7756 // new enum type
7757 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7758 auto md = createMemberDef(
7759 root->fileName,root->startLine,root->startColumn,
7760 DString(),name,DString(),DString(),
7761 root->protection,Specifier::Normal,false,
7762 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7763 MemberType::Enumeration,
7765 auto mmd = toMemberDefMutable(md.get());
7766 mmd->setTagInfo(root->tagInfo());
7767 mmd->setLanguage(root->lang);
7768 mmd->setId(root->id);
7769 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7770 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7771 mmd->setBodyDef(root->fileDef());
7772 mmd->setMemberSpecifiers(root->spec);
7773 mmd->setVhdlSpecifiers(root->vhdlSpec);
7774 mmd->setEnumBaseType(root->args);
7775 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7776 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7777 mmd->addSectionsToDefinition(root->anchors);
7778 mmd->setMemberGroupId(root->mGrpId);
7780 mmd->addQualifiers(root->qualifiers);
7781 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7782 mmd->setRefItems(root->sli);
7783 mmd->setRequirementReferences(root->rqli);
7784 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7785 bool defSet=false;
7786 addIncludeFile(mmd,root->fileDef(),root);
7787
7788 DString baseType = root->args;
7789 if (!baseType.empty())
7790 {
7791 baseType.prepend(" : ");
7792 }
7793
7794 if (nd)
7795 {
7796 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7797 {
7798 mmd->setDefinition(name+baseType);
7799 }
7800 else
7801 {
7802 mmd->setDefinition(nd->name()+"::"+name+baseType);
7803 }
7804 //printf("definition=%s\n",md->definition());
7805 defSet=true;
7806 mmd->setNamespace(nd);
7807 nd->insertMember(md.get());
7808 }
7809
7810 // even if we have already added the enum to a namespace, we still
7811 // also want to add it to other appropriate places such as file
7812 // or class.
7813 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7814 {
7815 if (!defSet) mmd->setDefinition(name+baseType);
7816 if (fd==nullptr && root->parent())
7817 {
7818 fd=root->parent()->fileDef();
7819 }
7820 if (fd)
7821 {
7822 mmd->setFileDef(fd);
7823 fd->insertMember(md.get());
7824 }
7825 }
7826 else if (cd)
7827 {
7828 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7829 {
7830 mmd->setDefinition(name+baseType);
7831 }
7832 else
7833 {
7834 mmd->setDefinition(cd->name()+"::"+name+baseType);
7835 }
7836 cd->insertMember(md.get());
7837 cd->insertUsedFile(fd);
7838 }
7839 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7840 mmd->setDocsForDefinition(!root->proto);
7841 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7842 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7843
7844 //printf("Adding member=%s\n",qPrint(md->name()));
7845 addMemberToGroups(root,md.get());
7847
7848 MemberName *mn = mnsd->add(name);
7849 mn->push_back(std::move(md));
7850 }
7851 }
7852 else
7853 {
7854 for (const auto &e : root->children()) findEnums(e.get());
7855 }
7856}
7857
7858//----------------------------------------------------------------------
7859
7860static void addEnumValuesToEnums(const Entry *root)
7861{
7862 if (root->section.isEnum())
7863 // non anonymous enumeration
7864 {
7865 AUTO_TRACE("name={}",root->name);
7866 ClassDefMutable *cd = nullptr;
7867 FileDef *fd = nullptr;
7868 NamespaceDefMutable *nd = nullptr;
7869 MemberNameLinkedMap *mnsd = nullptr;
7870 bool isGlobal = false;
7871 bool isRelated = false;
7872 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7873
7874 DString name;
7875 DString scope;
7876
7877 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7878 {
7879 scope=root->name.left(i); // extract scope
7880 if (root->lang==SrcLangExt::CSharp)
7881 {
7882 scope = mangleCSharpGenericName(scope);
7883 }
7884 name=root->name.right(root->name.length()-i-2); // extract name
7885 if ((cd=getClassMutable(scope))==nullptr)
7886 {
7888 }
7889 }
7890 else // no scope, check the scope in which the docs where found
7891 {
7892 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7893 {
7894 scope=root->parent()->name;
7895 if (root->lang==SrcLangExt::CSharp)
7896 {
7897 scope = mangleCSharpGenericName(scope);
7898 }
7899 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7900 }
7901 name=root->name;
7902 }
7903
7904 if (!root->relates.empty())
7905 { // related member, prefix user specified scope
7906 isRelated=true;
7907 if (getClassMutable(root->relates)==nullptr && !scope.empty())
7908 scope=mergeScopes(scope,root->relates);
7909 else
7910 scope=root->relates;
7911 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7912 }
7913
7914 if (cd && !name.empty()) // found a enum inside a compound
7915 {
7916 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7917 fd=nullptr;
7919 isGlobal=false;
7920 }
7921 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7922 {
7923 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7925 isGlobal=true;
7926 }
7927 else // found a global enum
7928 {
7929 fd=root->fileDef();
7930 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7932 isGlobal=true;
7933 }
7934
7935 if (!name.empty())
7936 {
7937 //printf("** name=%s\n",qPrint(name));
7938 MemberName *mn = mnsd->find(name); // for all members with this name
7939 if (mn)
7940 {
7941 struct EnumValueInfo
7942 {
7943 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
7944 name(n), member(std::move(md)) {}
7945 DString name;
7946 std::unique_ptr<MemberDef> member;
7947 };
7948 std::vector< EnumValueInfo > extraMembers;
7949 // for each enum in this list
7950 for (const auto &imd : *mn)
7951 {
7952 MemberDefMutable *md = toMemberDefMutable(imd.get());
7953 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7954 if (md && md->isEnumerate() && !root->children().empty())
7955 {
7956 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7957 for (const auto &e : root->children())
7958 {
7959 SrcLangExt sle = root->lang;
7960 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7961 if ( isJavaLike || root->spec.isStrong())
7962 {
7963 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7964 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7965 // values are only visible inside the enum scope, so we must create
7966 // them here and only add them to the enum
7967 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7968 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7969 DString qualifiedName = root->name;
7970 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
7971 {
7972 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7973 }
7974 if (isJavaLike)
7975 {
7976 qualifiedName=substitute(qualifiedName,"::",".");
7977 }
7978 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7979 {
7980 DString fileName = e->fileName;
7981 if (fileName.empty() && e->tagInfo())
7982 {
7983 fileName = e->tagInfo()->tagName;
7984 }
7985 AUTO_TRACE_ADD("strong enum value {}",e->name);
7986 auto fmd = createMemberDef(
7987 fileName,e->startLine,e->startColumn,
7988 e->type,e->name,e->args,DString(),
7989 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7990 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7991 auto fmmd = toMemberDefMutable(fmd.get());
7992 NamespaceDef *mnd = md->getNamespaceDef();
7993 if (md->getClassDef())
7994 fmmd->setMemberClass(md->getClassDef());
7995 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7996 fmmd->setNamespace(mnd);
7997 else if (md->getFileDef())
7998 fmmd->setFileDef(md->getFileDef());
7999 fmmd->setOuterScope(md->getOuterScope());
8000 fmmd->setTagInfo(e->tagInfo());
8001 fmmd->setLanguage(e->lang);
8002 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
8003 fmmd->setBodyDef(e->fileDef());
8004 fmmd->setId(e->id);
8005 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
8006 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
8007 fmmd->addSectionsToDefinition(e->anchors);
8008 fmmd->setInitializer(e->initializer.str());
8009 fmmd->setMaxInitLines(e->initLines);
8010 fmmd->setMemberGroupId(e->mGrpId);
8011 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
8012 fmmd->setRefItems(e->sli);
8013 fmmd->setRequirementReferences(e->rqli);
8014 fmmd->setAnchor();
8015 md->insertEnumField(fmd.get());
8016 fmmd->setEnumScope(md,true);
8017 extraMembers.emplace_back(e->name,std::move(fmd));
8018 }
8019 }
8020 else
8021 {
8022 AUTO_TRACE_ADD("enum value {}",e->name);
8023 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
8024 MemberName *fmn=nullptr;
8025 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
8026 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
8027 // get list of members with the same name as the field
8028 {
8029 for (const auto &ifmd : *fmn)
8030 {
8031 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8032 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
8033 {
8034 //printf("found enum value with same name %s in scope %s\n",
8035 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
8036 if (nd && !nd->isAnonymous())
8037 {
8038 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8039 {
8040 const NamespaceDef *fnd=fmd->getNamespaceDef();
8041 if (fnd==nd) // enum value is inside a namespace
8042 {
8043 md->insertEnumField(fmd);
8044 fmd->setEnumScope(md);
8045 }
8046 }
8047 }
8048 else if (isGlobal)
8049 {
8050 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8051 {
8052 const FileDef *ffd=fmd->getFileDef();
8053 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8054 {
8055 md->insertEnumField(fmd);
8056 fmd->setEnumScope(md);
8057 }
8058 }
8059 }
8060 else if (isRelated && cd) // reparent enum value to
8061 // match the enum's scope
8062 {
8063 md->insertEnumField(fmd); // add field def to list
8064 fmd->setEnumScope(md); // cross ref with enum name
8065 fmd->setEnumClassScope(cd); // cross ref with enum name
8066 fmd->setOuterScope(cd);
8067 fmd->makeRelated();
8068 cd->insertMember(fmd);
8069 }
8070 else
8071 {
8072 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8073 {
8074 const ClassDef *fcd=fmd->getClassDef();
8075 if (fcd==cd) // enum value is inside a class
8076 {
8077 //printf("Inserting enum field %s in enum scope %s\n",
8078 // qPrint(fmd->name()),qPrint(md->name()));
8079 md->insertEnumField(fmd); // add field def to list
8080 fmd->setEnumScope(md); // cross ref with enum name
8081 }
8082 }
8083 }
8084 }
8085 }
8086 }
8087 }
8088 }
8089 }
8090 }
8091 // move the newly added members into mn
8092 for (auto &e : extraMembers)
8093 {
8094 MemberName *emn=mnsd->add(e.name);
8095 emn->push_back(std::move(e.member));
8096 }
8097 }
8098 }
8099 }
8100 else
8101 {
8102 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8103 }
8104}
8105
8106//----------------------------------------------------------------------
8107
8108static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8109{
8110 AUTO_TRACE();
8111 // documentation outside a compound overrides the documentation inside it
8112 {
8113 md->setDocumentation(root->doc,root->docFile,root->docLine);
8114 md->setDocsForDefinition(!root->proto);
8115 }
8116
8117 // brief descriptions inside a compound override the documentation
8118 // outside it
8119 {
8120 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8121 }
8122
8123 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8124 {
8126 }
8127
8128 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8129 {
8130 md->setMemberGroupId(root->mGrpId);
8131 }
8132
8134 md->setRefItems(root->sli);
8135 md->setRequirementReferences(root->rqli);
8136
8137 const GroupDef *gd=md->getGroupDef();
8138 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8139 {
8140 addMemberToGroups(root,md);
8141 }
8143}
8144
8145//----------------------------------------------------------------------
8146// Search for the name in the associated groups. If a matching member
8147// definition exists, then add the documentation to it and return true,
8148// otherwise false.
8149
8150static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8151{
8152 for (const auto &g : root->groups)
8153 {
8154 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8155 if (gd)
8156 {
8157 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8158 if (ml)
8159 {
8160 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8161 if (md)
8162 {
8163 addEnumDocs(root,md);
8164 return true;
8165 }
8166 }
8167 }
8168 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8169 {
8170 warn(root->fileName, root->startLine,
8171 "Found non-existing group '{}' for the command '{}', ignoring command",
8172 g.groupname, Grouping::getGroupPriName( g.pri )
8173 );
8174 }
8175 }
8176
8177 return false;
8178}
8179
8180//----------------------------------------------------------------------
8181// find the documentation blocks for the enumerations
8182
8183static void findEnumDocumentation(const Entry *root)
8184{
8185 if (root->section.isEnumDoc() &&
8186 !root->name.empty() &&
8187 root->name.at(0)!='@' // skip anonymous enums
8188 )
8189 {
8190 DString name;
8191 DString scope;
8192 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8193 {
8194 name=root->name.mid(i+2); // extract name
8195 scope=root->name.left(i); // extract scope
8196 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8197 }
8198 else // just the name
8199 {
8200 name=root->name;
8201 }
8202 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8203 {
8204 if (!scope.empty()) scope.prepend("::");
8205 scope.prepend(root->parent()->name);
8206 }
8207 const ClassDef *cd = getClass(scope);
8209 const FileDef *fd = root->fileDef();
8210 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8211 name,scope,root->parent()->name,
8212 cd ? cd->name() : DString("<none>"),
8213 nd ? nd->name() : DString("<none>"),
8214 fd ? fd->name() : DString("<none>"));
8215
8216 if (!name.empty())
8217 {
8218 bool found = tryAddEnumDocsToGroupMember(root, name);
8219 if (!found)
8220 {
8222 if (mn)
8223 {
8224 for (const auto &imd : *mn)
8225 {
8226 MemberDefMutable *md = toMemberDefMutable(imd.get());
8227 if (md && md->isEnumerate())
8228 {
8229 const ClassDef *mcd = md->getClassDef();
8230 const NamespaceDef *mnd = md->getNamespaceDef();
8231 const FileDef *mfd = md->getFileDef();
8232 if (cd && mcd==cd)
8233 {
8234 AUTO_TRACE_ADD("Match found for class scope");
8235 addEnumDocs(root,md);
8236 found = true;
8237 break;
8238 }
8239 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8240 {
8241 AUTO_TRACE_ADD("Match found for namespace scope");
8242 addEnumDocs(root,md);
8243 found = true;
8244 break;
8245 }
8246 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8247 {
8248 AUTO_TRACE_ADD("Match found for global scope");
8249 addEnumDocs(root,md);
8250 found = true;
8251 break;
8252 }
8253 }
8254 }
8255 }
8256 }
8257 if (!found)
8258 {
8259 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8260 }
8261 }
8262 }
8263 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8264}
8265
8266// search for each enum (member or function) in mnl if it has documented
8267// enum values.
8268static void findDEV(const MemberNameLinkedMap &mnsd)
8269{
8270 // for each member name
8271 for (const auto &mn : mnsd)
8272 {
8273 // for each member definition
8274 for (const auto &imd : *mn)
8275 {
8276 MemberDefMutable *md = toMemberDefMutable(imd.get());
8277 if (md && md->isEnumerate()) // member is an enum
8278 {
8279 int documentedEnumValues=0;
8280 // for each enum value
8281 for (const auto &fmd : md->enumFieldList())
8282 {
8283 if (fmd->isLinkableInProject()) documentedEnumValues++;
8284 }
8285 // at least one enum value is documented
8286 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8287 }
8288 }
8289 }
8290}
8291
8292// search for each enum (member or function) if it has documented enum
8293// values.
8299
8300//----------------------------------------------------------------------
8301
8303{
8304 auto &index = Index::instance();
8305 // for each class member name
8306 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8307 {
8308 // for each member definition
8309 for (const auto &md : *mn)
8310 {
8311 index.addClassMemberNameToIndex(md.get());
8312 if (md->getModuleDef())
8313 {
8314 index.addModuleMemberNameToIndex(md.get());
8315 }
8316 }
8317 }
8318 // for each file/namespace function name
8319 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8320 {
8321 // for each member definition
8322 for (const auto &md : *mn)
8323 {
8324 if (md->getNamespaceDef())
8325 {
8326 index.addNamespaceMemberNameToIndex(md.get());
8327 }
8328 else
8329 {
8330 index.addFileMemberNameToIndex(md.get());
8331 }
8332 if (md->getModuleDef())
8333 {
8334 index.addModuleMemberNameToIndex(md.get());
8335 }
8336 }
8337 }
8338
8339 index.sortMemberIndexLists();
8340}
8341
8342//----------------------------------------------------------------------
8343
8344static void addToIndices()
8345{
8346 for (const auto &cd : *Doxygen::classLinkedMap)
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 &cd : *Doxygen::conceptLinkedMap)
8360 {
8361 if (cd->isLinkableInProject())
8362 {
8363 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8364 if (Doxygen::searchIndex.enabled())
8365 {
8366 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8367 Doxygen::searchIndex.addWord(cd->localName(),true);
8368 }
8369 }
8370 }
8371
8372 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8373 {
8374 if (nd->isLinkableInProject())
8375 {
8376 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8377 if (Doxygen::searchIndex.enabled())
8378 {
8379 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8380 Doxygen::searchIndex.addWord(nd->localName(),true);
8381 }
8382 }
8383 }
8384
8385 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8386 {
8387 for (const auto &fd : *fn)
8388 {
8389 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8390 {
8391 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8392 Doxygen::searchIndex.addWord(fd->localName(),true);
8393 }
8394 }
8395 }
8396
8397 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8398 {
8400 if (Doxygen::searchIndex.enabled())
8401 {
8402 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8403 std::string s = title.str();
8404 static const reg::Ex re(R"(\a[\w-]*)");
8405 reg::Iterator it(s,re);
8407 for (; it!=end ; ++it)
8408 {
8409 const auto &match = *it;
8410 std::string matchStr = match.str();
8411 Doxygen::searchIndex.addWord(matchStr,true);
8412 }
8413 }
8414 };
8415
8416 for (const auto &gd : *Doxygen::groupLinkedMap)
8417 {
8418 if (gd->isLinkableInProject())
8419 {
8420 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8421 }
8422 }
8423
8424 for (const auto &pd : *Doxygen::pageLinkedMap)
8425 {
8426 if (pd->isLinkableInProject())
8427 {
8428 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8429 }
8430 }
8431
8433 {
8434 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8435 }
8436
8437 auto addMemberToSearchIndex = [](const MemberDef *md)
8438 {
8439 if (Doxygen::searchIndex.enabled())
8440 {
8441 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8442 DString ln=md->localName();
8443 DString qn=md->qualifiedName();
8445 if (ln!=qn)
8446 {
8448 if (md->getClassDef())
8449 {
8450 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8451 }
8452 if (md->getNamespaceDef())
8453 {
8454 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8455 }
8456 }
8457 }
8458 };
8459
8460 auto getScope = [](const MemberDef *md)
8461 {
8462 const Definition *scope = nullptr;
8463 if (md->getGroupDef()) scope = md->getGroupDef();
8464 else if (md->getClassDef()) scope = md->getClassDef();
8465 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8466 else if (md->getFileDef()) scope = md->getFileDef();
8467 return scope;
8468 };
8469
8470 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8471 {
8472 if (md->isLinkableInProject())
8473 {
8474 if (!(md->isEnumerate() && md->isAnonymous()))
8475 {
8476 Doxygen::indexList->addIndexItem(getScope(md),md);
8477 addMemberToSearchIndex(md);
8478 }
8479 if (md->isEnumerate())
8480 {
8481 for (const auto &fmd : md->enumFieldList())
8482 {
8483 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8484 addMemberToSearchIndex(fmd);
8485 }
8486 }
8487 }
8488 };
8489
8490 // for each class member name
8491 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8492 {
8493 // for each member definition
8494 for (const auto &md : *mn)
8495 {
8496 addMemberToIndices(md.get());
8497 }
8498 }
8499 // for each file/namespace function name
8500 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8501 {
8502 // for each member definition
8503 for (const auto &md : *mn)
8504 {
8505 addMemberToIndices(md.get());
8506 }
8507 }
8508}
8509
8510//----------------------------------------------------------------------
8511
8513{
8514 // for each member name
8515 for (const auto &mn : *Doxygen::memberNameLinkedMap)
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 // for each member name
8528 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8529 {
8530 // for each member definition
8531 for (const auto &imd : *mn)
8532 {
8533 MemberDefMutable *md = toMemberDefMutable(imd.get());
8534 if (md)
8535 {
8537 }
8538 }
8539 }
8540}
8541
8542// recursive helper function looking for reimplements/implemented
8543// by relations between class cd and direct or indirect base class bcd
8545{
8546 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8547 {
8548 for (const auto &imd : *mn) // for each member with a given name
8549 {
8550 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8551 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8552 {
8553 ClassDef *mbcd = bcd->classDef;
8554 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8555 {
8556 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8557 const auto &bmni = bmn.find(mn->memberName());
8558 if (bmni) // there are base class members with the same name
8559 {
8560 for (const auto &ibmd : *bmni) // for base class member with that name
8561 {
8562 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8563 if (bmd) // not part of an inline namespace
8564 {
8565 auto lang = bmd->getLanguage();
8566 auto compType = mbcd->compoundType();
8567 if (bmd->virtualness()!=Specifier::Normal ||
8568 lang==SrcLangExt::Python ||
8569 lang==SrcLangExt::Java ||
8570 lang==SrcLangExt::PHP ||
8571 compType==ClassDef::Interface ||
8572 compType==ClassDef::Protocol)
8573 {
8574 const ArgumentList &bmdAl = bmd->argumentList();
8575 const ArgumentList &mdAl = md->argumentList();
8576 //printf(" Base argList='%s'\n Super argList='%s'\n",
8577 // qPrint(argListToString(bmdAl)),
8578 // qPrint(argListToString(mdAl))
8579 // );
8580 if (
8581 lang==SrcLangExt::Python ||
8582 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8583 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8584 true,lang
8585 )
8586 )
8587 {
8588 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8589 //printf("match!\n");
8590 const MemberDef *rmd = md->reimplements();
8591 if (rmd==nullptr) // not already assigned
8592 {
8593 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8594 md->setReimplements(bmd);
8595 }
8596 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8597 bmd->insertReimplementedBy(md);
8598 }
8599 else
8600 {
8601 //printf("no match!\n");
8602 }
8603 }
8604 }
8605 }
8606 }
8607 }
8608 }
8609 }
8610 }
8611
8612 // do also for indirect base classes
8613 for (const auto &bbcd : bcd->classDef->baseClasses())
8614 {
8616 }
8617}
8618
8619//----------------------------------------------------------------------
8620// computes the relation between all members. For each member 'm'
8621// the members that override the implementation of 'm' are searched and
8622// the member that 'm' overrides is searched.
8623
8625{
8626 for (const auto &cd : *Doxygen::classLinkedMap)
8627 {
8628 if (cd->isLinkable())
8629 {
8630 for (const auto &bcd : cd->baseClasses())
8631 {
8633 }
8634 }
8635 }
8636}
8637
8638//----------------------------------------------------------------------------
8639
8641{
8642 // for each class
8643 for (const auto &cd : *Doxygen::classLinkedMap)
8644 {
8645 // that is a template
8646 for (const auto &ti : cd->getTemplateInstances())
8647 {
8648 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8649 if (tcdm)
8650 {
8651 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8652 }
8653 }
8654 }
8655}
8656
8657//----------------------------------------------------------------------------
8658
8659static void mergeCategories()
8660{
8661 AUTO_TRACE();
8662 // merge members of categories into the class they extend
8663 for (const auto &cd : *Doxygen::classLinkedMap)
8664 {
8665 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8666 {
8667 DString baseName=cd->name().left(i);
8668 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8669 if (baseClass)
8670 {
8671 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8672 baseClass->mergeCategory(cd.get());
8673 }
8674 }
8675 }
8676}
8677
8678// builds the list of all members for each class
8679
8681{
8682 // merge the member list of base classes into the inherited classes.
8683 for (const auto &cd : *Doxygen::classLinkedMap)
8684 {
8685 if (// !cd->isReference() && // not an external class
8686 cd->subClasses().empty() && // is a root of the hierarchy
8687 !cd->baseClasses().empty()) // and has at least one base class
8688 {
8689 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8690 if (cdm)
8691 {
8692 //printf("*** merging members for %s\n",qPrint(cd->name()));
8693 cdm->mergeMembers();
8694 }
8695 }
8696 }
8697 // now sort the member list of all members for all classes.
8698 for (const auto &cd : *Doxygen::classLinkedMap)
8699 {
8700 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8701 if (cdm)
8702 {
8703 cdm->sortAllMembersList();
8704 }
8705 }
8706}
8707
8708//----------------------------------------------------------------------------
8709
8711{
8712 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8713 {
8714 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8715 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8716 if (showSources)
8717 {
8718 msg("Generating code for file {}...\n",fd->docName());
8719 fd->writeSourceHeader(ol);
8720 fd->writeSourceBody(ol,parser);
8721 fd->writeSourceFooter(ol);
8722 }
8723 else if (parseSources)
8724 {
8725 msg("Parsing code for file {}...\n",fd->docName());
8726 fd->parseSource(parser);
8727 }
8728 };
8729 if (!Doxygen::inputNameLinkedMap->empty())
8730 {
8731#if USE_LIBCLANG
8733 {
8734 StringUnorderedSet processedFiles;
8735
8736 // create a dictionary with files to process
8737 StringUnorderedSet filesToProcess;
8738
8739 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8740 {
8741 for (const auto &fd : *fn)
8742 {
8743 filesToProcess.insert(fd->absFilePath().str());
8744 }
8745 }
8746 // process source files (and their include dependencies)
8747 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8748 {
8749 for (const auto &fd : *fn)
8750 {
8751 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8752 (fd->generateSourceFile() ||
8754 )
8755 )
8756 {
8757 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8758 clangParser->parse();
8759 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8760
8761 for (auto incFile : clangParser->filesInSameTU())
8762 {
8763 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8764 fd->absFilePath()!=incFile && // not same file
8765 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8766 {
8767 StringVector moreFiles;
8768 bool ambig = false;
8769 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
8770 if (ifd && !ifd->isReference())
8771 {
8772 processSourceFile(ifd,*g_outputList,clangParser.get());
8773 processedFiles.insert(incFile);
8774 }
8775 }
8776 }
8777 processedFiles.insert(fd->absFilePath().str());
8778 }
8779 }
8780 }
8781 // process remaining files
8782 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8783 {
8784 for (const auto &fd : *fn)
8785 {
8786 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8787 {
8788 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8789 {
8790 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8791 clangParser->parse();
8792 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8793 }
8794 else // non C/C++ file, use built-in parser
8795 {
8796 processSourceFile(fd.get(),*g_outputList,nullptr);
8797 }
8798 }
8799 }
8800 }
8801 }
8802 else
8803#endif
8804 {
8805 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8806 if (numThreads>1)
8807 {
8808 msg("Generating code files using {} threads.\n",numThreads);
8809 struct SourceContext
8810 {
8811 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8812 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8813 FileDef *fd;
8814 bool generateSourceFile;
8815 OutputList ol;
8816 };
8817 ThreadPool threadPool(numThreads);
8818 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8819 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8820 {
8821 for (const auto &fd : *fn)
8822 {
8823 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8824 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8825 auto processFile = [ctx]()
8826 {
8827 if (ctx->generateSourceFile)
8828 {
8829 msg("Generating code for file {}...\n",ctx->fd->docName());
8830 }
8831 else
8832 {
8833 msg("Parsing code for file {}...\n",ctx->fd->docName());
8834 }
8835 StringVector filesInSameTu;
8836 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8837 if (ctx->generateSourceFile) // sources need to be shown in the output
8838 {
8839 ctx->fd->writeSourceHeader(ctx->ol);
8840 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8841 ctx->fd->writeSourceFooter(ctx->ol);
8842 }
8843 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8844 // we needed to parse the sources even if we do not show them
8845 {
8846 ctx->fd->parseSource(nullptr);
8847 }
8848 return ctx;
8849 };
8850 results.emplace_back(threadPool.queue(processFile));
8851 }
8852 }
8853 for (auto &f : results)
8854 {
8855 auto ctx = f.get();
8856 }
8857 }
8858 else // single threaded version
8859 {
8860 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8861 {
8862 for (const auto &fd : *fn)
8863 {
8864 StringVector filesInSameTu;
8865 fd->getAllIncludeFilesRecursively(filesInSameTu);
8866 processSourceFile(fd.get(),*g_outputList,nullptr);
8867 }
8868 }
8869 }
8870 }
8871 }
8872}
8873
8874//----------------------------------------------------------------------------
8875
8876static void generateFileDocs()
8877{
8878 if (Index::instance().numDocumentedFiles()==0) return;
8879
8880 if (!Doxygen::inputNameLinkedMap->empty())
8881 {
8882 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8883 if (numThreads>1) // multi threaded processing
8884 {
8885 struct DocContext
8886 {
8887 DocContext(FileDef *fd_,const OutputList &ol_)
8888 : fd(fd_), ol(ol_) {}
8889 FileDef *fd;
8890 OutputList ol;
8891 };
8892 ThreadPool threadPool(numThreads);
8893 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8894 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8895 {
8896 for (const auto &fd : *fn)
8897 {
8898 bool doc = fd->isLinkableInProject();
8899 if (doc)
8900 {
8901 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8902 auto processFile = [ctx]() {
8903 msg("Generating docs for file {}...\n",ctx->fd->docName());
8904 ctx->fd->writeDocumentation(ctx->ol);
8905 return ctx;
8906 };
8907 results.emplace_back(threadPool.queue(processFile));
8908 }
8909 }
8910 }
8911 for (auto &f : results)
8912 {
8913 auto ctx = f.get();
8914 }
8915 }
8916 else // single threaded processing
8917 {
8918 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8919 {
8920 for (const auto &fd : *fn)
8921 {
8922 bool doc = fd->isLinkableInProject();
8923 if (doc)
8924 {
8925 msg("Generating docs for file {}...\n",fd->docName());
8926 fd->writeDocumentation(*g_outputList);
8927 }
8928 }
8929 }
8930 }
8931 }
8932}
8933
8934//----------------------------------------------------------------------------
8935
8937{
8938 // add source references for class definitions
8939 for (const auto &cd : *Doxygen::classLinkedMap)
8940 {
8941 const FileDef *fd=cd->getBodyDef();
8942 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8943 {
8944 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8945 }
8946 }
8947 // add source references for concept definitions
8948 for (const auto &cd : *Doxygen::conceptLinkedMap)
8949 {
8950 const FileDef *fd=cd->getBodyDef();
8951 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8952 {
8953 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8954 }
8955 }
8956 // add source references for namespace definitions
8957 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8958 {
8959 const FileDef *fd=nd->getBodyDef();
8960 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8961 {
8962 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8963 }
8964 }
8965
8966 // add source references for member names
8967 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8968 {
8969 for (const auto &md : *mn)
8970 {
8971 //printf("class member %s: def=%s body=%d link?=%d\n",
8972 // qPrint(md->name()),
8973 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8974 // md->getStartBodyLine(),md->isLinkableInProject());
8975 const FileDef *fd=md->getBodyDef();
8976 if (fd &&
8977 md->getStartDefLine()!=-1 &&
8978 md->isLinkableInProject() &&
8980 )
8981 {
8982 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8983 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8984 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8985 }
8986 }
8987 }
8988 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8989 {
8990 for (const auto &md : *mn)
8991 {
8992 const FileDef *fd=md->getBodyDef();
8993 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8994 // qPrint(md->name()),
8995 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8996 // md->isLinkableInProject(),
8997 // Doxygen::parseSourcesNeeded);
8998 if (fd &&
8999 md->getStartDefLine()!=-1 &&
9000 md->isLinkableInProject() &&
9002 )
9003 {
9004 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
9005 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
9006 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
9007 }
9008 }
9009 }
9010}
9011
9012//----------------------------------------------------------------------------
9013
9014// add the macro definitions found during preprocessing as file members
9015static void buildDefineList()
9016{
9017 AUTO_TRACE();
9018 for (const auto &s : g_inputFiles)
9019 {
9020 auto it = Doxygen::macroDefinitions.find(s);
9022 {
9023 for (const auto &def : it->second)
9024 {
9025 auto md = createMemberDef(
9026 def.fileName,def.lineNr,def.columnNr,
9027 "#define",def.name,def.args,DString(),
9028 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9029 ArgumentList(),ArgumentList(),"");
9030 auto mmd = toMemberDefMutable(md.get());
9031
9032 if (!def.args.empty())
9033 {
9034 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
9035 }
9036 mmd->setInitializer(def.definition);
9037 mmd->setFileDef(def.fileDef);
9038 mmd->setDefinition("#define "+def.name);
9039
9041 if (def.fileDef)
9042 {
9043 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9044 if (defMl)
9045 {
9046 const MemberDef *defMd = defMl->findRev(def.name);
9047 if (defMd) // definition already stored
9048 {
9049 mmd->setRedefineCount(defMd->redefineCount()+1);
9050 }
9051 }
9052 def.fileDef->insertMember(md.get());
9053 }
9054 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9055 mn->push_back(std::move(md));
9056 }
9057 }
9058 }
9059}
9060
9061//----------------------------------------------------------------------------
9062
9063static void sortMemberLists()
9064{
9065 // sort class member lists
9066 for (const auto &cd : *Doxygen::classLinkedMap)
9067 {
9068 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9069 if (cdm)
9070 {
9071 cdm->sortMemberLists();
9072 }
9073 }
9074
9075 // sort namespace member lists
9076 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9077 {
9079 if (ndm)
9080 {
9081 ndm->sortMemberLists();
9082 }
9083 }
9084
9085 // sort file member lists
9086 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9087 {
9088 for (const auto &fd : *fn)
9089 {
9090 fd->sortMemberLists();
9091 }
9092 }
9093
9094 // sort group member lists
9095 for (const auto &gd : *Doxygen::groupLinkedMap)
9096 {
9097 gd->sortMemberLists();
9098 }
9099
9101}
9102
9103//----------------------------------------------------------------------------
9104
9105static bool isSymbolHidden(const Definition *d)
9106{
9107 bool hidden = d->isHidden();
9108 const Definition *parent = d->getOuterScope();
9109 return parent ? hidden || isSymbolHidden(parent) : hidden;
9110}
9111
9113{
9114 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9115 if (numThreads>1)
9116 {
9117 ThreadPool threadPool(numThreads);
9118 std::vector < std::future< void > > results;
9119 // queue the work
9120 for (const auto &[name,symList] : *Doxygen::symbolMap)
9121 {
9122 for (const auto &def : symList)
9123 {
9125 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9126 {
9127 auto processTooltip = [dm]() {
9128 dm->computeTooltip();
9129 };
9130 results.emplace_back(threadPool.queue(processTooltip));
9131 }
9132 }
9133 }
9134 // wait for the results
9135 for (auto &f : results)
9136 {
9137 f.get();
9138 }
9139 }
9140 else
9141 {
9142 for (const auto &[name,symList] : *Doxygen::symbolMap)
9143 {
9144 for (const auto &def : symList)
9145 {
9147 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9148 {
9149 dm->computeTooltip();
9150 }
9151 }
9152 }
9153 }
9154}
9155
9156//----------------------------------------------------------------------------
9157
9159{
9160 for (const auto &cd : *Doxygen::classLinkedMap)
9161 {
9162 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9163 if (cdm)
9164 {
9165 cdm->setAnonymousEnumType();
9166 }
9167 }
9168}
9169
9170//----------------------------------------------------------------------------
9171
9172static void countMembers()
9173{
9174 for (const auto &cd : *Doxygen::classLinkedMap)
9175 {
9176 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9177 if (cdm)
9178 {
9179 cdm->countMembers();
9180 }
9181 }
9182
9183 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9184 {
9186 if (ndm)
9187 {
9188 ndm->countMembers();
9189 }
9190 }
9191
9192 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9193 {
9194 for (const auto &fd : *fn)
9195 {
9196 fd->countMembers();
9197 }
9198 }
9199
9200 for (const auto &gd : *Doxygen::groupLinkedMap)
9201 {
9202 gd->countMembers();
9203 }
9204
9205 auto &mm = ModuleManager::instance();
9206 mm.countMembers();
9207}
9208
9209
9210//----------------------------------------------------------------------------
9211// generate the documentation for all classes
9212
9213static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9214{
9215 AUTO_TRACE();
9216 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9217 if (numThreads>1) // multi threaded processing
9218 {
9219 struct DocContext
9220 {
9221 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9222 : cd(cd_), ol(ol_) {}
9223 ClassDefMutable *cd;
9224 OutputList ol;
9225 };
9226 ThreadPool threadPool(numThreads);
9227 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9228 for (const auto &cd : classList)
9229 {
9230 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9231 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9232 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9233 )
9234 {
9235 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9236 auto processFile = [ctx]()
9237 {
9238 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9239
9240 // skip external references, anonymous compounds and
9241 // template instances
9242 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9243 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9244 {
9245 ctx->cd->writeDocumentation(ctx->ol);
9246 ctx->cd->writeMemberList(ctx->ol);
9247 }
9248
9249 // even for undocumented classes, the inner classes can be documented.
9250 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9251 return ctx;
9252 };
9253 results.emplace_back(threadPool.queue(processFile));
9254 }
9255 }
9256 for (auto &f : results)
9257 {
9258 auto ctx = f.get();
9259 }
9260 }
9261 else // single threaded processing
9262 {
9263 for (const auto &cd : classList)
9264 {
9265 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9266 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9267 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9268 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9269 )
9270 {
9271 // skip external references, anonymous compounds and
9272 // template instances
9273 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9274 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9275 {
9276 msg("Generating docs for compound {}...\n",cd->displayName());
9277
9278 cd->writeDocumentation(*g_outputList);
9279 cd->writeMemberList(*g_outputList);
9280 }
9281 // even for undocumented classes, the inner classes can be documented.
9282 cd->writeDocumentationForInnerClasses(*g_outputList);
9283 }
9284 }
9285 }
9286}
9287
9288static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9289{
9290 list.push_back(cd);
9291 for (const auto &innerCdi : cd->getClasses())
9292 {
9293 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9294 if (innerCd)
9295 {
9296 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9297 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9298 innerCd->isEmbeddedInOuterScope());
9299 }
9300 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9301 protectionLevelVisible(innerCd->protection()) &&
9302 !innerCd->isEmbeddedInOuterScope()
9303 )
9304 {
9305 list.push_back(innerCd);
9306 addClassAndNestedClasses(list,innerCd);
9307 }
9308 }
9309}
9310
9312{
9313 std::vector<ClassDefMutable*> classList;
9314 for (const auto &cdi : *Doxygen::classLinkedMap)
9315 {
9316 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9317 if (cd && (cd->getOuterScope()==nullptr ||
9319 {
9320 addClassAndNestedClasses(classList,cd);
9321 }
9322 }
9323 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9324 {
9325 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9326 if (cd && (cd->getOuterScope()==nullptr ||
9328 {
9329 addClassAndNestedClasses(classList,cd);
9330 }
9331 }
9332 generateDocsForClassList(classList);
9333}
9334
9335//----------------------------------------------------------------------------
9336
9338{
9339 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9340 {
9342
9343 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9344 if (cd &&
9345 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9346 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9347 ) && !cd->isHidden() && cd->isLinkableInProject()
9348 )
9349 {
9350 msg("Generating docs for concept {}...\n",cd->displayName());
9352 }
9353 }
9354}
9355
9356//----------------------------------------------------------------------------
9357
9359{
9360 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9361 {
9362 for (const auto &imd : *mn)
9363 {
9364 MemberDefMutable *md = toMemberDefMutable(imd.get());
9365 //static int count=0;
9366 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9367 if (md && md->documentation().empty() && md->briefDescription().empty())
9368 { // no documentation yet
9369 const MemberDef *bmd = md->reimplements();
9370 while (bmd && bmd->documentation().empty() &&
9371 bmd->briefDescription().empty()
9372 )
9373 { // search up the inheritance tree for a documentation member
9374 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9375 bmd = bmd->reimplements();
9376 }
9377 if (bmd) // copy the documentation from the reimplemented member
9378 {
9379 md->setInheritsDocsFrom(bmd);
9380 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9382 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9383 md->copyArgumentNames(bmd);
9385 }
9386 }
9387 }
9388 }
9389}
9390
9391//----------------------------------------------------------------------------
9392
9394{
9395 // for each file
9396 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9397 {
9398 for (const auto &fd : *fn)
9399 {
9400 fd->combineUsingRelations();
9401 }
9402 }
9403
9404 // for each namespace
9405 NamespaceDefSet visitedNamespaces;
9406 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9407 {
9409 if (ndm)
9410 {
9411 ndm->combineUsingRelations(visitedNamespaces);
9412 }
9413 }
9414}
9415
9416//----------------------------------------------------------------------------
9417
9419{
9420 // for each class
9421 for (const auto &cd : *Doxygen::classLinkedMap)
9422 {
9423 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9424 if (cdm)
9425 {
9427 }
9428 }
9429 // for each file
9430 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9431 {
9432 for (const auto &fd : *fn)
9433 {
9434 fd->addMembersToMemberGroup();
9435 }
9436 }
9437 // for each namespace
9438 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9439 {
9441 if (ndm)
9442 {
9444 }
9445 }
9446 // for each group
9447 for (const auto &gd : *Doxygen::groupLinkedMap)
9448 {
9449 gd->addMembersToMemberGroup();
9450 }
9452}
9453
9454//----------------------------------------------------------------------------
9455
9457{
9458 // for each class
9459 for (const auto &cd : *Doxygen::classLinkedMap)
9460 {
9461 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9462 if (cdm)
9463 {
9465 }
9466 }
9467 // for each file
9468 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9469 {
9470 for (const auto &fd : *fn)
9471 {
9472 fd->distributeMemberGroupDocumentation();
9473 }
9474 }
9475 // for each namespace
9476 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9477 {
9479 if (ndm)
9480 {
9482 }
9483 }
9484 // for each group
9485 for (const auto &gd : *Doxygen::groupLinkedMap)
9486 {
9487 gd->distributeMemberGroupDocumentation();
9488 }
9490}
9491
9492//----------------------------------------------------------------------------
9493
9495{
9496 // for each class
9497 for (const auto &cd : *Doxygen::classLinkedMap)
9498 {
9499 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9500 if (cdm)
9501 {
9503 }
9504 }
9505 // for each concept
9506 for (const auto &cd : *Doxygen::conceptLinkedMap)
9507 {
9508 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9509 if (cdm)
9510 {
9512 }
9513 }
9514 // for each file
9515 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9516 {
9517 for (const auto &fd : *fn)
9518 {
9519 fd->findSectionsInDocumentation();
9520 }
9521 }
9522 // for each namespace
9523 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9524 {
9526 if (ndm)
9527 {
9529 }
9530 }
9531 // for each group
9532 for (const auto &gd : *Doxygen::groupLinkedMap)
9533 {
9534 gd->findSectionsInDocumentation();
9535 }
9536 // for each page
9537 for (const auto &pd : *Doxygen::pageLinkedMap)
9538 {
9539 pd->findSectionsInDocumentation();
9540 }
9541 // for each directory
9542 for (const auto &dd : *Doxygen::dirLinkedMap)
9543 {
9544 dd->findSectionsInDocumentation();
9545 }
9547 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9548}
9549
9550//----------------------------------------------------------------------
9551
9552
9554{
9555 // remove all references to classes from the cache
9556 // as there can be new template instances in the inheritance path
9557 // to this class. Optimization: only remove those classes that
9558 // have inheritance instances as direct or indirect sub classes.
9560
9561 // remove all cached typedef resolutions whose target is a
9562 // template class as this may now be a template instance
9563 // for each global function name
9564 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9565 {
9566 // for each function with that name
9567 for (const auto &ifmd : *fn)
9568 {
9569 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9570 if (fmd && fmd->isTypedefValCached())
9571 {
9572 const ClassDef *cd = fmd->getCachedTypedefVal();
9573 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9574 }
9575 }
9576 }
9577 // for each class method name
9578 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9579 {
9580 // for each function with that name
9581 for (const auto &imd : *nm)
9582 {
9583 MemberDefMutable *md = toMemberDefMutable(imd.get());
9584 if (md && md->isTypedefValCached())
9585 {
9586 const ClassDef *cd = md->getCachedTypedefVal();
9587 if (cd->isTemplate()) md->invalidateTypedefValCache();
9588 }
9589 }
9590 }
9591}
9592
9593//----------------------------------------------------------------------------
9594
9596{
9597 // Remove all unresolved references to classes from the cache.
9598 // This is needed before resolving the inheritance relations, since
9599 // it would otherwise not find the inheritance relation
9600 // for C in the example below, as B::I was already found to be unresolvable
9601 // (which is correct if you ignore the inheritance relation between A and B).
9602 //
9603 // class A { class I {} };
9604 // class B : public A {};
9605 // class C : public B::I {};
9607
9608 // for each class method name
9609 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9610 {
9611 // for each function with that name
9612 for (const auto &imd : *nm)
9613 {
9614 MemberDefMutable *md = toMemberDefMutable(imd.get());
9615 if (md)
9616 {
9618 }
9619 }
9620 }
9621
9622}
9623
9624//----------------------------------------------------------------------------
9625// Returns true if the entry and member definition have equal file names,
9626// otherwise false.
9627
9628static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9629{
9630 if (const FileDef *fd = md->getFileDef())
9631 {
9632 return fd->absFilePath() == root->fileName;
9633 }
9634 return false;
9635}
9636
9637//----------------------------------------------------------------------------
9638
9639static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9640{
9641 md->setDocumentation(root->doc,root->docFile,root->docLine);
9642 md->setDocsForDefinition(!root->proto);
9643 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9644 if (md->inbodyDocumentation().empty())
9645 {
9647 }
9648 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9649 {
9650 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9651 md->setBodyDef(root->fileDef());
9652 }
9654 md->setMaxInitLines(root->initLines);
9656 md->setRefItems(root->sli);
9657 md->setRequirementReferences(root->rqli);
9658 md->addQualifiers(root->qualifiers);
9659 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9660 addMemberToGroups(root,md);
9662}
9663
9664//----------------------------------------------------------------------------
9665
9667{
9668 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9669 {
9670 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9671 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9672
9673 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9674 {
9675 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9676 "#define",root->name,root->args,DString(),
9677 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9678 ArgumentList(),ArgumentList(),"");
9679 auto mmd = toMemberDefMutable(md.get());
9680 mmd->setTagInfo(root->tagInfo());
9681 mmd->setLanguage(root->lang);
9682 mmd->addQualifiers(root->qualifiers);
9683 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9684 mmd->setFileDef(root->parent()->fileDef());
9685 //printf("Adding member=%s\n",qPrint(md->name()));
9687 mn->push_back(std::move(md));
9688 }
9690 if (mn)
9691 {
9692 int count=0;
9693 for (const auto &md : *mn)
9694 {
9695 if (md->memberType()==MemberType::Define) count++;
9696 }
9697 if (count==1)
9698 {
9699 for (const auto &imd : *mn)
9700 {
9701 MemberDefMutable *md = toMemberDefMutable(imd.get());
9702 if (md && md->memberType()==MemberType::Define)
9703 {
9704 addDefineDoc(root,md);
9705 }
9706 }
9707 }
9708 else if (count>1 &&
9709 (!root->doc.empty() ||
9710 !root->brief.empty() ||
9711 root->bodyLine!=-1
9712 )
9713 )
9714 // multiple defines don't know where to add docs
9715 // but maybe they are in different files together with their documentation
9716 {
9717 for (const auto &imd : *mn)
9718 {
9719 MemberDefMutable *md = toMemberDefMutable(imd.get());
9720 if (md && md->memberType()==MemberType::Define)
9721 {
9722 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9723 // doc and define in the same file or group assume they belong together.
9724 {
9725 addDefineDoc(root,md);
9726 }
9727 }
9728 }
9729 //warn("define {} found in the following files:\n",root->name);
9730 //warn("Cannot determine where to add the documentation found "
9731 // "at line {} of file {}. \n",
9732 // root->startLine,root->fileName);
9733 }
9734 }
9735 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9736 {
9737 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9738 if (preEnabled)
9739 {
9740 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9741 }
9742 else
9743 {
9744 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9745 }
9746 }
9747 }
9748 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9749}
9750
9751//----------------------------------------------------------------------------
9752
9753static void findDirDocumentation(const Entry *root)
9754{
9755 if (root->section.isDirDoc())
9756 {
9757 DString normalizedName = root->name;
9758 normalizedName = substitute(normalizedName,"\\","/");
9759 //printf("root->docFile=%s normalizedName=%s\n",
9760 // qPrint(root->docFile),qPrint(normalizedName));
9761 if (root->docFile==normalizedName) // current dir?
9762 {
9763 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9764 {
9765 normalizedName=normalizedName.left(lastSlashPos);
9766 }
9767 }
9768 if (normalizedName.at(normalizedName.length()-1)!='/')
9769 {
9770 normalizedName+='/';
9771 }
9772 DirDef *matchingDir=nullptr;
9773 for (const auto &dir : *Doxygen::dirLinkedMap)
9774 {
9775 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9776 if (dir->name().right(normalizedName.length())==normalizedName)
9777 {
9778 if (matchingDir)
9779 {
9780 warn(root->fileName,root->startLine,
9781 "\\dir command matches multiple directories.\n"
9782 " Applying the command for directory {}\n"
9783 " Ignoring the command for directory {}",
9784 matchingDir->name(),dir->name()
9785 );
9786 }
9787 else
9788 {
9789 matchingDir=dir.get();
9790 }
9791 }
9792 }
9793 if (matchingDir)
9794 {
9795 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9796 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9797 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9798 matchingDir->setRefItems(root->sli);
9799 matchingDir->setRequirementReferences(root->rqli);
9800 matchingDir->addSectionsToDefinition(root->anchors);
9801 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9802 addDirToGroups(root,matchingDir);
9803 }
9804 else
9805 {
9806 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9807 }
9808 }
9809 for (const auto &e : root->children()) findDirDocumentation(e.get());
9810}
9811
9812//----------------------------------------------------------------------------
9814{
9815 if (root->section.isRequirementDoc())
9816 {
9818 }
9819 for (const auto &e : root->children()) buildRequirementsList(e.get());
9820}
9821
9822//----------------------------------------------------------------------------
9823// create a (sorted) list of separate documentation pages
9824
9825static void buildPageList(Entry *root)
9826{
9827 if (root->section.isPageDoc())
9828 {
9829 if (!root->name.empty())
9830 {
9831 addRelatedPage(root);
9832 }
9833 }
9834 else if (root->section.isMainpageDoc())
9835 {
9836 DString title=root->args.stripWhiteSpace();
9837 if (title.empty()) title=theTranslator->trMainPage();
9838 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9839 DString name = "index";
9840 addRefItem(root->sli,
9841 name,
9842 theTranslator->trPage(true,true),
9843 name,
9844 title,
9845 DString(),nullptr
9846 );
9847 }
9848 for (const auto &e : root->children()) buildPageList(e.get());
9849}
9850
9851// search for the main page defined in this project
9852static void findMainPage(Entry *root)
9853{
9854 if (root->section.isMainpageDoc())
9855 {
9856 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9857 {
9858 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9859 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9860 DString title=root->args.stripWhiteSpace();
9861 if (title.empty()) title = Config_getString(PROJECT_NAME);
9862 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9863 DString indexName="index";
9865 indexName, root->brief+root->doc+root->inbodyDocs,title);
9866 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9867 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9868 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9869 Doxygen::mainPage->setFileName(indexName);
9870 Doxygen::mainPage->setLocalToc(root->localToc);
9872
9874 if (si)
9875 {
9876 if (!si->ref().empty()) // we are from a tag file
9877 {
9878 // a page name is a label as well! but should no be double either
9880 Doxygen::mainPage->name(),
9881 indexName,
9882 root->startLine,
9883 Doxygen::mainPage->title(),
9885 0); // level 0
9886 }
9887 else if (si->lineNr() != -1)
9888 {
9889 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9890 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9891 }
9892 else
9893 {
9894 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9895 Doxygen::mainPage->name(),si->fileName());
9896 }
9897 }
9898 else
9899 {
9900 // a page name is a label as well! but should no be double either
9902 Doxygen::mainPage->name(),
9903 indexName,
9904 root->startLine,
9905 Doxygen::mainPage->title(),
9907 0); // level 0
9908 }
9909 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9910 }
9911 else if (root->tagInfo()==nullptr)
9912 {
9913 warn(root->fileName,root->startLine,
9914 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9915 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9916 }
9917 }
9918 for (const auto &e : root->children()) findMainPage(e.get());
9919}
9920
9921// search for the main page imported via tag files and add only the section labels
9922static void findMainPageTagFiles(Entry *root)
9923{
9924 if (root->section.isMainpageDoc())
9925 {
9926 if (Doxygen::mainPage && root->tagInfo())
9927 {
9928 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9929 }
9930 }
9931 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9932}
9933
9934static void computePageRelations(Entry *root)
9935{
9936 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
9937 {
9938 PageDef *pd = root->section.isPageDoc() ?
9940 Doxygen::mainPage.get();
9941 if (pd)
9942 {
9943 for (const BaseInfo &bi : root->extends)
9944 {
9946 if (pd==subPd)
9947 {
9948 term("page defined {} with label {} is a direct "
9949 "subpage of itself! Please remove this cyclic dependency.\n",
9950 warn_line(pd->docFile(),pd->docLine()),pd->name());
9951 }
9952 else if (subPd)
9953 {
9954 pd->addInnerCompound(subPd);
9955 //printf("*** Added subpage relation: %s->%s\n",
9956 // qPrint(pd->name()),qPrint(subPd->name()));
9957 }
9958 }
9959 }
9960 }
9961 for (const auto &e : root->children()) computePageRelations(e.get());
9962}
9963
9965{
9966 for (const auto &pd : *Doxygen::pageLinkedMap)
9967 {
9968 Definition *ppd = pd->getOuterScope();
9969 while (ppd)
9970 {
9971 if (ppd==pd.get())
9972 {
9973 term("page defined {} with label {} is a subpage "
9974 "of itself! Please remove this cyclic dependency.\n",
9975 warn_line(pd->docFile(),pd->docLine()),pd->name());
9976 }
9977 ppd=ppd->getOuterScope();
9978 }
9979 }
9980}
9981
9982//----------------------------------------------------------------------------
9983
9985{
9986 for (const auto &si : SectionManager::instance())
9987 {
9988 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9989 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9990 // qPrint(si->fileName));
9991 PageDef *pd=nullptr;
9992
9993 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9994 // different files, so the resulting section's all have the wrong file
9995 // name (not from the todo/test/bug/deprecated list, but from the file in
9996 // which they are defined). We correct this here by looking at the
9997 // generated section labels!
9999 {
10000 DString label="_"+rl->listName(); // "_todo", "_test", ...
10001 if (si->label().left(label.length())==label)
10002 {
10003 si->setFileName(rl->listName());
10004 si->setGenerated(true);
10005 break;
10006 }
10007 }
10008
10009 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10010 if (!si->generated())
10011 {
10012 // if this section is in a page and the page is in a group, then we
10013 // have to adjust the link file name to point to the group.
10014 if (!si->fileName().empty() &&
10015 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
10016 pd->getGroupDef())
10017 {
10018 si->setFileName(pd->getGroupDef()->getOutputFileBase());
10019 }
10020
10021 if (si->definition())
10022 {
10023 // TODO: there should be one function in Definition that returns
10024 // the file to link to, so we can avoid the following tests.
10025 const GroupDef *gd=nullptr;
10026 if (si->definition()->definitionType()==Definition::TypeMember)
10027 {
10028 gd = (toMemberDef(si->definition()))->getGroupDef();
10029 }
10030
10031 if (gd)
10032 {
10033 si->setFileName(gd->getOutputFileBase());
10034 }
10035 else
10036 {
10037 //si->fileName=si->definition->getOutputFileBase();
10038 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
10039 }
10040 }
10041 }
10042 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10043 }
10044}
10045
10046
10047
10048//----------------------------------------------------------------------------
10049// generate all separate documentation pages
10050
10051
10052static void generatePageDocs()
10053{
10054 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10055 if (Index::instance().numDocumentedPages()==0) return;
10056 for (const auto &pd : *Doxygen::pageLinkedMap)
10057 {
10058 if (!pd->getGroupDef() && !pd->isReference())
10059 {
10060 msg("Generating docs for page {}...\n",pd->name());
10061 pd->writeDocumentation(*g_outputList);
10062 }
10063 }
10064}
10065
10066//----------------------------------------------------------------------------
10067// create a (sorted) list & dictionary of example pages
10068
10069static void buildExampleList(Entry *root)
10070{
10071 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10072 {
10073 if (Doxygen::exampleLinkedMap->find(root->name))
10074 {
10075 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10076 }
10077 else
10078 {
10080 createPageDef(root->fileName,root->startLine,
10081 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10082 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10083 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10085 pd->setLanguage(root->lang);
10086 pd->setShowLineNo(root->section.isExampleLineno());
10087
10088 //we don't add example to groups
10089 //addExampleToGroups(root,pd);
10090 }
10091 }
10092 for (const auto &e : root->children()) buildExampleList(e.get());
10093}
10094
10095//----------------------------------------------------------------------------
10096// prints the Entry tree (for debugging)
10097
10098void printNavTree(Entry *root,int indent)
10099{
10101 {
10102 DString indentStr;
10103 indentStr.fill(' ',indent);
10104 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10105 indentStr.empty()?"":indentStr,
10106 root->name.empty()?"<empty>":root->name,
10107 root->fileName,root->startLine,
10108 root->section.to_string(),
10109 root->spec.to_string());
10110 for (const auto &e : root->children())
10111 {
10112 printNavTree(e.get(),indent+2);
10113 }
10114 }
10115}
10116
10117
10118//----------------------------------------------------------------------------
10119// prints the Sections tree (for debugging)
10120
10122{
10124 {
10125 for (const auto &si : SectionManager::instance())
10126 {
10127 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10128 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10129 }
10130 }
10131}
10132
10133
10134//----------------------------------------------------------------------------
10135// generate the example documentation
10136
10138{
10140 for (const auto &pd : *Doxygen::exampleLinkedMap)
10141 {
10142 msg("Generating docs for example {}...\n",pd->name());
10143 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10144 if (lang != SrcLangExt::Unknown)
10145 {
10146 DString ext = getFileNameExtension(pd->name());
10147 auto intf = Doxygen::parserManager->getCodeParser(ext);
10148 intf->resetCodeParserState();
10149 }
10150 DString n=pd->getOutputFileBase();
10151 startFile(*g_outputList,n,false,n,pd->name());
10153 g_outputList->docify(pd->name());
10156 DString lineNoOptStr;
10157 if (pd->showLineNo())
10158 {
10159 lineNoOptStr="{lineno}";
10160 }
10161 g_outputList->generateDoc(pd->docFile(), // file
10162 pd->docLine(), // startLine
10163 pd.get(), // context
10164 nullptr, // memberDef
10165 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10166 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10167 DocOptions()
10168 .setIndexWords(true)
10169 .setExample(pd->name()));
10170 endFile(*g_outputList); // contains g_outputList->endContents()
10171 }
10173}
10174
10175//----------------------------------------------------------------------------
10176// generate module pages
10177
10179{
10180 for (const auto &gd : *Doxygen::groupLinkedMap)
10181 {
10182 if (!gd->isReference())
10183 {
10184 gd->writeDocumentation(*g_outputList);
10185 }
10186 }
10187}
10188
10189//----------------------------------------------------------------------------
10190// generate module pages
10191
10193{
10194 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10195 if (numThreads>1) // multi threaded processing
10196 {
10197 struct DocContext
10198 {
10199 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10200 : cdm(cdm_), ol(ol_) {}
10201 ClassDefMutable *cdm;
10202 OutputList ol;
10203 };
10204 ThreadPool threadPool(numThreads);
10205 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10206 // for each class in the namespace...
10207 for (const auto &cd : classList)
10208 {
10210 if (cdm)
10211 {
10212 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10213 auto processFile = [ctx]()
10214 {
10215 if ( ( ctx->cdm->isLinkableInProject() &&
10216 !ctx->cdm->isImplicitTemplateInstance()
10217 ) // skip external references, anonymous compounds and
10218 // template instances and nested classes
10219 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10220 )
10221 {
10222 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10223 ctx->cdm->writeDocumentation(ctx->ol);
10224 ctx->cdm->writeMemberList(ctx->ol);
10225 }
10226 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10227 return ctx;
10228 };
10229 results.emplace_back(threadPool.queue(processFile));
10230 }
10231 }
10232 // wait for the results
10233 for (auto &f : results)
10234 {
10235 auto ctx = f.get();
10236 }
10237 }
10238 else // single threaded processing
10239 {
10240 // for each class in the namespace...
10241 for (const auto &cd : classList)
10242 {
10244 if (cdm)
10245 {
10246 if ( ( cd->isLinkableInProject() &&
10247 !cd->isImplicitTemplateInstance()
10248 ) // skip external references, anonymous compounds and
10249 // template instances and nested classes
10250 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10251 )
10252 {
10253 msg("Generating docs for compound {}...\n",cd->displayName());
10254
10257 }
10259 }
10260 }
10261 }
10262}
10263
10265{
10266 // for each concept in the namespace...
10267 for (const auto &cd : conceptList)
10268 {
10270 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10271 {
10272 msg("Generating docs for concept {}...\n",cd->name());
10274 }
10275 }
10276}
10277
10279{
10280 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10281
10282 //writeNamespaceIndex(*g_outputList);
10283
10284 // for each namespace...
10285 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10286 {
10287 if (nd->isLinkableInProject())
10288 {
10290 if (ndm)
10291 {
10292 msg("Generating docs for namespace {}\n",nd->displayName());
10294 }
10295 }
10296
10297 generateNamespaceClassDocs(nd->getClasses());
10298 if (sliceOpt)
10299 {
10300 generateNamespaceClassDocs(nd->getInterfaces());
10301 generateNamespaceClassDocs(nd->getStructs());
10302 generateNamespaceClassDocs(nd->getExceptions());
10303 }
10304 generateNamespaceConceptDocs(nd->getConcepts());
10305 }
10306}
10307
10309{
10310 std::string oldDir = Dir::currentDirPath();
10311 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10314 {
10315 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10316 }
10317 Dir::setCurrent(oldDir);
10318}
10319
10321{
10322 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10323 std::string oldDir = Dir::currentDirPath();
10324 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10325
10326 DString qhgLocation=Config_getString(QHG_LOCATION);
10327 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10328 {
10329 // run qhelpgenerator -v and extract the Qt version used
10330 DString cmd=qhgLocation+ " -v 2>&1";
10331 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10332 FILE *f=Portable::popen(cmd,"r");
10333 if (!f)
10334 {
10335 err("could not execute {}\n",qhgLocation);
10336 }
10337 else
10338 {
10339 const size_t bufSize = 1024;
10340 char inBuf[bufSize+1];
10341 size_t numRead=fread(inBuf,1,bufSize,f);
10342 inBuf[numRead] = '\0';
10343 Debug::print(Debug::Qhp,0,"{}",inBuf);
10345
10346 int qtVersion=0;
10347 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10348 reg::Match match;
10349 std::string s = inBuf;
10350 if (reg::search(s,match,versionReg))
10351 {
10352 qtVersion = 10000*DString(match[1].str()).toInt() +
10353 100*DString(match[2].str()).toInt() +
10354 DString(match[3].str()).toInt();
10355 }
10356 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10357 {
10358 // dump the output of qhelpgenerator -c file.qhp
10359 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10360 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10361 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10362 f=Portable::popen(cmd,"r");
10363 if (!f)
10364 {
10365 err("could not execute {}\n",qhgLocation);
10366 }
10367 else
10368 {
10369 std::string output;
10370 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10371 {
10372 inBuf[numRead] = '\0';
10373 output += inBuf;
10374 }
10376 Debug::print(Debug::Qhp,0,"{}",output);
10377 }
10378 }
10379 }
10380 }
10381
10382 if (Portable::system(qhgLocation, args, false))
10383 {
10384 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10385 }
10386 Dir::setCurrent(oldDir);
10387}
10388
10389//----------------------------------------------------------------------------
10390
10392{
10393 // check dot path
10394 DString dotPath = Config_getString(DOT_PATH);
10395 if (!dotPath.empty())
10396 {
10397 FileInfo fi(dotPath.str());
10398 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10399 {
10400 dotPath = dotPath+"/dot"+Portable::commandExtension();
10401 FileInfo dp(dotPath.str());
10402 if (!dp.exists() || !dp.isFile())
10403 {
10404 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10405 dotPath = "dot";
10406 dotPath += Portable::commandExtension();
10407 }
10408 else if (dp.isRelative())
10409 {
10410 dotPath = fi.dirPath() + "/" + dotPath;
10411 }
10412 }
10413#if defined(_WIN32) // convert slashes
10414 size_t l=dotPath.length();
10415 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10416#endif
10417 }
10418 else
10419 {
10420 dotPath = "dot";
10421 dotPath += Portable::commandExtension();
10422 }
10423 Doxygen::verifiedDotPath = dotPath;
10425}
10426
10427//----------------------------------------------------------------------------
10428
10429/*! Generate a template version of the configuration file.
10430 * If the \a shortList parameter is true a configuration file without
10431 * comments will be generated.
10432 */
10433static void generateConfigFile(const DString &configFile,bool shortList,
10434 bool updateOnly=false)
10435{
10436 std::ofstream f;
10437 bool fileOpened=openOutputFile(configFile,f);
10438 bool writeToStdout=configFile=="-";
10439 if (fileOpened)
10440 {
10441 TextStream t(&f);
10442 Config::writeTemplate(t,shortList,updateOnly);
10443 if (!writeToStdout)
10444 {
10445 if (!updateOnly)
10446 {
10447 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10448 msg("Now edit the configuration file and enter\n\n");
10449 if (configFile!="Doxyfile" && configFile!="doxyfile")
10450 msg(" doxygen {}\n\n",configFile);
10451 else
10452 msg(" doxygen\n\n");
10453 msg("to generate the documentation for your project\n\n");
10454 }
10455 else
10456 {
10457 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10458 }
10459 }
10460 }
10461 else
10462 {
10463 term("Cannot open file {} for writing\n",configFile);
10464 }
10465}
10466
10468{
10469 std::ofstream f;
10470 bool fileOpened=openOutputFile("-",f);
10471 if (fileOpened)
10472 {
10473 TextStream t(&f);
10474 Config::compareDoxyfile(t,diffList);
10475 }
10476 else
10477 {
10478 term("Cannot open stdout for writing\n");
10479 }
10480}
10481
10482//----------------------------------------------------------------------------
10483// read and parse a tag file
10484
10485static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10486{
10487 DString fileName;
10488 DString destName;
10489 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10490 {
10491 fileName = tagLine.left(eqPos).stripWhiteSpace();
10492 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10493 if (fileName.empty() || destName.empty()) return;
10494 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10495 }
10496 else
10497 {
10498 fileName = tagLine;
10499 }
10500
10501 FileInfo fi(fileName.str());
10502 if (!fi.exists() || !fi.isFile())
10503 {
10504 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10505 return;
10506 }
10507
10508 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10509
10510 Doxygen::tagFileSet.emplace(fi.absFilePath());
10511
10512 if (!destName.empty())
10513 {
10514 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10515 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10516 }
10517 else
10518 {
10519 msg("Reading tag file '{}'...\n",fileName);
10520 }
10521
10522 parseTagFile(root,fi.absFilePath().c_str());
10523}
10524
10525//----------------------------------------------------------------------------
10527{
10528 StringVector latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10529 for (const auto &sheet : latexExtraStyleSheet)
10530 {
10531 std::string fileName = sheet;
10532 if (!fileName.empty())
10533 {
10534 FileInfo fi(fileName);
10535 if (!fi.exists())
10536 {
10537 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10538 }
10539 else if (fi.isDir())
10540 {
10541 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10542 }
10543 else
10544 {
10545 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10547 {
10548 destFileName += LATEX_STYLE_EXTENSION;
10549 }
10550 copyFile(fileName, destFileName);
10551 }
10552 }
10553 }
10554}
10555
10556//----------------------------------------------------------------------------
10557static void copyStyleSheet()
10558{
10559 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10560 if (!htmlStyleSheet.empty())
10561 {
10562 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10563 {
10564 FileInfo fi(htmlStyleSheet.str());
10565 if (!fi.exists())
10566 {
10567 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10568 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10569 }
10570 else if (fi.isDir())
10571 {
10572 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10573 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10574 }
10575 else
10576 {
10577 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10578 copyFile(htmlStyleSheet,destFileName);
10579 }
10580 }
10581 }
10582 StringVector htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10583 for (const auto &sheet : htmlExtraStyleSheet)
10584 {
10585 DString fileName(sheet);
10586 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10587 {
10588 FileInfo fi(fileName.str());
10589 if (!fi.exists())
10590 {
10591 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10592 }
10593 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10594 {
10595 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10596 }
10597 else if (fi.isDir())
10598 {
10599 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10600 }
10601 else
10602 {
10603 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10604 copyFile(fileName, destFileName);
10605 }
10606 }
10607 }
10608}
10609
10610static void copyLogo(const DString &outputOption, bool toIndex)
10611{
10612 DString projectLogo = projectLogoFile();
10613 if (!projectLogo.empty())
10614 {
10615 FileInfo fi(projectLogo.str());
10616 if (!fi.exists())
10617 {
10618 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10619 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10620 }
10621 else if (fi.isDir())
10622 {
10623 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10624 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10625 }
10626 else
10627 {
10628 DString destFileName = outputOption+"/"+fi.fileName();
10629 copyFile(projectLogo,destFileName);
10630 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10631 }
10632 }
10633}
10634
10635static void copyIcon(const DString &outputOption, bool toIndex)
10636{
10637 DString projectIcon = Config_getString(PROJECT_ICON);
10638 if (!projectIcon.empty())
10639 {
10640 FileInfo fi(projectIcon.str());
10641 if (!fi.exists())
10642 {
10643 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10644 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10645 }
10646 else if (fi.isDir())
10647 {
10648 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10649 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10650 }
10651 else
10652 {
10653 DString destFileName = outputOption+"/"+fi.fileName();
10654 copyFile(projectIcon,destFileName);
10655 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10656 }
10657 }
10658}
10659
10660static inline void copyExtraFiles(StringVector files,const DString &filesOption,const DString &outputOption, bool toIndex)
10661{
10662 for (const auto &fileName : files)
10663 {
10664 if (!fileName.empty())
10665 {
10666 FileInfo fi(fileName);
10667 if (!fi.exists())
10668 {
10669 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10670 }
10671 else if (fi.isDir())
10672 {
10673 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10674 }
10675 else
10676 {
10677 DString destFileName = outputOption+"/"+fi.fileName();
10678 copyFile(fileName, destFileName);
10679 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10680 }
10681 }
10682 }
10683}
10684
10685//----------------------------------------------------------------------------
10686
10688{
10689 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10690 {
10691 struct FileEntry
10692 {
10693 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10694 DString path;
10695 FileDef *fileDef;
10696 };
10697
10698 // collect the entry for which to compute the longest common prefix (LCP) of the path
10699 std::vector<FileEntry> fileEntries;
10700 for (const auto &fd : *fn)
10701 {
10702 if (!fd->isReference()) // skip external references
10703 {
10704 fileEntries.emplace_back(fd->getPath(),fd.get());
10705 }
10706 }
10707
10708 size_t size = fileEntries.size();
10709
10710 if (size==1) // name if unique, so diskname is simply the name
10711 {
10712 FileDef *fd = fileEntries[0].fileDef;
10713 fd->setDiskName(fn->fileName());
10714 }
10715 else if (size>1) // multiple occurrences of the same file name
10716 {
10717 // sort the array
10718 std::stable_sort(fileEntries.begin(),
10719 fileEntries.end(),
10720 [](const FileEntry &fe1,const FileEntry &fe2)
10721 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10722 );
10723
10724 // since the entries are sorted, the common prefix of the whole array is same
10725 // as the common prefix between the first and last entry
10726 const FileEntry &first = fileEntries[0];
10727 const FileEntry &last = fileEntries[size-1];
10728 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10729 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10730 int j=0;
10731 int i=0;
10732 for (i=0;i<first_path_size && i<last_path_size;i++)
10733 {
10734 if (first.path[i]=='/') j=i;
10735 if (first.path[i]!=last.path[i]) break;
10736 }
10737 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10738 {
10739 // case first='some/path' and last='some/path/more' => match is 'some/path'
10740 j=first_path_size;
10741 }
10742 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10743 {
10744 // case first='some/path/more' and last='some/path' => match is 'some/path'
10745 j=last_path_size;
10746 }
10747
10748 // add non-common part of the path to the name
10749 for (auto &fileEntry : fileEntries)
10750 {
10751 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10752 fileEntry.fileDef->setName(prefix+fn->fileName());
10753 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10754 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10755 }
10756 }
10757 }
10758}
10759
10760
10761
10762//----------------------------------------------------------------------------
10763
10764static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10765{
10766 DString fileName=fn;
10767 DString extension;
10768 size_t sep = fileName.rfind('/');
10769 size_t ei = fileName.rfind('.');
10770 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10771 {
10772 extension=fileName.mid(ei);
10773 }
10774 else
10775 {
10776 extension = ".no_extension";
10777 }
10778
10779 return Doxygen::parserManager->getOutlineParser(extension);
10780}
10781
10782static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10783 FileDef *fd,const DString &fn,
10784 ClangTUParser *clangParser,bool newTU)
10785{
10786 DString fileName=fn;
10787 AUTO_TRACE("fileName={}",fileName);
10788 DString extension;
10789 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10790 {
10791 extension=fileName.mid(ei);
10792 }
10793 else
10794 {
10795 extension = ".no_extension";
10796 }
10797
10798 FileInfo fi(fileName.str());
10799 std::string preBuf;
10800
10801 if (Config_getBool(ENABLE_PREPROCESSING) &&
10802 parser.needsPreprocessing(extension))
10803 {
10804 Preprocessor preprocessor;
10805 StringVector includePath = Config_getList(INCLUDE_PATH);
10806 for (const auto &s : includePath)
10807 {
10808 std::string absPath = FileInfo(s).absFilePath();
10809 preprocessor.addSearchDir(absPath);
10810 }
10811 std::string inBuf;
10812 msg("Preprocessing {}...\n",fn);
10813 readInputFile(fileName,inBuf);
10814 addTerminalCharIfMissing(inBuf,'\n');
10815 preprocessor.processFile(fileName,inBuf,preBuf);
10816 }
10817 else // no preprocessing
10818 {
10819 msg("Reading {}...\n",fn);
10820 readInputFile(fileName,preBuf);
10821 addTerminalCharIfMissing(preBuf,'\n');
10822 }
10823
10824 std::string convBuf;
10825 convBuf.reserve(preBuf.size()+1024);
10826
10827 // convert multi-line C++ comments to C style comments
10828 convertCppComments(preBuf,convBuf,fileName.str());
10829
10830 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10831 // use language parse to parse the file
10832 if (clangParser)
10833 {
10834 if (newTU) clangParser->parse();
10835 clangParser->switchToFile(fd);
10836 }
10837 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10838 fileRoot->setFileDef(fd);
10839 return fileRoot;
10840}
10841
10842//! parse the list of input files
10843static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10844{
10845 AUTO_TRACE();
10846#if USE_LIBCLANG
10848 {
10849 StringUnorderedSet processedFiles;
10850
10851 // create a dictionary with files to process
10852 StringUnorderedSet filesToProcess;
10853 for (const auto &s : g_inputFiles)
10854 {
10855 filesToProcess.insert(s);
10856 }
10857
10858 std::mutex processedFilesLock;
10859 // process source files (and their include dependencies)
10860 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10861 msg("Processing input using {} threads.\n",numThreads);
10862 ThreadPool threadPool(numThreads);
10863 using FutureType = std::vector< std::shared_ptr<Entry> >;
10864 std::vector< std::future< FutureType > > results;
10865 for (const auto &s : g_inputFiles)
10866 {
10867 bool ambig = false;
10868 DString qs = s;
10870 ASSERT(fd!=nullptr);
10871 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10872 {
10873 // lambda representing the work to executed by a thread
10874 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10875 bool ambig_l = false;
10876 std::vector< std::shared_ptr<Entry> > roots;
10877 FileDef *fd_l = Doxygen::inputNameLinkedMap->findFileDef(qs,ambig_l);
10878 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10879 auto parser = getParserForFile(qs);
10880 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10881 roots.push_back(fileRoot);
10882
10883 // Now process any include files in the same translation unit
10884 // first. When libclang is used this is much more efficient.
10885 for (auto incFile : clangParser->filesInSameTU())
10886 {
10887 DString qincFile = incFile;
10888 if (filesToProcess.find(incFile)!=filesToProcess.end())
10889 {
10890 bool needsToBeProcessed = false;
10891 {
10892 std::lock_guard<std::mutex> lock(processedFilesLock);
10893 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10894 if (needsToBeProcessed) processedFiles.insert(incFile);
10895 }
10896 if (qincFile!=qs && needsToBeProcessed)
10897 {
10898 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(qincFile,ambig_l);
10899 if (ifd && !ifd->isReference())
10900 {
10901 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10902 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10903 roots.push_back(fileRoot);
10904 }
10905 }
10906 }
10907 }
10908 return roots;
10909 };
10910 // dispatch the work and collect the future results
10911 results.emplace_back(threadPool.queue(processFile));
10912 }
10913 }
10914 // synchronize with the Entry result lists produced and add them to the root
10915 for (auto &f : results)
10916 {
10917 auto l = f.get();
10918 for (auto &e : l)
10919 {
10920 root->moveToSubEntryAndKeep(e);
10921 }
10922 }
10923 // process remaining files
10924 results.clear();
10925 for (const auto &s : g_inputFiles)
10926 {
10927 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10928 {
10929 // lambda representing the work to executed by a thread
10930 auto processFile = [s]() {
10931 bool ambig = false;
10932 DString qs = s;
10933 std::vector< std::shared_ptr<Entry> > roots;
10935 auto parser { getParserForFile(qs) };
10936 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10937 if (useClang)
10938 {
10939 auto clangParser = ClangParser::instance()->createTUParser(fd);
10940 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10941 roots.push_back(fileRoot);
10942 }
10943 else
10944 {
10945 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10946 roots.push_back(fileRoot);
10947 }
10948 return roots;
10949 };
10950 results.emplace_back(threadPool.queue(processFile));
10951 }
10952 }
10953 // synchronize with the Entry result lists produced and add them to the root
10954 for (auto &f : results)
10955 {
10956 auto l = f.get();
10957 for (auto &e : l)
10958 {
10959 root->moveToSubEntryAndKeep(e);
10960 }
10961 }
10962 }
10963 else // normal processing
10964#endif
10965 {
10966 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10967 msg("Processing input using {} threads.\n",numThreads);
10968 ThreadPool threadPool(numThreads);
10969 using FutureType = std::shared_ptr<Entry>;
10970 std::vector< std::future< FutureType > > results;
10971 for (const auto &s : g_inputFiles)
10972 {
10973 // lambda representing the work to executed by a thread
10974 auto processFile = [s]() {
10975 bool ambig = false;
10976 DString qs = s;
10978 auto parser = getParserForFile(qs);
10979 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10980 return fileRoot;
10981 };
10982 // dispatch the work and collect the future results
10983 results.emplace_back(threadPool.queue(processFile));
10984 }
10985 // synchronize with the Entry results produced and add them to the root
10986 for (auto &f : results)
10987 {
10988 root->moveToSubEntryAndKeep(f.get());
10989 }
10990 }
10991}
10992
10993//! parse the list of input files
10994static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10995{
10996 AUTO_TRACE();
10997#if USE_LIBCLANG
10999 {
11000 StringUnorderedSet processedFiles;
11001
11002 // create a dictionary with files to process
11003 StringUnorderedSet filesToProcess;
11004 for (const auto &s : g_inputFiles)
11005 {
11006 filesToProcess.insert(s);
11007 }
11008
11009 // process source files (and their include dependencies)
11010 for (const auto &s : g_inputFiles)
11011 {
11012 bool ambig = false;
11013 DString qs =s;
11015 ASSERT(fd!=nullptr);
11016 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
11017 {
11018 auto clangParser = ClangParser::instance()->createTUParser(fd);
11019 auto parser { getParserForFile(qs) };
11020 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11021 root->moveToSubEntryAndKeep(fileRoot);
11022 processedFiles.insert(s);
11023
11024 // Now process any include files in the same translation unit
11025 // first. When libclang is used this is much more efficient.
11026 for (auto incFile : clangParser->filesInSameTU())
11027 {
11028 //printf(" file %s\n",qPrint(incFile));
11029 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
11030 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
11031 {
11032 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
11033 if (ifd && !ifd->isReference())
11034 {
11035 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
11036 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
11037 root->moveToSubEntryAndKeep(fileRoot);
11038 processedFiles.insert(incFile);
11039 }
11040 }
11041 }
11042 }
11043 }
11044 // process remaining files
11045 for (const auto &s : g_inputFiles)
11046 {
11047 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11048 {
11049 bool ambig = false;
11050 DString qs = s;
11052 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11053 {
11054 auto clangParser = ClangParser::instance()->createTUParser(fd);
11055 auto parser { getParserForFile(qs) };
11056 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11057 root->moveToSubEntryAndKeep(fileRoot);
11058 }
11059 else
11060 {
11061 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11062 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11063 root->moveToSubEntryAndKeep(fileRoot);
11064 }
11065 processedFiles.insert(s);
11066 }
11067 }
11068 }
11069 else // normal processing
11070#endif
11071 {
11072 for (const auto &s : g_inputFiles)
11073 {
11074 bool ambig = false;
11075 DString qs = s;
11077 ASSERT(fd!=nullptr);
11078 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11079 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11080 root->moveToSubEntryAndKeep(std::move(fileRoot));
11081 }
11082 }
11083}
11084
11085// resolves a path that may include symlinks, if a recursive symlink is
11086// found an empty string is returned.
11087static std::string resolveSymlink(const std::string &path)
11088{
11089 size_t sepPos=0;
11090 size_t oldPos=0;
11091 StringUnorderedSet nonSymlinks;
11092 StringUnorderedSet known;
11093 DString result(path);
11094 DString oldPrefix = "/";
11095 do
11096 {
11097#if defined(_WIN32)
11098 // UNC path, skip server and share name
11099 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11100 sepPos = result.find('/',2);
11101 if (sepPos!=DString::npos)
11102 sepPos = result.find('/',sepPos+1);
11103#else
11104 sepPos = result.find('/',sepPos+1);
11105#endif
11106 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11107 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11108 {
11109 FileInfo fi(prefix.str());
11110 if (fi.isSymLink())
11111 {
11112 DString target = fi.readLink();
11113 bool isRelative = FileInfo(target.str()).isRelative();
11114 if (isRelative)
11115 {
11116 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11117 }
11118 if (sepPos!=DString::npos)
11119 {
11120 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11121 {
11122 target+='/';
11123 }
11124 target+=result.mid(sepPos);
11125 }
11126 result = Dir::cleanDirPath(target.str());
11127 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11128 known.insert(result.str());
11129 if (isRelative)
11130 {
11131 sepPos = oldPos;
11132 }
11133 else // link to absolute path
11134 {
11135 sepPos = 0;
11136 oldPrefix = "/";
11137 }
11138 }
11139 else
11140 {
11141 nonSymlinks.insert(prefix.str());
11142 oldPrefix = prefix;
11143 }
11144 oldPos = sepPos;
11145 }
11146 }
11147 while (sepPos!=DString::npos);
11148 return Dir::cleanDirPath(result.str());
11149}
11150
11152
11153//----------------------------------------------------------------------------
11154// Read all files matching at least one pattern in 'patList' in the
11155// directory represented by 'fi'.
11156// The directory is read iff the recursiveFlag is set.
11157// The contents of all files is append to the input string
11158
11159static void readDir(FileInfo *fi,
11160 FileNameLinkedMap *fnMap,
11161 StringUnorderedSet *exclSet,
11162 const StringVector *patList,
11163 const StringVector *exclPatList,
11164 StringVector *resultList,
11165 StringUnorderedSet *resultSet,
11166 bool errorIfNotExist,
11167 bool recursive,
11168 StringUnorderedSet *killSet,
11169 StringUnorderedSet *paths
11170 )
11171{
11172 std::string dirName = fi->absFilePath();
11173 if (paths && !dirName.empty())
11174 {
11175 paths->insert(dirName);
11176 }
11177 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11178 if (fi->isSymLink())
11179 {
11180 dirName = resolveSymlink(dirName);
11181 if (dirName.empty())
11182 {
11183 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11184 return; // recursive symlink
11185 }
11186 }
11187
11188 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11189 {
11190 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11191 return; // already visited path
11192 }
11193 g_pathsVisited.insert(dirName);
11194
11195 Dir dir(dirName);
11196 msg("Searching for files in directory {}\n", fi->absFilePath());
11197 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11198
11199 StringVector dirResultList;
11200
11201 bool caseSenseNames = useCaseSenseNames();
11202
11203 for (const auto &dirEntry : dir.iterator())
11204 {
11205 FileInfo cfi(dirEntry.path());
11206 auto checkPatterns = [&]() -> bool
11207 {
11208 return (patList==nullptr || cfi.match(*patList,caseSenseNames)) &&
11209 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11210 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11211 };
11212
11213 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11214 { // file should not be excluded
11215 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11216 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11217 {
11218 }
11219 else if (!cfi.exists() || !cfi.isReadable())
11220 {
11221 if (errorIfNotExist && checkPatterns())
11222 {
11223 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11224 }
11225 }
11226 else if (cfi.isFile() && checkPatterns())
11227 {
11228 std::string name=cfi.fileName();
11229 std::string path=cfi.dirPath()+"/";
11230 std::string fullName=path+name;
11231 if (fnMap)
11232 {
11233 auto fd = createFileDef(path,name);
11234 FileName *fn=nullptr;
11235 if (!name.empty())
11236 {
11237 fn = fnMap->add(name);
11238 fn->push_back(std::move(fd));
11239 }
11240 }
11241 dirResultList.push_back(fullName);
11242 if (resultSet) resultSet->insert(fullName);
11243 if (killSet) killSet->insert(fullName);
11244 }
11245 else if (recursive &&
11246 cfi.isDir() &&
11247 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11248 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11249 {
11250 FileInfo acfi(cfi.absFilePath());
11251 readDir(&acfi,fnMap,exclSet,
11252 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11253 recursive,killSet,paths);
11254 }
11255 }
11256 }
11257 if (resultList && !dirResultList.empty())
11258 {
11259 // sort the resulting list to make the order platform independent.
11260 std::stable_sort(dirResultList.begin(),
11261 dirResultList.end(),
11262 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11263
11264 // append the sorted results to resultList
11265 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11266 }
11267}
11268
11269
11270//----------------------------------------------------------------------------
11271// read a file or all files in a directory and append their contents to the
11272// input string. The names of the files are appended to the 'fiList' list.
11273
11275 FileNameLinkedMap *fnMap,
11276 StringUnorderedSet *exclSet,
11277 const StringVector *patList,
11278 const StringVector *exclPatList,
11279 StringVector *resultList,
11280 StringUnorderedSet *resultSet,
11281 bool recursive,
11282 bool errorIfNotExist,
11283 StringUnorderedSet *killSet,
11284 StringUnorderedSet *paths
11285 )
11286{
11287 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11288 // strip trailing slashes
11289 if (s.empty()) return;
11290
11291 g_pathsVisited.clear();
11292
11293 FileInfo fi(s.str());
11294 //printf("readFileOrDirectory(%s)\n",s);
11295 {
11296 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11297 {
11298 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11299 {
11300 }
11301 else if (!fi.exists() || !fi.isReadable())
11302 {
11303 if (errorIfNotExist)
11304 {
11305 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11306 }
11307 }
11308 else if (fi.isFile())
11309 {
11310 std::string dirPath = fi.dirPath(true);
11311 std::string filePath = fi.absFilePath();
11312 if (paths && !dirPath.empty())
11313 {
11314 paths->insert(dirPath);
11315 }
11316 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11317 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11318 {
11319 std::string name=fi.fileName();
11320 if (fnMap)
11321 {
11322 auto fd = createFileDef(dirPath+"/",name);
11323 if (!name.empty())
11324 {
11325 FileName *fn = fnMap->add(name);
11326 fn->push_back(std::move(fd));
11327 }
11328 }
11329 if (resultList || resultSet)
11330 {
11331 if (resultList) resultList->push_back(filePath);
11332 if (resultSet) resultSet->insert(filePath);
11333 }
11334
11335 if (killSet) killSet->insert(fi.absFilePath());
11336 }
11337 }
11338 else if (fi.isDir()) // readable dir
11339 {
11340 readDir(&fi,fnMap,exclSet,patList,
11341 exclPatList,resultList,resultSet,errorIfNotExist,
11342 recursive,killSet,paths);
11343 }
11344 }
11345 }
11346}
11347
11348//----------------------------------------------------------------------------
11349
11351{
11352 DString anchor;
11354 {
11355 MemberDef *md = toMemberDef(d);
11356 anchor=":"+md->anchor();
11357 }
11358 DString scope;
11359 DString fn = d->getOutputFileBase();
11362 {
11363 scope = fn;
11364 }
11365 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11366 << fn+anchor << "','"
11367 << scope << "','"
11368 << d->name() << "','"
11369 << d->getDefFileName() << "','"
11370 << d->getDefLine()
11371 << "');\n";
11372}
11373
11374static void dumpSymbolMap()
11375{
11376 std::ofstream f = Portable::openOutputStream("symbols.sql");
11377 if (f.is_open())
11378 {
11379 TextStream t(&f);
11380 for (const auto &[name,symList] : *Doxygen::symbolMap)
11381 {
11382 for (const auto &def : symList)
11383 {
11384 dumpSymbol(t,def);
11385 }
11386 }
11387 }
11388}
11389
11390// print developer options of Doxygen
11391static void devUsage()
11392{
11394 msg("Developer parameters:\n");
11395 msg(" -m dump symbol map\n");
11396 msg(" -b making messages output unbuffered\n");
11397 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11398#if ENABLE_TRACING
11399 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11400 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11401 " and include time and thread information\n");
11402#endif
11403 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11405}
11406
11407
11408//----------------------------------------------------------------------------
11409// print the version of Doxygen
11410
11411static void version(const bool extended)
11412{
11414 DString versionString = getFullVersion();
11415 msg("{}\n",versionString);
11416 if (extended)
11417 {
11418 DString extVers;
11419 if (!extVers.empty()) extVers+= ", ";
11420 extVers += "sqlite3 ";
11421 extVers += sqlite3_libversion();
11422#if USE_LIBCLANG
11423 if (!extVers.empty()) extVers+= ", ";
11424 extVers += "clang support ";
11425 extVers += CLANG_VERSION_STRING;
11426#endif
11427 if (!extVers.empty())
11428 {
11429 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11430 {
11431 extVers = extVers.replace(lastComma,1," and");
11432 }
11433 msg(" with {}.\n",extVers);
11434 }
11435 }
11436}
11437
11438//----------------------------------------------------------------------------
11439// print the usage of Doxygen
11440
11441static void usage(const DString &name,const DString &versionString)
11442{
11444 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11445 "You can use Doxygen in a number of ways:\n\n"
11446 "1) Use Doxygen to generate a template configuration file*:\n"
11447 " {1} [-s] -g [configName]\n\n"
11448 "2) Use Doxygen to update an old configuration file*:\n"
11449 " {1} [-s] -u [configName]\n\n"
11450 "3) Use Doxygen to generate documentation using an existing "
11451 "configuration file*:\n"
11452 " {1} [configName]\n\n"
11453 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11454 " generated documentation:\n"
11455 " {1} -l [layoutFileName]\n\n"
11456 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11457 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11458 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11459 " RTF: {1} -w rtf styleSheetFile\n"
11460 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11461 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11462 "6) Use Doxygen to generate a rtf extensions file\n"
11463 " {1} -e rtf extensionsFile\n\n"
11464 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11465 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11466 " {1} -x [configFile]\n\n"
11467 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11468 " without replacing the environment variables or CMake type replacement variables\n"
11469 " {1} -x_noenv [configFile]\n\n"
11470 "8) Use Doxygen to show a list of built-in emojis.\n"
11471 " {1} -f emoji outputFileName\n\n"
11472 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11473 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11474 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11475 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11476 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11477 "-v print version string, -V print extended version information\n"
11478 "-h,-? prints usage help information\n"
11479 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11480}
11481
11482//----------------------------------------------------------------------------
11483// read the argument of option 'c' from the comment argument list and
11484// update the option index 'optInd'.
11485
11486static const char *getArg(int argc,char **argv,int &optInd)
11487{
11488 char *s=nullptr;
11489 if (dstrlen(&argv[optInd][2])>0)
11490 s=&argv[optInd][2];
11491 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11492 s=argv[++optInd];
11493 return s;
11494}
11495
11496//----------------------------------------------------------------------------
11497
11498/** @brief /dev/null outline parser */
11500{
11501 public:
11502 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11503 bool needsPreprocessing(const DString &) const override { return false; }
11504 void parsePrototype(const DString &) override {}
11505};
11506
11507
11508template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11509{
11510 return []() { return std::make_unique<T>(); };
11511}
11512
11514{
11515 initResources();
11516 DString lang = Portable::getenv("LC_ALL");
11517 if (!lang.empty()) Portable::setenv("LANG",lang);
11518 std::setlocale(LC_ALL,"");
11519 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11520 std::setlocale(LC_NUMERIC,"C");
11521
11523
11547
11548 // register any additional parsers here...
11549
11551
11552#if USE_LIBCLANG
11554#endif
11563 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11564 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11565 //Doxygen::tagDestinationDict.setAutoDelete(true);
11567
11568 // initialization of these globals depends on
11569 // configuration switches so we need to postpone these
11570 Doxygen::globalScope = nullptr;
11580
11581}
11582
11615
11616void readConfiguration(int argc, char **argv)
11617{
11618 DString versionString = getFullVersion();
11619
11620 // helper that calls \a func to write to file \a fileName via a TextStream
11621 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11622 {
11623 std::ofstream f;
11624 if (openOutputFile(fileName,f))
11625 {
11626 TextStream t(&f);
11627 func(t);
11628 return true;
11629 }
11630 return false;
11631 };
11632
11633
11634 /**************************************************************************
11635 * Handle arguments *
11636 **************************************************************************/
11637
11638 int optInd=1;
11639 DString configName;
11640 DString traceName;
11641 bool genConfig=false;
11642 bool shortList=false;
11643 bool traceTiming=false;
11645 bool updateConfig=false;
11646 bool quiet = false;
11647 while (optInd<argc && argv[optInd][0]=='-' &&
11648 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11649 argv[optInd][1]=='-')
11650 )
11651 {
11652 switch(argv[optInd][1])
11653 {
11654 case 'g':
11655 {
11656 genConfig=true;
11657 }
11658 break;
11659 case 'l':
11660 {
11661 DString layoutName;
11662 if (optInd+1>=argc)
11663 {
11664 layoutName="DoxygenLayout.xml";
11665 }
11666 else
11667 {
11668 layoutName=argv[optInd+1];
11669 }
11670 writeDefaultLayoutFile(layoutName);
11672 exit(0);
11673 }
11674 break;
11675 case 'c':
11676 if (optInd+1>=argc) // no file name given
11677 {
11678 msg("option \"-c\" is missing the file name to read\n");
11679 devUsage();
11681 exit(1);
11682 }
11683 else
11684 {
11685 g_commentFileName=argv[optInd+1];
11686 optInd++;
11687 }
11688 g_singleComment=true;
11689 quiet=true;
11690 break;
11691 case 'd':
11692 {
11693 DString debugLabel=getArg(argc,argv,optInd);
11694 if (debugLabel.empty())
11695 {
11696 devUsage();
11698 exit(0);
11699 }
11700 int retVal = Debug::setFlagStr(debugLabel);
11701 if (!retVal)
11702 {
11703 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11704 devUsage();
11706 exit(1);
11707 }
11708 }
11709 break;
11710 case 't':
11711 {
11712#if ENABLE_TRACING
11713 if (!strcmp(argv[optInd]+1,"t_time"))
11714 {
11715 traceTiming = true;
11716 }
11717 else if (!strcmp(argv[optInd]+1,"t"))
11718 {
11719 traceTiming = false;
11720 }
11721 else
11722 {
11723 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11725 exit(1);
11726 }
11727 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11728 {
11729 traceName="stdout";
11730 }
11731 else
11732 {
11733 traceName=argv[optInd+1];
11734 optInd++;
11735 }
11736#else
11737 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11739 exit(1);
11740#endif
11741 }
11742 break;
11743 case 'x':
11744 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11745 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11746 else
11747 {
11748 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11750 exit(1);
11751 }
11752 break;
11753 case 's':
11754 shortList=true;
11755 break;
11756 case 'u':
11757 updateConfig=true;
11758 break;
11759 case 'e':
11760 {
11761 DString formatName=getArg(argc,argv,optInd);
11762 if (formatName.empty())
11763 {
11764 err("option \"-e\" is missing format specifier rtf.\n");
11766 exit(1);
11767 }
11768 if (dstricmp(formatName.data(),"rtf")==0)
11769 {
11770 if (optInd+1>=argc)
11771 {
11772 err("option \"-e rtf\" is missing an extensions file name\n");
11774 exit(1);
11775 }
11776 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11778 exit(0);
11779 }
11780 err("option \"-e\" has invalid format specifier.\n");
11782 exit(1);
11783 }
11784 break;
11785 case 'f':
11786 {
11787 DString listName=getArg(argc,argv,optInd);
11788 if (listName.empty())
11789 {
11790 err("option \"-f\" is missing list specifier.\n");
11792 exit(1);
11793 }
11794 if (dstricmp(listName.data(),"emoji")==0)
11795 {
11796 if (optInd+1>=argc)
11797 {
11798 err("option \"-f emoji\" is missing an output file name\n");
11800 exit(1);
11801 }
11802 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11804 exit(0);
11805 }
11806 err("option \"-f\" has invalid list specifier.\n");
11808 exit(1);
11809 }
11810 break;
11811 case 'w':
11812 {
11813 DString formatName=getArg(argc,argv,optInd);
11814 if (formatName.empty())
11815 {
11816 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11818 exit(1);
11819 }
11820 if (dstricmp(formatName.data(),"rtf")==0)
11821 {
11822 if (optInd+1>=argc)
11823 {
11824 err("option \"-w rtf\" is missing a style sheet file name\n");
11826 exit(1);
11827 }
11828 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11829 {
11830 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11832 exit(1);
11833 }
11835 exit(0);
11836 }
11837 else if (dstricmp(formatName.data(),"html")==0)
11838 {
11839 Config::init();
11840 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11841 // explicit config file mentioned or default found on disk
11842 {
11843 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11844 if (!Config::parse(df)) // parse the config file
11845 {
11846 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11848 exit(1);
11849 }
11850 }
11851 if (optInd+3>=argc)
11852 {
11853 err("option \"-w html\" does not have enough arguments\n");
11855 exit(1);
11856 }
11857 Config::postProcess(true);
11860 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11861 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11862 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11863 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11865 exit(0);
11866 }
11867 else if (dstricmp(formatName.data(),"latex")==0)
11868 {
11869 Config::init();
11870 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11871 {
11872 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11873 if (!Config::parse(df))
11874 {
11875 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11877 exit(1);
11878 }
11879 }
11880 if (optInd+3>=argc)
11881 {
11882 err("option \"-w latex\" does not have enough arguments\n");
11884 exit(1);
11885 }
11886 Config::postProcess(true);
11889 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11890 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11891 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11892 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11894 exit(0);
11895 }
11896 else
11897 {
11898 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11900 exit(1);
11901 }
11902 }
11903 break;
11904 case 'm':
11905 g_dumpSymbolMap = true;
11906 break;
11907 case 'v':
11908 version(false);
11910 exit(0);
11911 break;
11912 case 'V':
11913 version(true);
11915 exit(0);
11916 break;
11917 case '-':
11918 if (dstrcmp(&argv[optInd][2],"help")==0)
11919 {
11920 usage(argv[0],versionString);
11921 exit(0);
11922 }
11923 else if (dstrcmp(&argv[optInd][2],"version")==0)
11924 {
11925 version(false);
11927 exit(0);
11928 }
11929 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
11930 (dstrcmp(&argv[optInd][2],"VERSION")==0))
11931 {
11932 version(true);
11934 exit(0);
11935 }
11936 else
11937 {
11938 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11939 usage(argv[0],versionString);
11940 exit(1);
11941 }
11942 break;
11943 case 'b':
11944 setvbuf(stdout,nullptr,_IONBF,0);
11945 break;
11946 case 'q':
11947 quiet = true;
11948 break;
11949 case 'h':
11950 case '?':
11951 usage(argv[0],versionString);
11952 exit(0);
11953 break;
11954 default:
11955 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11956 usage(argv[0],versionString);
11957 exit(1);
11958 }
11959 optInd++;
11960 }
11961
11962 /**************************************************************************
11963 * Parse or generate the config file *
11964 **************************************************************************/
11965
11966 initTracing(traceName.data(),traceTiming);
11967 TRACE("Doxygen version used: {}",getFullVersion());
11968 Config::init();
11969
11970 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11971 if (optInd>=argc)
11972 {
11973 if (configFileInfo1.exists())
11974 {
11975 configName="Doxyfile";
11976 }
11977 else if (configFileInfo2.exists())
11978 {
11979 configName="doxyfile";
11980 }
11981 else if (genConfig)
11982 {
11983 configName="Doxyfile";
11984 }
11985 else
11986 {
11987 err("Doxyfile not found and no input file specified!\n");
11988 usage(argv[0],versionString);
11989 exit(1);
11990 }
11991 }
11992 else
11993 {
11994 FileInfo fi(argv[optInd]);
11995 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
11996 {
11997 configName=argv[optInd];
11998 }
11999 else
12000 {
12001 err("configuration file {} not found!\n",argv[optInd]);
12002 usage(argv[0],versionString);
12003 exit(1);
12004 }
12005 }
12006
12007 if (genConfig)
12008 {
12009 generateConfigFile(configName,shortList);
12011 exit(0);
12012 }
12013
12014 if (!Config::parse(configName,updateConfig,diffList))
12015 {
12016 err("could not open or read configuration file {}!\n",configName);
12018 exit(1);
12019 }
12020
12021 if (diffList!=Config::CompareMode::Full)
12022 {
12024 compareDoxyfile(diffList);
12026 exit(0);
12027 }
12028
12029 if (updateConfig)
12030 {
12032 generateConfigFile(configName,shortList,true);
12034 exit(0);
12035 }
12036
12037 /* Perlmod wants to know the path to the config file.*/
12038 FileInfo configFileInfo(configName.str());
12039 setPerlModDoxyfile(configFileInfo.absFilePath());
12040
12041 /* handle -q option */
12042 if (quiet) Config_updateBool(QUIET,true);
12043}
12044
12045/** check and resolve config options */
12047{
12048 AUTO_TRACE();
12049
12050 Config::postProcess(false);
12054}
12055
12056/** adjust globals that depend on configuration settings. */
12058{
12059 AUTO_TRACE();
12060 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12071
12072 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12073
12074 /* Set the global html file extension. */
12075 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12076
12077
12079 Config_getBool(CALLER_GRAPH) ||
12080 Config_getBool(REFERENCES_RELATION) ||
12081 Config_getBool(REFERENCED_BY_RELATION);
12082
12083 /**************************************************************************
12084 * Add custom extension mappings
12085 **************************************************************************/
12086
12087 StringVector extMaps = Config_getList(EXTENSION_MAPPING);
12088 for (const auto &mapping : extMaps)
12089 {
12090 DString mapStr = mapping;
12091 if (size_t i=mapStr.find('='); i==DString::npos)
12092 {
12093 continue;
12094 }
12095 else
12096 {
12097 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12098 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12099 if (ext.empty() || language.empty())
12100 {
12101 continue;
12102 }
12103
12104 if (!updateLanguageMapping(ext,language))
12105 {
12106 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12107 "Check the EXTENSION_MAPPING setting in the config file.\n",
12108 ext,language);
12109 }
12110 else
12111 {
12112 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12113 ext,language);
12114 }
12115 }
12116 }
12117 // create input file exncodings
12118
12119 // check INPUT_ENCODING
12120 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12121 if (cd==reinterpret_cast<void *>(-1))
12122 {
12123 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12124 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12125 Config_getString(INPUT_ENCODING),strerror(errno));
12126 }
12127 else
12128 {
12130 }
12131
12132 // check and split INPUT_FILE_ENCODING
12133 StringVector fileEncod = Config_getList(INPUT_FILE_ENCODING);
12134 for (const auto &mapping : fileEncod)
12135 {
12136 DString mapStr = mapping;
12137 if (size_t i=mapStr.find('='); i==DString::npos)
12138 {
12139 continue;
12140 }
12141 else
12142 {
12143 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12144 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12145 if (pattern.empty() || encoding.empty())
12146 {
12147 continue;
12148 }
12149 cd = portable_iconv_open("UTF-8",encoding.data());
12150 if (cd==reinterpret_cast<void *>(-1))
12151 {
12152 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12153 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12154 encoding,strerror(errno));
12155 }
12156 else
12157 {
12159 }
12160
12161 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12162 }
12163 }
12164
12165 // add predefined macro name to a dictionary
12166 StringVector expandAsDefinedList = Config_getList(EXPAND_AS_DEFINED);
12167 for (const auto &s : expandAsDefinedList)
12168 {
12170 }
12171
12172 // read aliases and store them in a dictionary
12173 readAliases();
12174
12175 // store number of spaces in a tab into Doxygen::spaces
12176 int tabSize = Config_getInt(TAB_SIZE);
12177 Doxygen::spaces.resize(tabSize);
12178 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12179 Doxygen::spaces.at(tabSize)='\0';
12180}
12181
12182#ifdef HAS_SIGNALS
12183static void stopDoxygen(int)
12184{
12185 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12186 Dir thisDir;
12187 msg("Cleaning up...\n");
12188 if (!Doxygen::filterDBFileName.empty())
12189 {
12190 thisDir.remove(Doxygen::filterDBFileName.str());
12191 }
12192 killpg(0,SIGINT);
12194 exitTracing();
12195 exit(1);
12196}
12197#endif
12198
12199static void writeTagFile()
12200{
12201 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12202 if (generateTagFile.empty()) return;
12203
12204 std::ofstream f = Portable::openOutputStream(generateTagFile);
12205 if (!f.is_open())
12206 {
12207 err("cannot open tag file {} for writing\n", generateTagFile);
12208 return;
12209 }
12210 TextStream tagFile(&f);
12211 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12212 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12213 std::string gitVersion = getGitVersion();
12214 if (!gitVersion.empty())
12215 {
12216 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12217 }
12218 tagFile << ">\n";
12219
12220 // for each file
12221 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12222 {
12223 for (const auto &fd : *fn)
12224 {
12225 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12226 }
12227 }
12228 // for each class
12229 for (const auto &cd : *Doxygen::classLinkedMap)
12230 {
12231 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12232 if (cdm && cdm->isLinkableInProject())
12233 {
12234 cdm->writeTagFile(tagFile);
12235 }
12236 }
12237 // for each concept
12238 for (const auto &cd : *Doxygen::conceptLinkedMap)
12239 {
12240 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12241 if (cdm && cdm->isLinkableInProject())
12242 {
12243 cdm->writeTagFile(tagFile);
12244 }
12245 }
12246 // for each namespace
12247 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12248 {
12250 if (ndm && nd->isLinkableInProject())
12251 {
12252 ndm->writeTagFile(tagFile);
12253 }
12254 }
12255 // for each group
12256 for (const auto &gd : *Doxygen::groupLinkedMap)
12257 {
12258 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12259 }
12260 // for each module
12261 for (const auto &mod : ModuleManager::instance().modules())
12262 {
12263 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12264 }
12265 // for each page
12266 for (const auto &pd : *Doxygen::pageLinkedMap)
12267 {
12268 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12269 }
12270 // for requirements
12272 // for each directory
12273 for (const auto &dd : *Doxygen::dirLinkedMap)
12274 {
12275 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12276 }
12277 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12278
12279 tagFile << "</tagfile>\n";
12280}
12281
12282static void exitDoxygen() noexcept
12283{
12284 if (!g_successfulRun) // premature exit
12285 {
12286 Dir thisDir;
12287 msg("Exiting...\n");
12288 if (!Doxygen::filterDBFileName.empty())
12289 {
12290 thisDir.remove(Doxygen::filterDBFileName.str());
12291 }
12292 }
12293}
12294
12295static DString createOutputDirectory(const DString &baseDirName,
12296 const DString &formatDirName,
12297 const char *defaultDirName)
12298{
12299 DString result = formatDirName;
12300 if (result.empty())
12301 {
12302 result = baseDirName + defaultDirName;
12303 }
12304 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12305 {
12306 result.prepend(baseDirName+"/");
12307 }
12308 Dir formatDir(result.str());
12309 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12310 {
12311 term("Could not create output directory {}\n", result);
12312 }
12313 return result;
12314}
12315
12317{
12318 StringUnorderedSet killSet;
12319
12320 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12321 bool alwaysRecursive = Config_getBool(RECURSIVE);
12322 StringUnorderedSet excludeNameSet;
12323
12324 // gather names of all files in the include path
12325 g_s.begin("Searching for include files...\n");
12326 killSet.clear();
12327 StringVector includePathList = Config_getList(INCLUDE_PATH);
12328 for (const auto &s : includePathList)
12329 {
12330 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12331 StringVector pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12332 Config_getList(INCLUDE_FILE_PATTERNS);
12333 readFileOrDirectory(s, // s
12335 nullptr, // exclSet
12336 &pl, // patList
12337 &exclPatterns, // exclPatList
12338 nullptr, // resultList
12339 nullptr, // resultSet
12340 false, // INCLUDE_PATH isn't recursive
12341 true, // errorIfNotExist
12342 &killSet); // killSet
12343 }
12344 g_s.end();
12345
12346 g_s.begin("Searching for example files...\n");
12347 killSet.clear();
12348 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
12349 for (const auto &s : examplePathList)
12350 {
12351 StringVector patterns = Config_getList(EXAMPLE_PATTERNS);
12352 readFileOrDirectory(s, // s
12354 nullptr, // exclSet
12355 &patterns, // patList
12356 nullptr, // exclPatList
12357 nullptr, // resultList
12358 nullptr, // resultSet
12359 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12360 true, // errorIfNotExist
12361 &killSet); // killSet
12362 }
12363 g_s.end();
12364
12365 g_s.begin("Searching for images...\n");
12366 killSet.clear();
12367 StringVector imagePathList=Config_getList(IMAGE_PATH);
12368 for (const auto &s : imagePathList)
12369 {
12370 readFileOrDirectory(s, // s
12372 nullptr, // exclSet
12373 nullptr, // patList
12374 nullptr, // exclPatList
12375 nullptr, // resultList
12376 nullptr, // resultSet
12377 alwaysRecursive, // recursive
12378 true, // errorIfNotExist
12379 &killSet); // killSet
12380 }
12381 g_s.end();
12382
12383 g_s.begin("Searching for dot files...\n");
12384 killSet.clear();
12385 StringVector dotFileList=Config_getList(DOTFILE_DIRS);
12386 for (const auto &s : dotFileList)
12387 {
12388 readFileOrDirectory(s, // s
12390 nullptr, // exclSet
12391 nullptr, // patList
12392 nullptr, // exclPatList
12393 nullptr, // resultList
12394 nullptr, // resultSet
12395 alwaysRecursive, // recursive
12396 true, // errorIfNotExist
12397 &killSet); // killSet
12398 }
12399 g_s.end();
12400
12401 g_s.begin("Searching for msc files...\n");
12402 killSet.clear();
12403 StringVector mscFileList=Config_getList(MSCFILE_DIRS);
12404 for (const auto &s : mscFileList)
12405 {
12406 readFileOrDirectory(s, // s
12408 nullptr, // exclSet
12409 nullptr, // patList
12410 nullptr, // exclPatList
12411 nullptr, // resultList
12412 nullptr, // resultSet
12413 alwaysRecursive, // recursive
12414 true, // errorIfNotExist
12415 &killSet); // killSet
12416 }
12417 g_s.end();
12418
12419 g_s.begin("Searching for dia files...\n");
12420 killSet.clear();
12421 StringVector diaFileList=Config_getList(DIAFILE_DIRS);
12422 for (const auto &s : diaFileList)
12423 {
12424 readFileOrDirectory(s, // s
12426 nullptr, // exclSet
12427 nullptr, // patList
12428 nullptr, // exclPatList
12429 nullptr, // resultList
12430 nullptr, // resultSet
12431 alwaysRecursive, // recursive
12432 true, // errorIfNotExist
12433 &killSet); // killSet
12434 }
12435 g_s.end();
12436
12437 g_s.begin("Searching for plantuml files...\n");
12438 killSet.clear();
12439 StringVector plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12440 for (const auto &s : plantUmlFileList)
12441 {
12442 readFileOrDirectory(s, // s
12444 nullptr, // exclSet
12445 nullptr, // patList
12446 nullptr, // exclPatList
12447 nullptr, // resultList
12448 nullptr, // resultSet
12449 alwaysRecursive, // recursive
12450 true, // errorIfNotExist
12451 &killSet); // killSet
12452 }
12453 g_s.end();
12454
12455 g_s.begin("Searching for mermaid files...\n");
12456 killSet.clear();
12457 StringVector mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12458 for (const auto &s : mermaidFileList)
12459 {
12460 readFileOrDirectory(s, // s
12462 nullptr, // exclSet
12463 nullptr, // patList
12464 nullptr, // exclPatList
12465 nullptr, // resultList
12466 nullptr, // resultSet
12467 alwaysRecursive, // recursive
12468 true, // errorIfNotExist
12469 &killSet); // killSet
12470 }
12471 g_s.end();
12472
12473 g_s.begin("Searching for files to exclude\n");
12474 StringVector excludeList = Config_getList(EXCLUDE);
12475 for (const auto &s : excludeList)
12476 {
12477 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12478 readFileOrDirectory(s, // s
12479 nullptr, // fnDict
12480 nullptr, // exclSet
12481 &filePatterns, // patList
12482 nullptr, // exclPatList
12483 nullptr, // resultList
12484 &excludeNameSet, // resultSet
12485 alwaysRecursive, // recursive
12486 false); // errorIfNotExist
12487 }
12488 g_s.end();
12489
12490 /**************************************************************************
12491 * Determine Input Files *
12492 **************************************************************************/
12493
12494 g_s.begin("Searching INPUT for files to process...\n");
12495 killSet.clear();
12496 Doxygen::inputPaths.clear();
12497 StringVector inputList=Config_getList(INPUT);
12498 for (const auto &s : inputList)
12499 {
12500 DString path = s;
12501 size_t l = path.length();
12502 if (l>0)
12503 {
12504 // strip trailing slashes
12505 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12506
12507 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12509 path, // s
12511 &excludeNameSet, // exclSet
12512 &filePatterns, // patList
12513 &exclPatterns, // exclPatList
12514 &g_inputFiles, // resultList
12515 nullptr, // resultSet
12516 alwaysRecursive, // recursive
12517 true, // errorIfNotExist
12518 &killSet, // killSet
12519 &Doxygen::inputPaths); // paths
12520 }
12521 }
12522
12523 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12524 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12525 {
12526 if (fileName->size()>1)
12527 {
12528 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12529 {
12530 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12531 });
12532 }
12533 }
12534 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12536 [](const auto &f1,const auto &f2)
12537 {
12538 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12539 });
12540 if (Doxygen::inputNameLinkedMap->empty())
12541 {
12542 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12543 }
12544 g_s.end();
12545}
12546
12547
12549{
12550 if (Config_getBool(MARKDOWN_SUPPORT))
12551 {
12552 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12553 if (mdfileAsMainPage.empty()) return;
12554 FileInfo fi(mdfileAsMainPage.data());
12555 if (!fi.exists())
12556 {
12557 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12558 return;
12559 }
12560 bool ambig = false;
12561 if (Doxygen::inputNameLinkedMap->findFileDef(fi.absFilePath(),ambig)==nullptr)
12562 {
12563 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12564 return;
12565 }
12566 }
12567}
12568
12570{
12571 AUTO_TRACE();
12572 std::atexit(exitDoxygen);
12573
12574 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12575
12576#if USE_LIBCLANG
12577 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12578#endif
12579
12580 // we would like to show the versionString earlier, but we first have to handle the configuration file
12581 // to know the value of the QUIET setting.
12582 DString versionString = getFullVersion();
12583 msg("Doxygen version used: {}\n",versionString);
12584
12586
12587 /**************************************************************************
12588 * Make sure the output directory exists
12589 **************************************************************************/
12590 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12591 if (!g_singleComment)
12592 {
12593 if (outputDirectory.empty())
12594 {
12595 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12596 }
12597 else
12598 {
12599 Dir dir(outputDirectory.str());
12600 if (!dir.exists())
12601 {
12603 if (!dir.mkdir(outputDirectory.str()))
12604 {
12605 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12606 "exist and cannot be created\n",outputDirectory);
12607 }
12608 else
12609 {
12610 msg("Notice: Output directory '{}' does not exist. "
12611 "I have created it for you.\n", outputDirectory);
12612 }
12613 dir.setPath(outputDirectory.str());
12614 }
12615 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12616 }
12617 }
12618 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12619
12620 /**************************************************************************
12621 * Initialize global lists and dictionaries
12622 **************************************************************************/
12623
12624#ifdef HAS_SIGNALS
12625 signal(SIGINT, stopDoxygen);
12626#endif
12627
12628 uint32_t pid = Portable::pid();
12629 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12630 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12631
12632 /**************************************************************************
12633 * Check/create output directories *
12634 **************************************************************************/
12635
12636 bool generateHtml = Config_getBool(GENERATE_HTML);
12637 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12638 bool generateXml = Config_getBool(GENERATE_XML);
12639 bool generateLatex = Config_getBool(GENERATE_LATEX);
12640 bool generateRtf = Config_getBool(GENERATE_RTF);
12641 bool generateMan = Config_getBool(GENERATE_MAN);
12642 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12643 DString htmlOutput;
12644 DString docbookOutput;
12645 DString xmlOutput;
12646 DString latexOutput;
12647 DString rtfOutput;
12648 DString manOutput;
12649 DString sqlOutput;
12650
12651 if (!g_singleComment)
12652 {
12653 if (generateHtml)
12654 {
12655 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12656 Config_updateString(HTML_OUTPUT,htmlOutput);
12657
12658 DString sitemapUrl = Config_getString(SITEMAP_URL);
12659 bool generateSitemap = !sitemapUrl.empty();
12660 if (generateSitemap && !sitemapUrl.endsWith("/"))
12661 {
12662 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12663 }
12664
12665 // add HTML indexers that are enabled
12666 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12667 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12668 bool generateQhp = Config_getBool(GENERATE_QHP);
12669 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12670 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12671 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12672 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12673 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12674 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12675 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12676 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12679 }
12680
12681 if (generateDocbook)
12682 {
12683 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12684 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12685 }
12686
12687 if (generateXml)
12688 {
12689 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12690 Config_updateString(XML_OUTPUT,xmlOutput);
12691 }
12692
12693 if (generateLatex)
12694 {
12695 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12696 Config_updateString(LATEX_OUTPUT,latexOutput);
12697 }
12698
12699 if (generateRtf)
12700 {
12701 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12702 Config_updateString(RTF_OUTPUT,rtfOutput);
12703 }
12704
12705 if (generateMan)
12706 {
12707 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12708 Config_updateString(MAN_OUTPUT,manOutput);
12709 }
12710
12711 if (generateSql)
12712 {
12713 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12714 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12715 }
12716 }
12717
12718 if (Config_getBool(HAVE_DOT))
12719 {
12720 DString curFontPath = Config_getString(DOT_FONTPATH);
12721 if (curFontPath.empty())
12722 {
12723 Portable::getenv("DOTFONTPATH");
12724 DString newFontPath = ".";
12725 if (!curFontPath.empty())
12726 {
12727 newFontPath+=Portable::pathListSeparator();
12728 newFontPath+=curFontPath;
12729 }
12730 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12731 }
12732 else
12733 {
12734 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12735 }
12736 }
12737
12738 /**************************************************************************
12739 * Handle layout file *
12740 **************************************************************************/
12741
12743 DString layoutFileName = Config_getString(LAYOUT_FILE);
12744 bool defaultLayoutUsed = false;
12745 if (layoutFileName.empty())
12746 {
12747 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12748 defaultLayoutUsed = true;
12749 }
12750 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12751
12752 FileInfo fi(layoutFileName.str());
12753 if (fi.exists())
12754 {
12755 msg("Parsing layout file {}...\n",layoutFileName);
12756 LayoutDocManager::instance().parse(layoutFileName);
12757 }
12758 else if (!defaultLayoutUsed)
12759 {
12760 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12761 }
12762 printLayout();
12763
12764 /**************************************************************************
12765 * Read and preprocess input *
12766 **************************************************************************/
12767
12768 // prevent search in the output directories
12769 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12770 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12771 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12772 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12773 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12774 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12775 if (generateMan) exclPatterns.push_back(manOutput.str());
12776 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12777
12778 if (!g_singleComment)
12779 {
12781
12783 }
12784
12785 // Notice: the order of the function calls below is very important!
12786
12787 if (generateHtml && !Config_getBool(USE_MATHJAX))
12788 {
12790 }
12791 if (generateRtf)
12792 {
12794 }
12795 if (generateDocbook)
12796 {
12798 }
12799
12801
12802 /**************************************************************************
12803 * Handle Tag Files *
12804 **************************************************************************/
12805
12806 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12807
12808 if (!g_singleComment)
12809 {
12810 msg("Reading and parsing tag files\n");
12811 StringVector tagFileList = Config_getList(TAGFILES);
12812 for (const auto &s : tagFileList)
12813 {
12814 readTagFile(root,s.c_str());
12815 }
12816 }
12817
12818 /**************************************************************************
12819 * Parse source files *
12820 **************************************************************************/
12821
12822 addSTLSupport(root);
12823
12824 g_s.begin("Parsing files\n");
12825 if (g_singleComment)
12826 {
12827 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12828 if (g_commentFileName=="-")
12829 {
12830 std::string text = fileToString(g_commentFileName).str();
12831 addTerminalCharIfMissing(text,'\n');
12832 generateHtmlForComment("stdin.md",text);
12833 }
12834 else if (FileInfo(g_commentFileName.str()).isFile())
12835 {
12836 std::string text;
12838 addTerminalCharIfMissing(text,'\n');
12840 }
12841 else
12842 {
12843 }
12845 exit(0);
12846 }
12847 else
12848 {
12849 if (Config_getInt(NUM_PROC_THREADS)==1)
12850 {
12852 }
12853 else
12854 {
12856 }
12857 }
12858 g_s.end();
12859
12860 /**************************************************************************
12861 * Gather information *
12862 **************************************************************************/
12863
12864 g_s.begin("Building macro definition list...\n");
12866 g_s.end();
12867
12868 g_s.begin("Building group list...\n");
12869 buildGroupList(root.get());
12870 organizeSubGroups(root.get());
12871 g_s.end();
12872
12873 g_s.begin("Building directory list...\n");
12875 findDirDocumentation(root.get());
12876 g_s.end();
12877
12878 g_s.begin("Building namespace list...\n");
12879 buildNamespaceList(root.get());
12880 findUsingDirectives(root.get());
12881 g_s.end();
12882
12883 g_s.begin("Building file list...\n");
12884 buildFileList(root.get());
12885 g_s.end();
12886
12887 g_s.begin("Building class list...\n");
12888 buildClassList(root.get());
12889 g_s.end();
12890
12891 g_s.begin("Building concept list...\n");
12892 buildConceptList(root.get());
12893 g_s.end();
12894
12895 // build list of using declarations here (global list)
12896 buildListOfUsingDecls(root.get());
12897 g_s.end();
12898
12899 g_s.begin("Computing nesting relations for classes...\n");
12901 g_s.end();
12902 // 1.8.2-20121111: no longer add nested classes to the group as well
12903 //distributeClassGroupRelations();
12904
12905 // calling buildClassList may result in cached relations that
12906 // become invalid after resolveClassNestingRelations(), that's why
12907 // we need to clear the cache here
12909 // we don't need the list of using declaration anymore
12910 g_usingDeclarations.clear();
12911
12912 g_s.begin("Associating documentation with classes...\n");
12913 buildClassDocList(root.get());
12914 g_s.end();
12915
12916 g_s.begin("Associating documentation with concepts...\n");
12917 buildConceptDocList(root.get());
12919 g_s.end();
12920
12921 g_s.begin("Associating documentation with modules...\n");
12922 findModuleDocumentation(root.get());
12923 g_s.end();
12924
12925 g_s.begin("Building example list...\n");
12926 buildExampleList(root.get());
12927 g_s.end();
12928
12929 g_s.begin("Searching for enumerations...\n");
12930 findEnums(root.get());
12931 g_s.end();
12932
12933 // Since buildVarList calls isVarWithConstructor
12934 // and this calls getResolvedClass we need to process
12935 // typedefs first so the relations between classes via typedefs
12936 // are properly resolved. See bug 536385 for an example.
12937 g_s.begin("Searching for documented typedefs...\n");
12938 buildTypedefList(root.get());
12939 g_s.end();
12940
12941 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12942 {
12943 g_s.begin("Searching for documented sequences...\n");
12944 buildSequenceList(root.get());
12945 g_s.end();
12946
12947 g_s.begin("Searching for documented dictionaries...\n");
12948 buildDictionaryList(root.get());
12949 g_s.end();
12950 }
12951
12952 g_s.begin("Searching for members imported via using declarations...\n");
12953 // this should be after buildTypedefList in order to properly import
12954 // used typedefs
12955 findUsingDeclarations(root.get(),true); // do for python packages first
12956 findUsingDeclarations(root.get(),false); // then the rest
12957 g_s.end();
12958
12959 g_s.begin("Searching for included using directives...\n");
12961 g_s.end();
12962
12963 g_s.begin("Searching for documented variables...\n");
12964 buildVarList(root.get());
12965 g_s.end();
12966
12967 g_s.begin("Building interface member list...\n");
12968 buildInterfaceAndServiceList(root.get()); // UNO IDL
12969
12970 g_s.begin("Building member list...\n"); // using class info only !
12971 buildFunctionList(root.get());
12972 g_s.end();
12973
12974 g_s.begin("Searching for friends...\n");
12975 findFriends();
12976 g_s.end();
12977
12978 g_s.begin("Searching for documented defines...\n");
12979 findDefineDocumentation(root.get());
12980 g_s.end();
12981
12982 g_s.begin("Computing class inheritance relations...\n");
12983 findClassEntries(root.get());
12985 g_s.end();
12986
12987 g_s.begin("Computing class usage relations...\n");
12989 g_s.end();
12990
12991 g_s.begin("Flushing cached template relations that have become invalid...\n");
12993 g_s.end();
12994
12995 g_s.begin("Warn for undocumented namespaces...\n");
12997 g_s.end();
12998
12999 g_s.begin("Computing class relations...\n");
13002 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
13003 {
13005 }
13007 g_classEntries.clear();
13008 g_s.end();
13009
13010 g_s.begin("Add enum values to enums...\n");
13011 addEnumValuesToEnums(root.get());
13012 findEnumDocumentation(root.get());
13013 g_s.end();
13014
13015 g_s.begin("Searching for member function documentation...\n");
13016 findObjCMethodDefinitions(root.get());
13017 findMemberDocumentation(root.get()); // may introduce new members !
13018 findUsingDeclImports(root.get()); // may introduce new members !
13019 g_usingClassMap.clear();
13023 g_s.end();
13024
13025 // moved to after finding and copying documentation,
13026 // as this introduces new members see bug 722654
13027 g_s.begin("Creating members for template instances...\n");
13029 g_s.end();
13030
13031 g_s.begin("Searching for tag less structs...\n");
13033 g_s.end();
13034
13035 g_s.begin("Building page list...\n");
13036 buildPageList(root.get());
13037 g_s.end();
13038
13039 g_s.begin("Building requirements list...\n");
13040 buildRequirementsList(root.get());
13041 g_s.end();
13042
13043 g_s.begin("Search for main page...\n");
13044 findMainPage(root.get());
13045 findMainPageTagFiles(root.get());
13046 g_s.end();
13047
13048 g_s.begin("Computing page relations...\n");
13049 computePageRelations(root.get());
13051 g_s.end();
13052
13053 g_s.begin("Determining the scope of groups...\n");
13054 findGroupScope(root.get());
13055 g_s.end();
13056
13057 g_s.begin("Computing module relations...\n");
13058 auto &mm = ModuleManager::instance();
13059 mm.resolvePartitions();
13060 mm.resolveImports();
13061 mm.collectExportedSymbols();
13062 g_s.end();
13063
13064 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13065 {
13066 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13067 n2->memberName().data()+getPrefixIndex(n2->memberName())
13068 )<0;
13069 };
13070
13071 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13072 {
13073 if (Config_getBool(SORT_BY_SCOPE_NAME))
13074 {
13075 return dstricmp_sort(c1->name(), c2->name())<0;
13076 }
13077 else
13078 {
13079 int i = dstricmp_sort(c1->className(), c2->className());
13080 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13081 }
13082 };
13083
13084 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13085 {
13086 return dstricmp_sort(n1->name(),n2->name())<0;
13087 };
13088
13089 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13090 {
13091 return dstricmp_sort(c1->name(),c2->name())<0;
13092 };
13093
13094 g_s.begin("Sorting lists...\n");
13095 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13097 memberNameComp);
13098 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13100 memberNameComp);
13101 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13103 classComp);
13104 std::stable_sort(Doxygen::classLinkedMap->begin(),
13106 classComp);
13107 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13109 conceptComp);
13110 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13112 namespaceComp);
13113 g_s.end();
13114
13115 g_s.begin("Determining which enums are documented\n");
13117 g_s.end();
13118
13119 g_s.begin("Computing member relations...\n");
13122 g_s.end();
13123
13124 g_s.begin("Building full member lists recursively...\n");
13126 g_s.end();
13127
13128 g_s.begin("Adding members to member groups.\n");
13130 g_s.end();
13131
13132 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13133 {
13134 g_s.begin("Distributing member group documentation.\n");
13136 g_s.end();
13137 }
13138
13139 g_s.begin("Computing member references...\n");
13141 g_s.end();
13142
13143 if (Config_getBool(INHERIT_DOCS))
13144 {
13145 g_s.begin("Inheriting documentation...\n");
13147 g_s.end();
13148 }
13149
13150
13151 // compute the shortest possible names of all files
13152 // without losing the uniqueness of the file names.
13153 g_s.begin("Generating disk names...\n");
13155 g_s.end();
13156
13157 g_s.begin("Adding source references...\n");
13159 g_s.end();
13160
13161 g_s.begin("Adding xrefitems...\n");
13164 g_s.end();
13165
13166 g_s.begin("Adding requirements...\n");
13169 g_s.end();
13170
13171 g_s.begin("Sorting member lists...\n");
13173 g_s.end();
13174
13175 g_s.begin("Setting anonymous enum type...\n");
13177 g_s.end();
13178
13179 g_s.begin("Computing dependencies between directories...\n");
13181 g_s.end();
13182
13183 g_s.begin("Generating citations page...\n");
13185 g_s.end();
13186
13187 g_s.begin("Counting data structures...\n");
13189 g_s.end();
13190
13191 g_s.begin("Resolving user defined references...\n");
13193 g_s.end();
13194
13195 g_s.begin("Finding anchors and sections in the documentation...\n");
13197 g_s.end();
13198
13199 g_s.begin("Transferring function references...\n");
13201 g_s.end();
13202
13203 g_s.begin("Combining using relations...\n");
13205 g_s.end();
13206
13208 g_s.begin("Adding members to index pages...\n");
13210 addToIndices();
13211 g_s.end();
13212
13213 g_s.begin("Correcting members for VHDL...\n");
13215 g_s.end();
13216
13217 g_s.begin("Computing tooltip texts...\n");
13219 g_s.end();
13220
13221 if (Config_getBool(SORT_GROUP_NAMES))
13222 {
13223 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13225 [](const auto &g1,const auto &g2)
13226 { return g1->groupTitle() < g2->groupTitle(); });
13227
13228 for (const auto &gd : *Doxygen::groupLinkedMap)
13229 {
13230 gd->sortSubGroups();
13231 }
13232 }
13233
13234 printNavTree(root.get(),0);
13236}
13237
13239{
13240 AUTO_TRACE();
13241 /**************************************************************************
13242 * Initialize output generators *
13243 **************************************************************************/
13244
13245 /// add extra languages for which we can only produce syntax highlighted code
13247
13248 //// dump all symbols
13249 if (g_dumpSymbolMap)
13250 {
13251 dumpSymbolMap();
13252 exit(0);
13253 }
13254
13255 bool generateHtml = Config_getBool(GENERATE_HTML);
13256 bool generateLatex = Config_getBool(GENERATE_LATEX);
13257 bool generateMan = Config_getBool(GENERATE_MAN);
13258 bool generateRtf = Config_getBool(GENERATE_RTF);
13259 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13260
13261
13263 if (generateHtml)
13264 {
13268 }
13269 if (generateLatex)
13270 {
13273 }
13274 if (generateDocbook)
13275 {
13278 }
13279 if (generateMan)
13280 {
13283 }
13284 if (generateRtf)
13285 {
13288 }
13289 if (Config_getBool(USE_HTAGS))
13290 {
13291 Htags::useHtags = true;
13292 DString htmldir = Config_getString(HTML_OUTPUT);
13293 if (!Htags::execute(htmldir))
13294 err("USE_HTAGS is YES but htags(1) failed. \n");
13295 else if (!Htags::loadFilemap(htmldir))
13296 err("htags(1) ended normally but failed to load the filemap. \n");
13297 }
13298
13299 /**************************************************************************
13300 * Generate documentation *
13301 **************************************************************************/
13302
13303 g_s.begin("Generating style sheet...\n");
13304 //printf("writing style info\n");
13305 g_outputList->writeStyleInfo(0); // write first part
13306 g_s.end();
13307
13308 bool searchEngine = Config_getBool(SEARCHENGINE);
13309 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13310
13311 g_s.begin("Generating search indices...\n");
13312 if (searchEngine && !serverBasedSearch && generateHtml)
13313 {
13315 }
13316
13317 // generate search indices (need to do this before writing other HTML
13318 // pages as these contain a drop down menu with options depending on
13319 // what categories we find in this function.
13320 if (generateHtml && searchEngine)
13321 {
13322 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13323 Dir searchDir(searchDirName.str());
13324 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13325 {
13326 term("Could not create search results directory '{}' $PWD='{}'\n",
13327 searchDirName,Dir::currentDirPath());
13328 }
13329 HtmlGenerator::writeSearchData(searchDirName);
13330 if (!serverBasedSearch) // client side search index
13331 {
13333 }
13334 }
13335 g_s.end();
13336
13337 // copy static stuff
13338 if (generateHtml)
13339 {
13341 copyLogo(Config_getString(HTML_OUTPUT),true);
13342 copyIcon(Config_getString(HTML_OUTPUT),true);
13343 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13344 }
13345 if (generateLatex)
13346 {
13348 copyLogo(Config_getString(LATEX_OUTPUT),false);
13349 copyIcon(Config_getString(LATEX_OUTPUT),false);
13350 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13351 }
13352 if (generateDocbook)
13353 {
13354 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13355 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13356 }
13357 if (generateRtf)
13358 {
13359 copyLogo(Config_getString(RTF_OUTPUT),false);
13360 copyIcon(Config_getString(RTF_OUTPUT),false);
13361 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13362 }
13363
13365 if (fm.hasFormulas() && generateHtml
13366 && !Config_getBool(USE_MATHJAX))
13367 {
13368 g_s.begin("Generating images for formulas in HTML...\n");
13369 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13371 g_s.end();
13372 }
13373 if (fm.hasFormulas() && generateRtf)
13374 {
13375 g_s.begin("Generating images for formulas in RTF...\n");
13377 g_s.end();
13378 }
13379
13380 if (fm.hasFormulas() && generateDocbook)
13381 {
13382 g_s.begin("Generating images for formulas in Docbook...\n");
13384 g_s.end();
13385 }
13386
13387 g_s.begin("Generating example documentation...\n");
13389 g_s.end();
13390
13391 g_s.begin("Generating file sources...\n");
13393 g_s.end();
13394
13395 g_s.begin("Counting members...\n");
13396 // needs to be done after generating the sources
13397 // but before generating the compound documentation, see bug #12233
13398 countMembers();
13399 g_s.end();
13400
13401 g_s.begin("Generating file documentation...\n");
13403 g_s.end();
13404
13405 g_s.begin("Generating page documentation...\n");
13407 g_s.end();
13408
13409 g_s.begin("Generating group documentation...\n");
13411 g_s.end();
13412
13413 g_s.begin("Generating class documentation...\n");
13415 g_s.end();
13416
13417 g_s.begin("Generating concept documentation...\n");
13419 g_s.end();
13420
13421 g_s.begin("Generating module documentation...\n");
13423 g_s.end();
13424
13425 g_s.begin("Generating namespace documentation...\n");
13427 g_s.end();
13428
13429 if (Config_getBool(GENERATE_LEGEND))
13430 {
13431 g_s.begin("Generating graph info page...\n");
13433 g_s.end();
13434 }
13435
13436 g_s.begin("Generating directory documentation...\n");
13438 g_s.end();
13439
13440 if (g_outputList->size()>0)
13441 {
13443 }
13444
13445 g_s.begin("finalizing index lists...\n");
13447 g_s.end();
13448
13449 g_s.begin("writing tag file...\n");
13450 writeTagFile();
13451 g_s.end();
13452
13453 if (Config_getBool(GENERATE_XML))
13454 {
13455 g_s.begin("Generating XML output...\n");
13457 generateXML();
13459 g_s.end();
13460 }
13461 if (Config_getBool(GENERATE_SQLITE3))
13462 {
13463 g_s.begin("Generating SQLITE3 output...\n");
13465 g_s.end();
13466 }
13467
13468 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13469 {
13470 g_s.begin("Generating AutoGen DEF output...\n");
13471 generateDEF();
13472 g_s.end();
13473 }
13474 if (Config_getBool(GENERATE_PERLMOD))
13475 {
13476 g_s.begin("Generating Perl module output...\n");
13478 g_s.end();
13479 }
13480 if (generateHtml && searchEngine && serverBasedSearch)
13481 {
13482 g_s.begin("Generating search index\n");
13483 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13484 {
13486 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13487 }
13488 else // write data for external search index
13489 {
13491 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13492 if (searchDataFile.empty())
13493 {
13494 searchDataFile="searchdata.xml";
13495 }
13496 if (!Portable::isAbsolutePath(searchDataFile.data()))
13497 {
13498 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13499 }
13500 Doxygen::searchIndex.write(searchDataFile);
13501 }
13502 g_s.end();
13503 }
13504
13505 if (generateRtf)
13506 {
13507 g_s.begin("Combining RTF output...\n");
13508 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13509 {
13510 err("An error occurred during post-processing the RTF files!\n");
13511 }
13512 g_s.end();
13513 }
13514
13515 if (PlantumlManager::instance().needToRun())
13516 {
13517 g_s.begin("Running plantuml with JAVA...\n");
13519 g_s.end();
13520 }
13521
13522 if (MermaidManager::instance().needToRun())
13523 {
13524 g_s.begin("Running mermaid (mmdc)...\n");
13526 g_s.end();
13527 }
13528
13529 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13530 {
13531 g_s.begin("Running dot...\n");
13533 g_s.end();
13534 }
13535
13536 if (generateHtml &&
13537 Config_getBool(GENERATE_HTMLHELP) &&
13538 !Config_getString(HHC_LOCATION).empty())
13539 {
13540 g_s.begin("Running html help compiler...\n");
13542 g_s.end();
13543 }
13544
13545 if ( generateHtml &&
13546 Config_getBool(GENERATE_QHP) &&
13547 !Config_getString(QHG_LOCATION).empty())
13548 {
13549 g_s.begin("Running qhelpgenerator...\n");
13551 g_s.end();
13552 }
13553
13556
13558
13560 {
13561
13562 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13563 if (numThreads<1) numThreads=1;
13564 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13565 (static_cast<double>(Debug::elapsedTime())),
13566 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13567 );
13568 g_s.print();
13569
13571 msg("finished...\n");
13573 }
13574 else
13575 {
13576 msg("finished...\n");
13577 }
13578
13579
13580 /**************************************************************************
13581 * Start cleaning up *
13582 **************************************************************************/
13583
13585
13587 Dir thisDir;
13588 thisDir.remove(Doxygen::filterDBFileName.str());
13590 exitTracing();
13592 delete Doxygen::clangUsrMap;
13593 g_successfulRun=true;
13594
13595 //dumpDocNodeSizes();
13596}
void readAliases()
Definition aliases.cpp:162
constexpr auto prefix
Definition anchor.cpp:47
This class contains the information about the argument of a function or template.
Definition arguments.h:27
DString defval
Definition arguments.h:47
DString array
Definition arguments.h:46
DString name
Definition arguments.h:45
DString type
Definition arguments.h:43
This class represents an function or template argument list.
Definition arguments.h:66
RefQualifierType refQualifier() const
Definition arguments.h:117
bool noParameters() const
Definition arguments.h:118
bool pureSpecifier() const
Definition arguments.h:114
iterator end()
Definition arguments.h:95
bool hasParameters() const
Definition arguments.h:77
DString trailingReturnType() const
Definition arguments.h:115
bool isDeleted() const
Definition arguments.h:116
size_t size() const
Definition arguments.h:101
void setPureSpecifier(bool b)
Definition arguments.h:122
bool constSpecifier() const
Definition arguments.h:112
void setTrailingReturnType(const DString &s)
Definition arguments.cpp:40
void push_back(const Argument &a)
Definition arguments.h:103
bool empty() const
Definition arguments.h:100
void setConstSpecifier(bool b)
Definition arguments.h:120
void setRefQualifier(RefQualifierType t)
Definition arguments.h:127
void setIsDeleted(bool b)
Definition arguments.h:126
iterator begin()
Definition arguments.h:94
bool volatileSpecifier() const
Definition arguments.h:113
void setNoParameters(bool b)
Definition arguments.h:128
void setVolatileSpecifier(bool b)
Definition arguments.h:121
static CitationManager & instance()
Definition cite.cpp:90
void clear()
clears the database
Definition cite.cpp:115
void generatePage()
Generate the citations page.
Definition cite.cpp:336
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:100
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:105
@ Singleton
Definition classdef.h:113
@ Interface
Definition classdef.h:108
@ Exception
Definition classdef.h:111
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 file in which this compound's definition can be found.
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 ClassDef * insertTemplateInstance(const DString &fileName, int startLine, size_t startColumn, const DString &templSpec, bool &freshInstance)=0
virtual void setClassName(const DString &name)=0
virtual void setFileDef(FileDef *fd)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=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 addDocPart(const DString &doc, int lineNr, size_t colNr)=0
virtual void addCodePart(const DString &code, int lineNr, size_t colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=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:84
void clear()
Definition dstring.h:214
DString & setNum(short n)
Definition dstring.h:552
void resize(size_t newlen)
Definition dstring.h:209
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:278
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:154
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:223
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:178
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:660
DString right(size_t len) const
Definition dstring.h:311
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:154
DString & prepend(const char *s)
Definition dstring.h:515
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
int toInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:191
@ ExplicitSize
Definition dstring.h:131
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
bool stripPrefix(const DString &prefix)
Definition dstring.h:290
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:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
@ ExtCmd
Definition debug.h:37
@ Sections
Definition debug.h:49
@ Time
Definition debug.h:36
@ Qhp
Definition debug.h:45
@ Entries
Definition debug.h:48
static void printFlags()
Definition debug.cpp:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:78
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const DString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
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 size_t getDefColumn() 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 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 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 setDefFile(const DString &df, int defLine, size_t defColumn)=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:108
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:73
static std::string currentDirPath()
Definition dir.cpp:348
std::string absPath() const
Definition dir.cpp:370
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:301
void setPath(const std::string &path)
Definition dir.cpp:235
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
DirIterator iterator() const
Definition dir.cpp:245
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:363
static bool setCurrent(const std::string &path)
Definition dir.cpp:356
bool exists() const
Definition dir.cpp:263
A linked map of directories.
Definition dirdef.h:173
A class that generates docset files.
Definition docsets.h:35
static void init()
bool run()
Definition dot.cpp:119
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:108
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:90
static bool suppressDocWarnings
Definition doxygen.h:123
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:102
static bool parseSourcesNeeded
Definition doxygen.h:116
static StringUnorderedSet inputPaths
Definition doxygen.h:96
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:93
static bool clangAssistedParsing
Definition doxygen.h:129
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:112
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static DString filterDBFileName
Definition doxygen.h:124
static ParserManager * parserManager
Definition doxygen.h:122
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:131
static DString verifiedDotPath
Definition doxygen.h:130
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:88
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:105
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:91
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:99
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:100
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:103
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:111
static IndexList * indexList
Definition doxygen.h:125
static StaticInitMap staticInitMap
Definition doxygen.h:134
static StringMap tagDestinationMap
Definition doxygen.h:109
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:132
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:89
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:101
static DString spaces
Definition doxygen.h:126
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static bool generatingXmlOutput
Definition doxygen.h:127
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:113
static DString htmlFileExtension
Definition doxygen.h:115
static DefinesPerFileList macroDefinitions
Definition doxygen.h:128
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:120
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:106
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:104
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:118
static StringUnorderedSet tagFileSet
Definition doxygen.h:110
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:94
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:95
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static DirRelationLinkedMap dirRelations
Definition doxygen.h:121
static std::mutex addExampleMutex
Definition doxygen.h:133
static ClangUsrMap * clangUsrMap
Definition doxygen.h:119
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:107
Generator for Eclipse help files.
Definition eclipsehelp.h:42
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1981
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:2002
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:115
TextStream initializer
initial value (for variables)
Definition entry.h:197
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:182
bool subGrouping
automatically group class members?
Definition entry.h:187
RequirementRefs rqli
references to requirements
Definition entry.h:227
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:138
bool proto
prototype ?
Definition entry.h:186
GroupDocType groupDocType
Definition entry.h:231
int docLine
line number at which the documentation was found
Definition entry.h:201
DString includeName
include name (3 arg of \class)
Definition entry.h:199
DString bitfields
member's bit fields
Definition entry.h:192
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:215
void markAsProcessed() const
Definition entry.h:166
int endBodyLine
line number where the definition ends
Definition entry.h:218
DString write
property write accessor
Definition entry.h:212
bool exported
is the symbol exported from a C++20 module
Definition entry.h:188
const TagInfo * tagInfo() const
Definition entry.h:176
DString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:198
DString fileName
file this entry was extracted from
Definition entry.h:223
ArgumentLists tArgLists
template argument declarations
Definition entry.h:195
DString docFile
file in which the documentation was found
Definition entry.h:202
LocalToc localToc
Definition entry.h:233
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:180
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:120
DString args
member argument string
Definition entry.h:191
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:228
Entry * parent() const
Definition entry.h:133
DString inside
name of the class in which documents are found
Definition entry.h:213
DString doc
documentation block (partly parsed)
Definition entry.h:200
DString req
C++20 requires clause.
Definition entry.h:235
bool explicitExternal
explicitly defined as external?
Definition entry.h:185
DString brief
brief description (doc block)
Definition entry.h:203
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:222
RelatesType relatesType
how relates is handled
Definition entry.h:210
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:221
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:189
int startLine
start line of entry in the source
Definition entry.h:224
size_t startColumn
start column of entry in the source
Definition entry.h:225
ArgumentList argList
member arguments as a list
Definition entry.h:194
DString type
member type
Definition entry.h:172
int inbodyLine
line number at which the body doc was found
Definition entry.h:207
EntryType section
entry type (see Sections);
Definition entry.h:171
int bodyLine
line number of the body in the source
Definition entry.h:216
DString exception
throw specification
Definition entry.h:214
DString relates
related class (doc block)
Definition entry.h:209
int mGrpId
member group id
Definition entry.h:219
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:220
DString inbodyFile
file in which the body doc was found
Definition entry.h:208
Specifier virt
virtualness of the entry
Definition entry.h:190
DString metaData
Slice metadata.
Definition entry.h:234
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:236
DString name
member name
Definition entry.h:173
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:226
Protection protection
class protection
Definition entry.h:179
bool artificial
Artificially introduced item.
Definition entry.h:230
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:229
DString inbodyDocs
documentation inside the body of a function
Definition entry.h:206
int briefLine
line number at which the brief desc. was found
Definition entry.h:204
FileDef * fileDef() const
Definition entry.h:168
DString id
libclang id
Definition entry.h:232
int initLines
define/variable initializer lines to show
Definition entry.h:183
bool isStatic
static ?
Definition entry.h:184
TypeSpecifier spec
class/member specifiers
Definition entry.h:181
DString briefFile
file in which the brief desc. was found
Definition entry.h:205
DString read
property read accessor
Definition entry.h:211
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
static EntryType guessSection(const DString &name)
Definition types.cpp:22
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:97
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:26
std::string readLink() const
Definition fileinfo.cpp:88
bool isRelative() const
Definition fileinfo.cpp:62
bool isSymLink() const
Definition fileinfo.cpp:81
bool exists() const
Definition fileinfo.cpp:34
bool match(const PatternList &patList, bool caseSenseNames, PatternElem *elem=nullptr, PatternGet getter=[](const std::string &s){ return s;}) const
Match the file name against a list of patterns.
Definition fileinfo.h:60
std::string fileName() const
Definition fileinfo.cpp:122
bool isReadable() const
Definition fileinfo.cpp:48
bool isDir() const
Definition fileinfo.cpp:74
bool isFile() const
Definition fileinfo.cpp:67
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:141
std::string absFilePath() const
Definition fileinfo.cpp:105
Class representing all files with a certain base name.
Definition filename.h:31
Ordered dictionary of FileName objects.
Definition filename.h:70
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:131
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
bool hasFormulas() const
Definition formula.cpp:720
void initFromRepository(const DString &dir)
Definition formula.cpp:60
void checkRepositories()
Definition formula.cpp:173
static FormulaManager & instance()
Definition formula.cpp:54
void generateImages(const DString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
A model of a group of symbols.
Definition groupdef.h:48
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:93
static void writeSearchData(const DString &dir)
Definition htmlgen.cpp:1455
static void init()
Definition htmlgen.cpp:1275
static void writeSearchPage()
Definition htmlgen.cpp:3272
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1623
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1629
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1439
static void writeExternalSearchPage()
Definition htmlgen.cpp:3371
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1617
A class that generated the HTML Help specific files.
Definition htmlhelp.h:37
static const DString hhpFileName
Definition htmlhelp.h:90
static Index & instance()
Definition index.cpp:110
void countDataStructures()
Definition index.cpp:266
A list of index interfaces.
Definition indexlist.h:65
void initialize()
Definition indexlist.h:101
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor=DString(), const DString &title=DString())
Definition indexlist.h:118
void addIndex(As &&... args)
Add an index generator to the list, using a syntax similar to std::make_unique<T>().
Definition indexlist.h:98
void addImageFile(const DString &name)
Definition indexlist.h:124
void finalize()
Definition indexlist.h:104
Generator for LaTeX output.
Definition latexgen.h:93
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:698
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:704
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:692
static void init()
Definition latexgen.cpp:634
void parse(const DString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1472
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1439
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:275
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
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 ClassDefMutable * getClassDefMutable()=0
virtual void setArgsString(const DString &as)=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 setPrototype(bool p, const DString &df, int line, size_t column)=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 mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setExplicitExternal(bool b, const DString &df, int line, size_t column)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void setDeclFile(const DString &df, int line, size_t column)=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 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:126
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:49
size_t size() const
Definition membername.h:46
iterator begin()
Definition membername.h:35
iterator end()
Definition membername.h:36
void push_back(Ptr &&p)
Definition membername.h:52
Ordered dictionary of MemberName objects.
Definition membername.h:61
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:63
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:107
const MemberDef * find(const DString &name) const
Definition memberlist.h:94
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:255
static MermaidManager & instance()
Definition mermaid.cpp:38
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:41
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:311
void disable(OutputType o)
void add()
Definition outputlist.h:348
void enable(OutputType o)
size_t size() const
Definition outputlist.h:357
void docify(const DString &s)
Definition outputlist.h:433
void writeStyleInfo(int part)
Definition outputlist.h:391
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:748
void startContents()
Definition outputlist.h:614
A model of a page symbol.
Definition pagedef.h:27
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:182
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:253
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:215
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:244
static PlantumlManager & instance()
Definition plantuml.cpp:236
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:432
void processFile(const DString &fileName, const std::string &input, std::string &output)
Definition pre.l:4302
void addSearchDir(const DString &dir)
Definition pre.l:4284
Definition qhp.h:29
static DString getQchFileName()
Definition qhp.cpp:430
static const DString qhpFileName
Definition qhp.h:49
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:464
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:2468
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:397
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:412
static RefListManager & instance()
Definition reflist.h:122
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:273
void print()
Definition doxygen.cpp:250
void begin(const char *name)
Definition doxygen.cpp:237
void end()
Definition doxygen.cpp:243
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:274
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.
This struct is used to capture the tag file information for an Entry.
Definition entry.h:101
DString tagName
Definition entry.h:103
DString fileName
Definition entry.h:104
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:232
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 trOverloadText()=0
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:803
std::unique_ptr< ClassDef > createClassDef(const DString &fileName, int startLine, size_t 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:577
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:91
std::map< std::string, int > TemplateNameMap
Definition classdef.h:89
ClassDefMutable * getClassMutable(const DString &key)
Definition classdef.h:461
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, size_t 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:318
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:821
void generateDEF()
Definition defgen.cpp:463
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:176
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
void buildDirectories()
Definition dirdef.cpp:1097
void computeDirDependencies()
Definition dirdef.cpp:1171
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1188
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
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:5419
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:950
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5434
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:540
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6807
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2189
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9553
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9213
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3033
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6105
static void computeTemplateClassRelations()
Definition doxygen.cpp:5513
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:8268
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1175
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:436
void initDoxygen()
static StringVector g_inputFiles
Definition doxygen.cpp:191
void printSectionsTree()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5692
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:724
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2342
static void copyStyleSheet()
static void addIncludeFile(Def *def, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:311
FindBaseClassRelation_Mode
Definition doxygen.cpp:290
@ Undocumented
Definition doxygen.cpp:293
@ TemplateInstances
Definition doxygen.cpp:291
@ DocumentedOnly
Definition doxygen.cpp:292
void distributeClassGroupRelations()
Definition doxygen.cpp:1511
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9753
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:1704
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:5020
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4950
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:580
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5468
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4629
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1336
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:6674
static void resolveClassNestingRelations()
Definition doxygen.cpp:1391
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static MemberDef * addVariableToFile(const Entry *root, MemberType mtype, const DString &scope, const DString &type, const DString &name, const DString &args)
Definition doxygen.cpp:2780
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5384
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8512
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:6318
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:6219
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5582
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4543
static void addMembersToMemberGroup()
Definition doxygen.cpp:9418
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9922
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:6263
static void distributeConceptGroups()
Definition doxygen.cpp:1358
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2019
static void transferFunctionDocumentation()
Definition doxygen.cpp:4462
static void setAnonymousEnumType()
Definition doxygen.cpp:9158
static void sortMemberLists()
Definition doxygen.cpp:9063
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6850
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8640
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4592
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7656
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5706
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3528
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:555
static void generateFileDocs()
Definition doxygen.cpp:8876
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3236
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9666
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:4692
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7626
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9456
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7860
static void generatePageDocs()
static void resolveUserReferences()
Definition doxygen.cpp:9984
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9813
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:417
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:3665
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3628
static void generateFileSources()
Definition doxygen.cpp:8710
static void copyExtraFiles(StringVector files, const DString &filesOption, const DString &outputOption, bool toIndex)
static void generateClassDocs()
Definition doxygen.cpp:9311
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:4990
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1848
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2592
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9639
static void countMembers()
Definition doxygen.cpp:9172
void clearAll()
Definition doxygen.cpp:205
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4657
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:599
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2281
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:6738
static void findFriends()
Definition doxygen.cpp:4365
static void findEnums(const Entry *root)
Definition doxygen.cpp:7684
static void generateConfigFile(const DString &configFile, bool shortList, bool updateOnly=false)
static DString g_commentFileName
Definition doxygen.cpp:196
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9288
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8108
static void addListReferences()
Definition doxygen.cpp:5683
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:5897
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5362
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:467
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8302
static bool g_dumpSymbolMap
Definition doxygen.cpp:195
static void version(const bool extended)
static OutputList * g_outputList
Definition doxygen.cpp:192
static DString extractClassName(const Entry *root)
Definition doxygen.cpp:5393
static void findMainPage(Entry *root)
Definition doxygen.cpp:9852
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:908
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:697
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2032
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3697
static bool g_successfulRun
Definition doxygen.cpp:194
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8150
static void addSourceReferences()
Definition doxygen.cpp:8936
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1545
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2293
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6135
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9358
static void flushUnresolvedRelations()
Definition doxygen.cpp:9595
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9105
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:4899
static void findDocumentedEnumValues()
Definition doxygen.cpp:8294
static void findTagLessClasses()
Definition doxygen.cpp:1796
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8344
static void computeClassRelations()
Definition doxygen.cpp:5488
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4060
static void checkPageRelations()
Definition doxygen.cpp:9964
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:3950
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1326
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const DString &type, const DString &name, const DString &args, Protection prot, Relationship related)
Definition doxygen.cpp:2609
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8183
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9934
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:3813
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3093
static StringSet g_usingDeclarations
Definition doxygen.cpp:193
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3646
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9628
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:1161
static void buildPageList(Entry *root)
Definition doxygen.cpp:9825
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5675
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:197
static void findSectionsInDocumentation()
Definition doxygen.cpp:9494
static void mergeCategories()
Definition doxygen.cpp:8659
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:202
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6121
static void buildFileList(const Entry *root)
Definition doxygen.cpp:611
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1151
static void usage(const DString &name, const DString &versionString)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5452
static void computeTooltipTexts()
Definition doxygen.cpp:9112
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3304
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:4848
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8680
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5858
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7477
static void generateConceptDocs()
Definition doxygen.cpp:9337
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:4979
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9393
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:878
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName)
Definition doxygen.cpp:1572
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2340
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1346
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5873
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5618
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:4495
static void buildDefineList()
Definition doxygen.cpp:9015
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3761
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:793
static void computeMemberRelations()
Definition doxygen.cpp:8624
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2176
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8544
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:190
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:73
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:77
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:79
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:75
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:444
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:39
int dstricmp_sort(const char *str1, const char *str2)
Definition dstring.h:67
int dstrcmp(const char *str1, const char *str2)
Definition dstring.h:50
const char * qPrint(const char *s)
Definition dstring.h:783
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:2027
std::unique_ptr< FileDef > createFileDef(const DString &p, const DString &n, const DString &ref, const DString &dn)
Definition filedef.cpp:269
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:41
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:184
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4097
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:398
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5811
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const DString &navPath)
Definition index.cpp:431
void startTitle(OutputList &ol, const DString &fileName, const DefinitionMutable *def)
Definition index.cpp:388
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:405
Translator * theTranslator
Definition language.cpp:76
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:78
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:21
void writeDefaultLayoutFile(const DString &fileName)
Definition layout.cpp:1736
void printLayout()
Definition layout.cpp:1824
std::unique_ptr< MemberDef > createMemberDef(const DString &defFileName, int defLine, size_t 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 mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
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:26
void initWarningFormat()
Definition message.cpp:233
void warn_flush()
Definition message.cpp:226
DString warn_line(const DString &file, int line)
Definition message.cpp:211
void finishWarnExit()
Definition message.cpp:291
#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 ASSERT(x)
Definition message.h:142
#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:532
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:495
double getSysElapsedTime()
Definition portable.cpp:113
void setenv(const DString &variable, const DString &value)
Definition portable.cpp:302
uint32_t pid()
Definition portable.cpp:264
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:121
int pclose(FILE *stream)
Definition portable.cpp:504
DString getenv(const DString &variable)
Definition portable.cpp:337
bool isAbsolutePath(const DString &fileName)
Definition portable.cpp:513
DString pathListSeparator()
Definition portable.cpp:399
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
const char * commandExtension()
Definition portable.cpp:477
void setShortDir()
Definition portable.cpp:569
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
std::unique_ptr< NamespaceDef > createNamespaceDef(const DString &defFileName, int defLine, size_t 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:850
std::unique_ptr< PageDef > createPageDef(const DString &f, int l, const DString &n, const DString &d, const DString &t)
Definition pagedef.cpp:89
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 addRefItem(const RefItemVector &sli, const DString &key, const DString &prefix, const DString &name, const DString &title, const DString &args, const Definition *scope)
Definition reflist.h:136
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:87
Class that contains information about an inheritance relation.
Definition classdef.h:51
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:56
This class stores information about an inheritance relation.
Definition entry.h:88
Protection prot
inheritance type
Definition entry.h:93
Specifier virt
virtualness
Definition entry.h:94
DString name
the name of the base class
Definition entry.h:92
Data associated with description found in the body.
Definition definition.h:64
Helper class to pass options when calling OutputList::generateDoc().
Definition docoptions.h:24
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:41
static bool loadFilemap(const DString &htmldir)
Definition htags.cpp:110
static bool useHtags
Definition htags.h:23
stat(const char *n, double el)
Definition doxygen.cpp:271
const char * name
Definition doxygen.cpp:268
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:55
void initTracing(const DString &logFile, bool timing)
Definition trace.cpp:25
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:569
bool isTypeAClassFriend(const DString &type)
Definition types.h:920
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2680
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3525
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4672
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3754
DString filterTitle(const DString &title)
Definition util.cpp:4455
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1833
void addCodeOnlyMappings()
Definition util.cpp:4163
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:729
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4272
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5399
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5294
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:426
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3927
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2864
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:740
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:903
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4211
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:232
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:4375
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3466
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4169
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:1589
void initDefaultExtensionMapping()
Definition util.cpp:4096
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3932
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1053
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3027
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3687
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:859
DString projectLogoFile()
Definition util.cpp:2581
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:4620
int getPrefixIndex(const DString &name)
Definition util.cpp:2652
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4630
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4064
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5335
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1689
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3799
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3381
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4987
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2316